本篇是從零實現vue2系列第六篇,將在 yourvue 中實現 component。從這篇開始實現的內容,部落格上討論的就比較少了,不過啃原始碼肯定要啃完整。
將 main.js 中的內容一部分提取到元件 helloworld 中,在 yourvue 例項上註冊 helloworld 元件。
1const helloworld = ,
6 props:['message'],
7 template: `
8 9 array: }
10 }
11 addcount
12 }
13 deccount
14
15 `,
16 methods:,
21 deccount()
25 }
26 }
27new yourvue(,
30 data:,
33 template: `
34 35
36 parent button
37
38 `,
39 methods:
43 }
44})
我們可以從流程上思考一下**發生了變化????
從 template -> ast -> gencode -> render 函式這個流程是沒有變化的,只不過其中有了乙個 tag 為hello-world
的 vnode,所以需要在生成 vnode 的時候新增判斷,是 html 標籤還是自定義標籤。
1function createelement (tag, data={}, children=)else
8}
ishtmltag 就是直接判斷 tag 是否在所有 html 元素組成的列表裡,如果不是 html 標籤就執行 componenttovnode。
1export function componenttovnode(tag, data, children, vm)
5 const ctor = yourvue.extend(vm.$options.components[tag])
6 const name = tag
7 data.hooks = )
13 initprops(child, vnode.props.attrs)
14 child.$mount()
15 },
16 prepatch (oldvnode, vnode)
24 child._props[key] = attrs[key]
25 }
26 }
27 }
28 const listeners = data.on
29 const vnode = new vnode(
30 `vue-component-$$` : ''}`,
31 data, undefined, undefined, undefined, vm,
32
33 )
34 return vnode
35}
因為需要將元件定義的引數傳入 yourvue 例項,所以定義 ctor 繼承 yourvue,先將元件引數作為 extendoptions 傳入,在 ctor 的建構函式中,將 extendoptions 和 options 融合作為 _init 的引數。並將 ctor 快取,再次使用該元件時候可以直接從快取中讀取該元件對應的 ctor。
1export default class yourvue
4 const super = this
5 const superid = super.cid
6 const cachedctors = extendoptions._ctor || (extendoptions._ctor = {})
7 if (cachedctors[superid])
10 const sub = function vuecomponent (options)
13 sub.prototype = object.create(super.prototype)
14 sub.prototype.constructor = sub
15 sub.cid = cid++
16 sub['super'] = super
17 sub.extend = super.extend
18 cachedctors[superid] = sub
19 return sub
20 }
21}
從 componenttovnode 最後可以看出來,返回的 vnode 的 tag 進行了重新命名,data 暫時有兩個 hooks,其餘引數都傳入了 vnode 的最後乙個引數 componentoptions 中。
1constructor(tag, data={}, children=, text='', elm, context, componentoptions)
vnode 建立好了,生成真實 dom 的時候就用到了 patch。在上篇文章中的 createelm 開始新增乙個 createcomponent 函式,在這個函式中會執行上面提到的 data.hooks.init。
1function createelm (vnode, parentelm, afterelm = undefined)
5 ...
6} 7function createcomponent (vnode, parentelm, afterelm)
13 if (isdef(vnode.componentinstance)) else if(parentelm)
20 return true
21 }
22 }
23}
再返回來看 hooks.init,其中初始化了 ctor,並傳入兩個引數標準 component 和記錄父 vnode。最後執行 $mount 函式,生成真實 dom。可以從上面**vnode.elm = vnode.componentinstance.vnode.elm
發現,父元件中hello-world
component 渲染的 elm,就是子元件的真實 dom。
1init(vnode))
6 initprops(child, vnode.props.attrs)
7 child.$mount()
8}
initprops(child, vnode.props.attrs)
處理父元件傳入子元件的 props,initprops 定義如下。
1function initprops(vm, propsoptions)
3 for (const key in propsoptions)
7 definereactive(props, key, propsoptions[key])
8 if (!(key in vm))
11 }
12}
將 propsoptions 傳遞來的變數通過響應式函式 definereactive 修改 props 的 get 和 set 方法,實現發布訂閱。然後通過 proxy 方法**,這樣就可以直接使用 this 來訪問 props 了。
prepatch 鉤子是在 patchvnode 中執行。
1function patchvnode(oldvnode, vnode)
5 let i
6 const data = vnode.props
7 if (isdef(data) && isdef(i = data.hooks) && isdef(i = i.prepatch))
10 ...
11}12
13prepatch (oldvnode, vnode)
21 child._props[key] = attrs[key]
22 }
23}
將 componentinstance 賦給新的 vnode,將父元件傳遞的 props 最新值賦給 _props,觸發雙向繫結中的 set 函式。
這樣,component 從定義到轉換成真實 dom 以及父元件向子元件傳遞 props 的功能就基本完成了。
本篇**:
從零寫乙個Java WEB框架(一)
從乙個簡單的servlet專案開始起步。對每一層進行優化,然後形成乙個輕量級的框架。每一篇,都是針對專案的不足點進行優化的。專案已放上github 乙個非常基礎的servlet專案。基本功能是 對資料表 客戶表進行資料處理。例如 客戶的資料獲取 controller 層 獲取客戶端的資料 思路 通過...
從零寫乙個Java WEB框架(一)
從乙個簡單的servlet專案開始起步。對每一層進行優化,然後形成乙個輕量級的框架。每一篇,都是針對專案的不足點進行優化的。專案已放上github 乙個非常基礎的servlet專案。基本功能是 例如 客戶的資料獲取 controller 層 獲取客戶端的資料 思路 server 層中的獲取所有客戶資...
如何寫乙個Vue元件
寫的是以.vue結尾的單檔案元件的寫法,是基於webpack構建的專案。template 模板 js 邏輯 css 樣式 每個元件都有屬於自己的模板,js和樣式。如果將乙個頁面比喻成一間房子的話,元件就是房子裡的客廳 臥室 廚房 廁所。如果把廚房單獨拿出來的話,元件又可以是刀 油煙機.等等。就是說頁...