看這篇之前,假如沒有看過之前的文章,可拉到文章末端檢察之前的文章。
媒介
激動人心的時刻即將來臨,之前我們做的 8
步,實在都在為這一步打基礎,這一步,我們來簡樸完成一個 Vue
對象,還沒有看過之前代碼的同硯,請確認看過之前的文章。
重要完成內容
我們從測試代碼入手,來看我們這個 Vue
完成了什麼,然後在依據要完成的內容來編寫這個 Vue
對象:
let test = new Vue({
data() {
return {
baseTest: 'baseTest',
objTest: {
stringA: 'stringA',
stringB: 'stringB'
}
}
},
methods: {
methodTest() {
console.log('methodTest')
this.$emit('eventTest', '事宜測試')
}
},
watch: {
'baseTest'(newValue, oldValue) {
console.log(`baseTest change ${oldValue} => ${newValue}`)
},
'objTest.stringA'(newValue, oldValue) {
console.log(`objTest.stringA change ${oldValue} => ${newValue}`)
}
}
})
test.$on('eventTest', function (event) {
console.log(event)
})
test.methodTest()
test.baseTest
重要完成的內容有:
- 有屬性的監聽
Watcher
- 實例下
data/methods
數據的代辦(直接運用this.xxx
就能夠接見到詳細的屬性/要領) - 有事宜
$on/$emit
完成
我們依據完成的難易水平來完成上面 3
點。
完成第 3
點,只需繼承 Event
這個類即可:
注: 在 Vue
源碼中並非經由過程這個體式格局完成的事宜,有興緻的能夠本身去了解下,然則在我看來如許是最輕易明白的體式格局。
class Vue extends Event {
constructor() {
// 挪用父類的 constructor 要領
super()
...
}
...
}
Event
類在我們上一步已完成。
接着我們來處置懲罰第二點。為了輕易代碼的治理,我們在類下定義一個 _init
要領,來完成 Vue
的初始化。
我們先完成 methods
的綁定,因為 data
是要被監聽,所以要舉行進一步的處置懲罰。
class Vue extends Event {
constructor(options) {
// 挪用父類的 constructor 要領
super()
this._init(options)
}
_init(options) {
let vm = this
if (options.methods) {
for (let key in options.methods) {
vm[key] = options.methods[key].bind(vm)
}
}
}
}
ok methods
要領綁定完事,實在就這麼簡樸。
接下來我們來處置懲罰 data
,因為 data
是須要被變換成可監聽構造,所以我們先處置懲罰一下,然後代辦到 this
對象下,假如直接賦值而不代辦的話 data
的可監聽構造就會被損壞,我們須要一個完全的對象,這個可監聽構造才完全。
這裏先完成一下代辦的要領:
export function proxy(target, sourceKey, key) {
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get() {
},
set() {
}
}
sharedPropertyDefinition.get = function proxyGetter() {
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter(val) {
this[sourceKey][key] = val
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
道理照樣經由過程 Object.defineProperty
要領來完成,當接見(get
) target
下的某個屬性的時刻,就會去找 target[sourceKey]
下的同名屬性,設置(set
) target
下的某個屬性,就會讓設置 target[sourceKey]
下的同名屬性。這就完成了代辦。
ok 代辦完成,我們繼承為 _init
增加要領,詳細的步驟看代碼中的解釋
class Vue extends Event {
constructor(options) {
// 挪用父類的 constructor 要領
super()
this._init(options)
}
_init(options) {
let vm = this
if (options.methods) {
for (let key in options.methods) {
// 綁定 this 指向
vm[key] = options.methods[key].bind(vm)
}
}
// 因為 data 是個函數,所以須要挪用,並綁定上下文環境
vm._data = options.data.call(vm)
// 將 vm._data 變成可監聽構造,完成 watcher 的增加
observe(vm._data)
// 代辦屬性,這保證了監聽構造是一個完成的對象
for (let key in vm._data) {
proxy(vm, '_data', key)
}
}
}
末了一步,增加 watcher
,仔細分析我們在實例化時寫的 watcher
:
watch: {
'baseTest'(newValue, oldValue) {
console.log(`baseTest change ${oldValue} => ${newValue}`)
},
'objTest.stringA'(newValue, oldValue) {
console.log(`objTest.stringA change ${oldValue} => ${newValue}`)
}
}
key
為須要監聽的屬性的途徑,value
為觸發監聽時的回調。
ok 我們來完成它
class Vue extends Event {
constructor(options) {
super()
this._init(options)
}
_init(options) {
...
// 輪迴掏出 key/value
for (let key in options.watch) {
// 用我們之前完成的 Watcher 來註冊監聽
// 參一:watcher 的運轉環境
// 參二:獵取註冊該 watcher 屬性
// 參三:觸發監聽時的回調
new Watcher(vm, () => {
// 須要監聽的值,eg: 'objTest.stringA' ==> vm.objTest.stringA
return key.split('.').reduce((obj, name) => obj[name], vm)
}, options.watch[key])
}
}
}
ok watcher
也已完成,以下就是完全的代碼:
export function proxy(target, sourceKey, key) {
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get() {
},
set() {
}
}
sharedPropertyDefinition.get = function proxyGetter() {
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter(val) {
this[sourceKey][key] = val
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
let uid = 0
export class Vue extends Event {
constructor(options) {
super()
this._init(options)
}
_init(options) {
let vm = this
vm.uid = uid++
if (options.methods) {
for (let key in options.methods) {
vm[key] = options.methods[key].bind(vm)
}
}
vm._data = options.data.call(vm)
observe(vm._data)
for (let key in vm._data) {
proxy(vm, '_data', key)
}
for (let key in options.watch) {
new Watcher(vm, () => {
return key.split('.').reduce((obj, name) => obj[name], vm)
}, options.watch[key])
}
}
}
接下來,我們來測試一下
let test = new Vue({
data() {
return {
baseTest: 'baseTest',
objTest: {
stringA: 'stringA',
stringB: 'stringB'
}
}
},
methods: {
methodTest() {
console.log('methodTest')
this.$emit('eventTest', '事宜測試')
}
},
watch: {
'baseTest'(newValue, oldValue) {
console.log(`baseTest change ${oldValue} => ${newValue}`)
},
'objTest.stringA'(newValue, oldValue) {
console.log(`objTest.stringA change ${oldValue} => ${newValue}`)
}
}
})
test.$on('eventTest', function (event) {
console.log(event)
})
test.methodTest()
// methodTest
// 事宜測試
test.baseTest = 'baseTestChange'
// baseTest change baseTest => baseTestChange
test.objTest.stringA = 'stringAChange'
// objTest.stringA change stringA => stringAChange
剛開始運用 Vue
的時刻,覺得代碼內里都是些黑魔法,在看了源碼以後驚覺:實在 Vue
的全部完成並沒有什麼黑魔法,有的是經心的構造和處置懲罰,耐煩點看下去,我置信我的收成會很大。
系列文章地點
- VUE – MVVM – part1 – defineProperty
- VUE – MVVM – part2 – Dep
- VUE – MVVM – part3 – Watcher
- VUE – MVVM – part4 – 優化Watcher
- VUE – MVVM – part5 – Observe
- VUE – MVVM – part6 – Array
- VUE – MVVM – part7 – Event
- VUE – MVVM – part8 – 優化Event
- VUE – MVVM – part9 – Vue
- VUE – MVVM – part10 – Computed
- VUE – MVVM – part11 – Extend
- VUE – MVVM – part12 – props
- VUE – MVVM – part13 – inject & 總結