手寫(xiě)Vue2.0 數(shù)據(jù)劫持的示例
簡(jiǎn)單的搭建一下webpack的配置。新建一個(gè)文件夾,然后init一下。之后新建一個(gè)webpack.config.js文件,這是webpack的配置文件。安裝一下簡(jiǎn)單的依賴(lài)。
npm install webpack webpack-cli webpack-dev-server -D
在同級(jí)目錄下新建一個(gè)public/index.html和src/index.js,作為出口文件和入口文件。
j簡(jiǎn)單配置一下webpack, 在webpack.config.js文件中:
const path = require(’path’);const HtmlWebpackPlugin = require(’html-webpack-plugin’);module.exports = { entry: ’./src/index.js’, output: { filename: ’bundle.js’, path: path.resolve(__dirname, ’dist’) }, resolve: { modules: [ path.resolve(__dirname, ’’), path.resolve(__dirname, ’node_modules’) ] }, plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, ’public/index.html’) }) ]}
ok,基本配置好webpack就可以開(kāi)始正題了。
二:數(shù)據(jù)劫持在v2中,通過(guò)new Vue(el, options)的方式,完成vue的實(shí)例化。我們需要新建一下vue文件,把數(shù)據(jù)劫持的方法統(tǒng)一到vue中。
新建一個(gè)vue/index.js,作為數(shù)據(jù)劫持的入口文件。
import {initState} from ’./init.js’;function Vue (options) { this._init(options); // 數(shù)據(jù)初始化}Vue.prototype._init = function (options) { var vm = options; // 保存一下實(shí)例 vm.$options = options; // 實(shí)例掛載 initState(vm); // 實(shí)例初始化}
新建一個(gè)init.js文件初始化實(shí)例:
初始化的時(shí)候注意幾個(gè)問(wèn)題:
1. 需要分別對(duì)computed,watch, data進(jìn)行處理。
2. 不要在用戶(hù)定義的data上直接修改。
3. 官方指定data為函數(shù),是為了保證組件內(nèi)部有自己的作用域不會(huì)有污染,直接訪(fǎng)問(wèn)data函數(shù)是不行的,需要自動(dòng)執(zhí)行。data也可以是對(duì)象(需要考慮到這個(gè)情況)
4. 這種方式獲取data是通過(guò)vm._data.xxx 但是在vue中不需要data來(lái)獲取,所以這里需要攔截重寫(xiě)。
5. 內(nèi)部的引用類(lèi)型需要遞歸
function initState (vm) { var options = vm.$options; // 獲取options if (options.data) { initData(vm); // 因?yàn)閏omputed,watch都需要在這里初始化,所以針對(duì)data初始化};function initData (vm) { var data = vm.$options.data; // 對(duì)data重新賦值,不要改變用戶(hù)定義的data data = vm._data = typeof data === ’function’ ? data.call(vm) : data || {}; for (var key in data) { proxyData(vm, ’_data’, key); // 對(duì)data的取值重新賦值 }; observe(vm._data); // 對(duì)data內(nèi)部進(jìn)行觀(guān)察}
新建一個(gè)proxy.js作為代理層:
function proxyData(vm, target, key) { Object.defineProperty(vm, key, { get () { // 這里做了攔截: vm.xxx => vm._data.xxx return vm[target][key]; }, set(newValue) { // vm.xxx = yyy ===> vm._data.title = yyy vm[target][key] = newValue; } }) }export default proxyData;
處理好了訪(fǎng)問(wèn)問(wèn)題,現(xiàn)在需要遞歸一下data內(nèi)部元素。obseve(vm._data);
新建一個(gè)observe.js:
function observe (data) { if (typeof data !== ’object’ || data = null) return; return new Observer(data); // 如果是應(yīng)用類(lèi)型,直接添加一個(gè)觀(guān)察者}
新建一個(gè)觀(guān)察者:observer.js
function Observer(data) { if (Array.isArray(data)) { // 處理數(shù)組 data._proto_ = arrMethods; } else { // 處理對(duì)象 this.walks(data); }}Observer.prototype.walks = function (data) { let keys = Object.keys(data); // 拿到data下面所有的key,并且還是一個(gè)數(shù)組 for (var i = 0 ; i < keys.length ; i++) { var key = keys[i]; var value = data[key]; defineReactiveData(data, key, value); // 每個(gè)重新生成響應(yīng)式數(shù)據(jù) }}
新建一個(gè)reactive.js 處理對(duì)象等響應(yīng)式
function defineReactiveData (data, key, value) { observe(value); // 對(duì)子元素接著遞歸。 Object.defineProperty(data, key, { get() { return value; }, set (newValue) { if (newValue === value) return; value = newValue; // 觸發(fā)更改 } } )};
ok,這里處理好了對(duì)象的數(shù)據(jù)劫持,剩余的需要處理數(shù)組了
在V2中采用重寫(xiě)原型上的7種方法,做到數(shù)據(jù)劫持。
劫持?jǐn)?shù)組:
新建一個(gè)Array.js文件:
import {ARR_METHODS} from ’./config.js’; // 7個(gè)數(shù)組方法的合集import observeArr from ’./observeArr.js’;var originArrMethods = Array.prototype, arrMethods = Object.create(originArrMethods); ARR_METHODS.map(function (m) { arrMethods[m] = function () { var args = Array.prototype.slice.call(arguments); // 類(lèi)數(shù)組轉(zhuǎn)為數(shù)組 var rt = originArrMethods[m].apply(this, args); var newArr; switch (m) { case ’push’: case ’ushift’: newArr = args; case ’splice’:newArr = args.slice(2);break; default: break; }; newArr && observeArr(newArr); return rt; } }); export { arrMethods }
observeArr(newArr): 數(shù)組也可能有嵌套,所以需要對(duì)數(shù)據(jù)進(jìn)行觀(guān)察。
import observe from './observe';function observeArr (arr) { for (var i = 0 ; i < arr.length ; i++) { observe(arr[i]); // 重新走到了observe上。 }}export default observeArr;三:總結(jié)
基本流程就是這樣的,不僅僅是object.defineProperty對(duì)數(shù)據(jù)進(jìn)行g(shù)et和set這么簡(jiǎn)單。總結(jié)一下主要流程:
(1): 在初始化的時(shí)候:保存一下實(shí)例,掛載實(shí)例。通過(guò)initState方法來(lái)初始化數(shù)據(jù),這里主要是data數(shù)據(jù),也有computed和watch需要處理。
(2): 調(diào)用initData(); 重新賦值data,然后執(zhí)行data,修改用戶(hù)獲取data屬性的寫(xiě)法統(tǒng)一為this.xxx同時(shí)observe(data)
(3):在observe(data)的時(shí)候需要對(duì)data進(jìn)行判斷,如果是引用類(lèi)型需要加上一個(gè)觀(guān)察者observer,同時(shí)在觀(guān)察者終判斷data是為數(shù)組還是對(duì)象,對(duì)象直接重新觸發(fā)object.defineProperty,同時(shí)對(duì)內(nèi)部重新observe。如果是數(shù)組直接重新7種數(shù)組方法,然后對(duì)數(shù)組內(nèi)部接著observe。
以上就是手寫(xiě)Vue2.0 數(shù)據(jù)劫持的示例的詳細(xì)內(nèi)容,更多關(guān)于手寫(xiě)vue 數(shù)據(jù)劫持的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. XML解析錯(cuò)誤:未組織好 的解決辦法2. XML入門(mén)的常見(jiàn)問(wèn)題(一)3. HTML5 Canvas繪制圖形從入門(mén)到精通4. css代碼優(yōu)化的12個(gè)技巧5. 詳解瀏覽器的緩存機(jī)制6. asp知識(shí)整理筆記4(問(wèn)答模式)7. XML入門(mén)的常見(jiàn)問(wèn)題(四)8. asp批量添加修改刪除操作示例代碼9. 微信開(kāi)發(fā) 網(wǎng)頁(yè)授權(quán)獲取用戶(hù)基本信息10. javascript xml xsl取值及數(shù)據(jù)修改第1/2頁(yè)
