手寫Vue2.0 數(shù)據(jù)劫持的示例
一:搭建webpack
簡單的搭建一下webpack的配置。新建一個(gè)文件夾,然后init一下。之后新建一個(gè)webpack.config.js文件,這是webpack的配置文件。安裝一下簡單的依賴。
npm install webpack webpack-cli webpack-dev-server -D
在同級目錄下新建一個(gè)public/index.html和src/index.js,作為出口文件和入口文件。
j簡單配置一下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就可以開始正題了。
二:數(shù)據(jù)劫持
在v2中,通過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è)問題:
1. 需要分別對computed,watch, data進(jìn)行處理。
2. 不要在用戶定義的data上直接修改。
3. 官方指定data為函數(shù),是為了保證組件內(nèi)部有自己的作用域不會(huì)有污染,直接訪問data函數(shù)是不行的,需要自動(dòng)執(zhí)行。data也可以是對象(需要考慮到這個(gè)情況)
4. 這種方式獲取data是通過vm._data.xxx 但是在vue中不需要data來獲取,所以這里需要攔截重寫。
5. 內(nèi)部的引用類型需要遞歸
function initState (vm) {
var options = vm.$options; // 獲取options
if (options.data) {
initData(vm); // 因?yàn)閏omputed,watch都需要在這里初始化,所以針對data初始化
};
function initData (vm) {
var data = vm.$options.data; // 對data重新賦值,不要改變用戶定義的data
data = vm._data = typeof data === 'function' ? data.call(vm) : data || {};
for (var key in data) {
proxyData(vm, '_data', key); // 對data的取值重新賦值
};
observe(vm._data); // 對data內(nèi)部進(jì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;
處理好了訪問問題,現(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)用類型,直接添加一個(gè)觀察者
}
新建一個(gè)觀察者:observer.js
function Observer(data) {
if (Array.isArray(data)) {
// 處理數(shù)組
data._proto_ = arrMethods;
}
else {
// 處理對象
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 處理對象等響應(yīng)式
function defineReactiveData (data, key, value) {
observe(value); // 對子元素接著遞歸。
Object.defineProperty(data, key, {
get() {
return value;
},
set (newValue) {
if (newValue === value) return;
value = newValue; // 觸發(fā)更改
}
}
)
};
ok,這里處理好了對象的數(shù)據(jù)劫持,剩余的需要處理數(shù)組了
在V2中采用重寫原型上的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); // 類數(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ù)組也可能有嵌套,所以需要對數(shù)據(jù)進(jì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對數(shù)據(jù)進(jìn)行g(shù)et和set這么簡單??偨Y(jié)一下主要流程:
(1): 在初始化的時(shí)候:保存一下實(shí)例,掛載實(shí)例。通過initState方法來初始化數(shù)據(jù),這里主要是data數(shù)據(jù),也有computed和watch需要處理。
(2): 調(diào)用initData(); 重新賦值data,然后執(zhí)行data,修改用戶獲取data屬性的寫法統(tǒng)一為this.xxx同時(shí)observe(data)
(3):在observe(data)的時(shí)候需要對data進(jìn)行判斷,如果是引用類型需要加上一個(gè)觀察者observer,同時(shí)在觀察者終判斷data是為數(shù)組還是對象,對象直接重新觸發(fā)object.defineProperty,同時(shí)對內(nèi)部重新observe。如果是數(shù)組直接重新7種數(shù)組方法,然后對數(shù)組內(nèi)部接著observe。
以上就是手寫Vue2.0 數(shù)據(jù)劫持的示例的詳細(xì)內(nèi)容,更多關(guān)于手寫vue 數(shù)據(jù)劫持的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue中響應(yīng)式系統(tǒng)實(shí)現(xiàn)原理圖文講解
Vue的響應(yīng)式實(shí)現(xiàn)是借助Object.defineProperty通過重寫getter和setter方法來進(jìn)行的數(shù)據(jù)劫持,Vue3通過Proxy代理攔截對象中任意屬性的變化,通過Reflect反射對源對象的屬性進(jìn)行操作,然后再在get里收集依賴在set里派發(fā)更新2023-03-03
Vue3純前端實(shí)現(xiàn)Vue路由權(quán)限的方法詳解
這篇文章主要給大家介紹了關(guān)于Vue3純前端實(shí)現(xiàn)Vue路由權(quán)限的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Vue3具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-05-05
html中引入Vue.js的cdn實(shí)現(xiàn)簡單的文檔單頁
這篇文章主要為大家介紹了html中引入Vue.js的cdn實(shí)現(xiàn)簡單的文檔單頁示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
vue 2.1.3 實(shí)時(shí)顯示當(dāng)前時(shí)間,每秒更新的方法
今天小編就為大家分享一篇vue 2.1.3 實(shí)時(shí)顯示當(dāng)前時(shí)間,每秒更新的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09
vuejs2.0運(yùn)用原生js實(shí)現(xiàn)簡單的拖拽元素功能示例
本篇文章主要介紹了vuejs2.0運(yùn)用原生js實(shí)現(xiàn)簡單的拖拽元素功能示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-02-02
解決vue項(xiàng)目中某一頁面不想引用公共組件app.vue的問題
這篇文章主要介紹了解決vue項(xiàng)目中某一頁面不想引用公共組件app.vue的問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08

