以前閱讀的是
preact
的v8
版本,官方已經更新到v10
的測試版了,接下來咱們主要對照兩個版本之間的差別來學習源碼,這樣就能清楚的知道做者爲何要這麼改,這麼改有哪些好處了!node
V10
的 preact
學乖了,總算在源碼階段就更貼近 react
了:react
function createElement(type, props, children) {
//...
}
複製代碼
哈哈,仔細看 createElement
方法的三個參數,類 react
,就要從變量名開始!git
咱們來看下上個版本的h方法:github
function createElement(nodeName, attributes){
// ...
}
複製代碼
新版本中新增了一個形參 children
(nodeName
,attributes
對應 type
,props
),這個變量的做用就是存儲子組件的,先看下新版本對這個形參的處理:api
if (arguments.length>3) {
children = [children];
for (let i=3; i<arguments.length; i++) {
children.push(arguments[i]);
}
}
複製代碼
傳入 createElement
方法的參數數目,若是大於3個,則第三個參數 children
從新定義位爲一個數組,原值是這個數組的第一個元素,而後遍歷這個方法第3位以後的全部參數,並做爲元素壓入 children
這個數組中。數組
上個版本的 createElement
方法也是經過遍歷 arguments
來收集子組件的,不過它在函數外定義了一個全局對象和一個初始子組件的空數組:緩存
// 將從第3位起的參數收集起來(其實就是一堆 h 方法的執行函數)
const stack = [];
// 定義一個初始子組件空數組
const EMPTY_CHILDREN = [];
function h(nodeName, attributes) {
// ...
// 遍歷h函數的參數,從第三個參數開始就是子組件
for (i = arguments.length; i-- > 2; ) {
// 將從第3位起的參數收集起來(其實就是一堆 h方法的執行函數)
stack.push(arguments[i]);
}
// ...
}
複製代碼
最終實現的效果是同樣的,新版本用一個形參代替了函數外的兩個變量,這兩個變量雖然不是全局,但對於這個模塊來講是共用的,這就引出了一個話題:函數的反作用。dom
舊版本中,h
函數內部改變了函數體外部的一個變量,在這個函數執行完成以後,這個對象不會被銷燬,並且所指向的值也發生了改變,而這種改變就是 h
函數的反作用。函數
若是這個模塊中另外一個函數也用到了這個變量,那就可能會形成不可預估的bug,因此,這個反作用是能夠避免的。性能
新版本的 h
方法用一個形參從新賦值爲一個數組的操做都是在函數體內部作的,與外部沒有任何聯繫,函數執行完成後,函數體內部的變量都會銷燬,這樣的優化是很是值得咱們學習的。
在舊版本中,函數內部會對 stack
這個收集子組件的數組元素作類型判斷:
boolean
,將元素從新賦值爲 null
number
,轉化成 string
新版本去掉這樣的操做,主要是由於它將子組件的操做放在了 props
中
if (children != null) {
props.children = children;
}
複製代碼
後面會講到新版本對 props
專門作了一個模塊及方法 diff/props.js
中的 diffProps
函數。
createVNode
方法,建立 vnode
對象不在用 new
關鍵字舊版本的虛擬 dom
是經過實例話 Vnode
類來實現的
const VNode = function VNode() {};
let p = new VNode();
p.nodeName = nodeName;
p.children = children;
p.attributes = attributes == null ? undefined : attributes;
p.key = attributes == null ? undefined : attributes.key;
複製代碼
建立一個虛擬 dom
,就要 new
一個對象,學過js的人都聽過,new
是很耗性能的,具體能夠參考這篇文章:prototype, constructor and new。
新版本中沒有在 createElement
這個函數內部直接作建立,而是調用了一個 createVNode
函數:
export function createVNode(type, props, key, ref) {
const vnode = {
type,
props,
key,
ref,
_children: null,
_dom: null,
_lastDomChild: null,
_component: null
};
vnode._self = vnode;
if (options.vnode) options.vnode(vnode);
return vnode;
}
複製代碼
這個函數的職責就是建立一個虛擬dom
,首先在函數內部定義了一個字面量 vnode
,它有若干的屬性,其中要注意的是以 _
開頭的都是系統內部會使用到的,這裏只是作了一個初始值:
type
虛擬dom的元素類型,如 div span ,一個文本類型,或者是一個 function
props
經過jsx傳入的屬性key
惟一鍵值ref
返回虛擬dom的真是dom_children
子組件集合_dom
真實dom_lastDomChild
子組件中的最後一個dom_component
指向的子組件_self
緩存了虛擬dom自己信息新版本把建立虛擬 dom
獨立成一個小方法供其餘模塊複用這個邏輯,尤爲是在 diff
子組件的時候會遞歸調用。
createElement
函數講完了,其實我以爲這個函數能夠說是 preact
真正的入口函數,在後續會講到 render
的時候就會發現,虛擬 dom
是一切的開始!