Vue 原始碼分析之proxy代理
阿新 • • 發佈:2018-11-07
Vue 原始碼分析之proxy代理
當我們在使用Vue進行資料設定時,通常初始化格式為:
let data = {
age: 12,
name: 'yang'
}
// 例項化Vue物件
let vm = new Vue({
data
})
// 輸出
vm.age // 12
vm.name // 'yang'
vm.data.age // throw error
我們發現,我們訪問vm例項的物件屬性時,是直接通過 vm.age
訪問,而通過vm.data.age
訪問則會報錯。
處於好奇,初窺Vue的原始碼,才瞭解一二,這裡就與大家一同分享一下:
實現步驟
看完原始碼大致梳理一下流程,主要是下面三點:
- 例項化Vue
- 掛載data
- 代理data
那麼我們來簡單的例項一個Viwe(仿Vue)類,來實現文章開頭展示的功能。
1. 例項化Vue
即建立一個vm例項物件,給View類傳入data物件
// 建立一個View類 const View = function (options) { console.log(this, options) } // 例項化View const vm = new View({ data: { age: 12, name: 'yang' } }) // 此時,無法訪問data中的屬性 vm // {} vm.age // undefined vm.name // undefined
2. 掛載data
緊接上一步,我們開始為vm掛載_data物件(私有物件以’_'起始),那麼,我們就要準備改造我們的View類了。
const vm = function (options) { // 取出data物件 const data = options.data || {} // 將data 指向this._data this._data = data } // 例項化View const vm = new View({ data: { age: 12, name: 'yang' } }) // 此時,無法訪問data中的屬性 vm // {_data:{...}} vm._data // {age:12,name:'yang'} vm._data.age // 12 vm._data.name // 'yang' vm.age // undefined vm.name // undefined
3. 代理data
在掛載data的時候,可以通過vm._data
訪問data中的物件屬性了,但是這和我們的預期還是差一點。
這裡,我們使用Object.defineProperty
來代理vm._data
中的物件屬性。
關於Object.defineProperty
使用教程,請參考MDN文件
下面我們繼續改造我們的View類
// 定義一個空函式
const noop = function () {}
// 定義一個預設屬性配置
const defineProperty = {
enumerable: true,
configurable: true,
get: noop,
set: noop
}
const View = function (options) {
this._data = options.data || {}
// 獲取this._data中的屬性名
Object.keys(this._data).forEach(key => {
// 代理
defineProperty.get = function dataGetter() {
return this._data[key]
}
defineProperty.set = function dataSetter (val) {
this._data[key] = val
}
// 關鍵點,代理this._data中的屬性
Object.defineProperty(this,key,defineProperty)
})
}
const vm = new View({
age: 12,
name: 'yang'
})
vm // {_data:{...},age:12,name:'yang'}
vm._data // {age:12,name:'yang'}
vm.age // 12
vm.name // 'yang'
改到這裡,一個基本的代理實現就完成啦~,下面,我們看看是否能夠使用其他方式達到相同的改造效果呢?
二、使用ES6原生 Proxy實現
先來了解一下Proxy的簡單用法:
例子一:
/**
* 定義:
* target type: Object
* handler type: Object
**/
let proxy = new Proxy(target,handler)
// 用法
let target = {}
let proxy = new Proxy(target,{
get: function () {
return 'yang'
},
set: function () {
return 12
}
})
proxy.a // 'yang'
proxy.xxx // 'yang'
proxy.a = 1 // 12
例子二:
const target = {
data: {
age: 12,
name: 'yang'
}
}
const proxy = new Proxy(target, {
get: function dataGetter (target, key) {
return target.data[key] || {}
},
set: function dataSetter (target, key, val) {
target.data[key] = val
return true
}
})
proxy.age = 1
console.log('p', proxy.age)
好了,掌握了基本語法之後,那麼開始進行構建:
// 定義View類
const View = function (options) {
// 掛載_data
this._data = options.data || {}
// 返回一個Proxy物件
return new Proxy(this, {
// get: function (target, key) {
// return target._data[key]
// },
// set: function (target, key, val) {
// target._data[key] = val
// return true
// }
// 簡化寫法
get: (target, key) => this._data[key],
set: (target, key, val) => this._data[key] = val // eslint-disable-line
})
}
let vm = new View({
data: {
age: 12,
name: 'yang'
}
})
vm // Proxy {...}
vm.age += 1
console.log('vm', vm.age) // 13