preact原始碼分析(二)
src/component.js
component.js檔案中定義了Preact中Component類。因為我們還沒有涉及到Diff演算法, 所以目前在這裡只會介紹檔案中的一部分程式碼, 其餘的部分在接下來文章中會得到補充。
constructor
Component類的定義, Component類中定義了兩個引數, props以及context。props為初始化元件的引數, context為父元件的上下文。
export function Component(props, context) { this.props = props this.context = context }
setState
setState方法中會初始化名為"_nextState"的屬性。_nextState屬性確保了每一次的setState都將在setState內部生成一個淺拷貝的物件。
Component.prototype.setState = function(update, callback) { // 在第一次初始化元件的時, _nextState為undefined, 所以將執行或語句的後半句 // this._nextState = assign({}, this.state), this._nextState將會被初始化 let s = (this._nextState!==this.state && this._nextState) || (this._nextState = assign({}, this.state)); // 在這裡同React一樣setState將會接受兩種型別的引數, Object和Function型別 // 如果是Object型別, 將會執行一層淺拷貝 // 如果是Function型別, 則會把現在state和props傳入函式, 返回的物件將作為執行一層淺拷貝 if (typeof update!=='function' || (update = update(s, this.props))) { assign(s, update); } if (update==null) return; // 如果有setState存在第二個引數, 將會把callback儲存到_renderCallbacks的陣列中 if (callback) this._renderCallbacks.push(callback); // 將元件傳入處理元件重新渲染(render)的佇列中處理 enqueueRender(this); };
q
q定義了一個渲染(render)的佇列
let q = []
defer
defer將會返回一個Promise.resolve()的回撥。
const defer = typeof Promise=='function' ? Promise.prototype.then.bind(Promise.resolve()) : setTimeout;
let foo = function () { return 'Hello World' } // Promise {<resolved>: "Hello World"} defer(foo)
enqueueRender
enqueueRender函式會將需要render的元件push到q佇列中。並根據條件判斷是否需要渲染元件。
為什麼說setState是一個非同步的過程, 可以從這裡得到答案, defer將會返回一個Promise.resolve()物件。
export function enqueueRender(c) { // 只有當_dirty為false時, 並且佇列的長度為1的情況下才會處理佇列 if (!c._dirty && (c._dirty = true) && q.push(c) === 1) { (options.debounceRendering || defer)(process); } }
process
process函式中將會遍歷q佇列, 呼叫forceUpdate方法重新渲染元件。
function process() { let p; // 迭代將會在q佇列被清空時結束 while ((p=q.pop())) { // 只有當_dirty為true時才會更新元件, forceUpdate方法因為涉及到diff演算法的內容這裡暫不涉及 if (p._dirty) p.forceUpdate(false) } }
render
render函式接受props和state返回需要更新的Dom樹。
render函式預設Fragment。Fragment在create-element.js中定義為空函式, Fragment可以用來包裹一組節點。具體作用可以參考React中Fragment。
Component.prototype.render = Fragment;
結語
目前我們簡略的概覽component檔案, 從我們目前已知的內容, 可以總結出以下的內容。
forceUpdate會在以後的文章中介紹。