摘要:一個字符串或者虛擬的數組用于表示該節點的。傳遞給函數的參數有兩個首先是當前狀態,其次是事件處理的回調函數,對生成的視圖中觸發的事件進行處理。回調函數主要負責為應用程序構建一個新的狀態,并使用新的狀態重啟循環。
原文鏈接原文寫于 2015-07-31,雖然時間比較久遠,但是對于我們理解虛擬 DOM 和 view 層之間的關系還是有很積極的作用的。
React 是 JavaScript 社區的新成員,盡管 JSX (在 JavaScript 中使用 HTML 語法)存在一定的爭議,但是對于虛擬 DOM 人們有不一樣的看法。
對于不熟悉的人來說,虛擬 DOM 可以描述為某個時刻真實DOM的簡單表示。其思想是:每次 UI 狀態發生更改時,重新創建一個虛擬 DOM,而不是直接使用命令式的語句更新真實 DOM ,底層庫將對應的更新映射到真實 DOM 上。
需要注意的是,更新操作并沒有替換整個 DOM 樹(例如使用 innerHTML 重新設置 HTML 字符串),而是替換 DOM 節點中實際修改的部分(改變節點屬性、添加子節點)。這里使用的是增量更新,通過比對新舊虛擬 DOM 來推斷更新的部分,然后將更新的部分通過補丁的方式更新到真實 DOM 中。
虛擬 DOM 因為高效的性能經常受到特別的關注。但是還有一項同樣重要的特性,虛擬 DOM 可以把 UI 表示為狀態函數的映射(PS. 也就是我們常說的 UI = render(state)),這也使得編寫 web 應用有了新的形式。
在本文中,我們將研究虛擬 DOM 的概念如何引用到 web 應用中。我們將從簡單的例子開始,然后給出一個架構來編寫基于 Virtual DOM 的應用。
為此我們將選擇一個獨立的 JavaScript 虛擬 DOM 庫,因為我們希望依賴最小化。本文中,我們將使用 snabbdom(paldepind/snabbdom),但是你也可以使用其他類似的庫,比如 Matt Esch 的 virtual-dom
snabbdom簡易教程snabbdom 是一個模塊化的庫,所以,我們需要使用一個打包工具,比如 webpack。
首先,讓我們看看如何進行 snabbdom 的初始化。
import snabbdom from "snabbdom"; const patch = snabbdom.init([ // 指定模塊初始化 patch 方法 require("snabbdom/modules/class"), // 切換 class require("snabbdom/modules/props"), // 設置 DOM 元素的屬性 require("snabbdom/modules/style"), // 處理元素的 style ,支持動畫 require("snabbdom/modules/eventlisteners"), // 事件處理 ]);
上面的代碼中,我們初始化了 snabbdom 模塊并添加了一些擴展。在 snabbdom 中,切換 class、style還有 DOM 元素上的屬性設置和事件綁定都是給不同模塊實現的。上面的實例,只使用了默認提供的模塊。
核心模塊只暴露了一個 patch 方法,它由 init 方法返回。我們使用它創建初始化的 DOM,之后也會使用它來進行 DOM 的更新。
下面是一個 Hello World 示例:
import h from "snabbdom/h"; var vnode = h("div", {style: {fontWeight: "bold"}}, "Hello world"); patch(document.getElementById("placeholder"), vnode);
h 是一個創建虛擬 DOM 的輔助函數。我們將在文章后面介紹具體用法,現在只需要該函數的 3 個輸入參數:
一個 CSS 選擇器(jQuery 的選擇器),比如 div#id.class。
一個可選的數據對象,它包含了虛擬節點的屬性(class、styles、events)。
一個字符串或者虛擬 DOM 的數組(用于表示該節點的children)。
第一次調用的時候,patch 方法需要一個 DOM 占位符和一個初始的虛擬 DOM,然后它會根據虛擬 DOM 創建一個對應的真實 DO樹。在隨后的的調用中,我們為它提供新舊兩個虛擬 DOM,然后它通過 diff 算法比對這兩個虛擬 DOM,并找出更新的部分對真實 DOM 進行必要的修改 ,使得真實的 DOM 樹為最新的虛擬 DOM 的映射。
為了快速上手,我在 GitHub 上創建了一個倉庫,其中包含了項目的必要內容。下面讓我們來克隆這個倉庫(yelouafi/snabbdom-starter),然后運行 npm install 安裝依賴。這個倉庫使用 Browserify 作為打包工具,文件變更后使用 Watchify 自動重新構建,并且通過 Babel 將 ES6 的代碼轉成兼容性更好的 ES5。
下面運行如下代碼:
npm run watch
這段代碼將啟動 watchify 模塊,它會在 app 文件夾內,創建一個瀏覽器能夠運行的包:build.js 。模塊還將檢測我們的 js 代碼是否發生改變,如果有修改,會自動的重新構建 build.js。(如果你想手動構建,可以使用:npm run build)
在瀏覽器中打開 app/index.html 就能運行程序,這時候你會在屏幕上看到 “Hello World”。
這篇文中的所有案例都能在特定的分支上進行實現,我會在文中鏈接到每個分支,同時 README.md 文件也包含了所有分支的鏈接。
動態視圖本例的源代碼在 dynamic-view branch
為了突出虛擬 DOM 動態化的優勢,接下來會構建一個很簡單的時鐘。
首先修改 app/js/main.js:
function view(currentDate) { return h("div", "Current date " + currentDate); } var oldVnode = document.getElementById("placeholder"); setInterval( () => { const newVnode = view(new Date()); oldVnode = patch(oldVnode, newVnode); }, 1000);
通過多帶帶的函數 view 來生成虛擬 DOM,它接受一個狀態(當前日期)作為輸入。
該案例展示了虛擬 DOM 的經典使用方式,在不同的時刻構造出新的虛擬 DOM,然后將新舊虛擬 DOM 進行對比,并更新到真實 DOM 上。案例中,我們每秒都構造了一個虛擬 DOM,并用它來更新真實 DOM。
事件響應本例的源代碼在 event-reactivity branch
下面的案例介紹了通過事件系統完成一個打招呼的應用程序:
function view(name) { return h("div", [ h("input", { props: { type: "text", placeholder: "Type your name" }, on : { input: update } }), h("hr"), h("div", "Hello " + name) ]); } var oldVnode = document.getElementById("placeholder"); function update(event) { const newVnode = view(event.target.value); oldVnode = patch(oldVnode, newVnode); } oldVnode = patch(oldVnode, view(""));
在 snabbdom 中,我們使用 props 對象來設置元素的屬性,props 模塊會對 props 對象進行處理。類似地,我們通過 on 對象進行元素的時間綁定,eventlistener 模塊會對 on 對象進行處理。
上面的案例中,update 函數執行了與前面案例中 setInterval 類似的事情:從傳入的事件對象中提取出 input 的值,構造出一個新的虛擬 DOM,然后調用 patch ,用新的虛擬 DOM 樹更新真實 DOM。
復雜的應用程序使用獨立的虛擬 DOM 庫的好處是,我們在構建自己的應用時,可以按照自己喜歡的方式來做。你可以使用 MVC 的設計模式,可以使用更現代化的數據流體系,比如 Flux。
在這篇文章中,我會介紹一種不太為人所知的架構模式,是我之前在 Elm(一種可編譯成 JavaScript 的 函數式語言)中使用過的。Elm 的開發者稱這種模式為 Elm Architecture,它的主要優點是允許我們將整個應用編寫為一組純函數。
主流程讓我們回顧一下上個案例的主流程:
通過 view 函數構造出我們初始的虛擬 DOM,在 view 函數中,給 input 輸入框添加了一個 input 事件。
通過 patch 將虛擬 DOM 渲染到真實 DOM 中,并將 input 事件綁定到真實 DOM 上。
等待用戶輸入……
用戶輸入內容,觸發 input 事件,然后調用 update 函數
在 update 函數中,我們更新了狀態
我們傳入了新的狀態給 view 函數,并生成新的虛擬 DOM (與步驟 1 相同)
再次調用 patch,重復上述過程(與步驟 2 相同)
上面的過程可以描述成一個循環。如果去掉實現的一些細節,我們可以建立一個抽象的函數調用序列。
user 是用戶交互的抽象,我們得到的是函數調用的循環序列。注意,user 函數是異步的,否則這將是一個無限的死循環。
讓我們將上述過程轉換為代碼:
function main(initState, element, {view, update}) { const newVnode = view(initState, event => { const newState = update(initState, event); main(newState, newVnode, {view, update}); }); patch(oldVnode, newVnode); }
main 函數反映了上述的循環過程:給定一個初始狀態(initState),一個 DOM 節點和一個頂層組件(view + update),main 通過當前的狀態經過 view 函數構建出新的虛擬 DOM,然后通過補丁的方式更新到真實 DOM上。
傳遞給 view 函數的參數有兩個:首先是當前狀態,其次是事件處理的回調函數,對生成的視圖中觸發的事件進行處理?;卣{函數主要負責為應用程序構建一個新的狀態,并使用新的狀態重啟 UI 循環。
新狀態的構造委托給頂層組件的 update 函數,該函數是一個簡單的純函數:無論何時,給定當前狀態和當前程序的輸入(事件或行為),它都會為程序返回一個新的狀態。
要注意的是,除了 patch 方法會有副作用,主函數內不會有任何改變狀態行為發生。
main 函數有點類似于低級GUI框架的 main 事件循環,這里的重點是收回對 UI 事件分發流程的控制: 在實際狀態下,DOM API通過采用觀察者模式強制我們進行事件驅動,但是我們不想在這里使用觀察者模式,下面就會講到。
Elm 架構(Elm architecture)基于 Elm-architecture 的程序中,是由一個個模塊或者說組件構成的。每個組件都有兩個基本函數:update和view,以及一個特定的數據結構:組件擁有的 model 以及更新該 model 實例的 actions。
update 是一個純函數,接受兩個參數:組件擁有的 model 實例,表示當前的狀態(state),以及一個 action 表示需要執行的更新操作。它將返回一個新的 model 實例。
view 同樣接受兩個參數:當前 model 實例和一個事件通道,它可以通過多種形式傳播數據,在我們的案例中,將使用一個簡單的回調函數。該函數返回一個新的虛擬 DOM,該虛擬 DOM 將會渲染成真實 DOM。
如上所述,Elm architecture 擺脫了傳統的由事件進行驅動觀察者模式。相反該架構傾向于集中式的管理數據(比如 React/Flux),任何的事件行為都會有兩種方式:
冒泡到頂層組件;
通過組件樹的形式進行下發,在此階段,每個組件都可以選擇自己的處理方式,或者轉發給其他一個或所有子組件。
該架構的另一個關鍵點,就是將程序需要的整個狀態都保存在一個對象中。樹中的每個組件都負責將它們擁有的狀態的一部分傳遞給子組件。
在我們的案例中,我們將使用與 Elm 網站相同的案例,因為它完美的展示了該模式。
案例一:計數器本例的源代碼在 counter-1 branch
我們在 “counter.js” 中定義了 counter 組件:
const INC = Symbol("inc"); const DEC = Symbol("dec"); // model : Number function view(count, handler) { return h("div", [ h("button", { on : { click: handler.bind(null, {type: INC}) } }, "+"), h("button", { on : { click: handler.bind(null, {type: DEC}) } }, "-"), h("div", `Count : ${count}`), ]); } function update(count, action) { return action.type === INC ? count + 1 : action.type === DEC ? count - 1 : count; } export default { view, update, actions : { INC, DEC } }
counter 組件由以下屬性組成:
Model: 一個簡單的 Number
View:為用戶提供兩個按鈕,用戶遞增、遞減計數器,以及顯示當前數字
Update:接受兩個動作:INC / DEC,增加或減少計數器的值
首先要注意的是,view/update 都是純函數,除了輸入之外,他們不依賴任何外部環境。計數器組件本身不包括任何狀態或變量,它只會從給定的狀態構造出固定的視圖,以及通過給定的狀態更新視圖。由于其純粹性,計數器組件可以輕松的插入任何提供依賴(state 和 action)環境。
其次需要注意 handler.bind(null, action) 表達式,每次點擊按鈕,事件監聽器都會觸發該函數。我們將原始的用戶事件轉換為一個有意義的操作(遞增或遞減),使用了 ES6 的 Symbol 類型,比原始的字符串類型更好(避免了操作名稱沖突的問題),稍后我們還將看到更好的解決方案:使用 union 類型。
下面看看如何進行組件的測試,我們使用了 “tape” 測試庫:
import test from "tape"; import { update, actions } from "../app/js/counter"; test("counter update function", (assert) => { var count = 10; count = update(count, {type: actions.INC}); assert.equal(count, 11); count = update(count, {type: actions.DEC}); assert.equal(count, 10); assert.end(); });
我們可以直接使用 babel-node 來進行測試
babel-node test/counterTest.js案例二:兩個計數器
本例的源代碼在 counter-2 branch
我們將和 Elm 官方教程保持同步,增加計數器的數量,現在我們會有2個計數器。此外,還有一個“重置”按鈕,將兩個計數器同時重置為“0”;
首先,我們需要修改計數器組件,讓該組件支持重置操作。為此,我們將引入一個新函數 init,其作用是為計數器構造一個新狀態 (count)。
function init() { return 0; }
init 在很多情況下都非常有用。例如,使用來自服務器或本地存儲的數據初始化狀態。它通過 JavaScript 對象創建一個豐富的數據模型(例如,為一個 JavaScript 對象添加一些原型屬性或方法)。
init 與 update 有一些區別:后者執行一個更新操作,然后從一個狀態派生出新的狀態;但是前者是使用一些輸入值(比如:默認值、服務器數據等等)構造一個狀態,輸入值是可選的,而且完全不管前一個狀態是什么。
下面我們將通過一些代碼管理兩個計數器,我們在 towCounters.js 中實現我們的代碼。
首先,我們需要定義模型相關的操作類型:
//{ first : counter.model, second : counter.model } const RESET = Symbol("reset"); const UPDATE_FIRST = Symbol("update first"); const UPDATE_SECOND = Symbol("update second");
該模型導出兩個屬性:first 和 second 分別保存兩個計數器的狀態。我們定義了三個操作類型:第一個用來將計數器重置為 0,另外兩個后面也會講到。
組件通過 init 方法創建 state。
function init() { return { first: counter.init(), second: counter.init() }; }
view 函數負責展示這兩個計數器,并為用戶提供一個重置按鈕。
function view(model, handler) { return h("div", [ h("button", { on : { click: handler.bind(null, {type: RESET}) } }, "Reset"), h("hr"), counter.view(model.first, counterAction => handler({ type: UPDATE_FIRST, data: counterAction})), h("hr"), counter.view(model.second, counterAction => handler({ type: UPDATE_SECOND, data: counterAction})), ]); }
我們給 view 方法傳遞了兩個參數:
每個視圖都會獲得父組件的部分狀態(model.first / model.second)
動態處理函數,它會傳遞到每個子節點的 view 。比如:第一個計數器觸發了一個動作,我們會將 UPDATE_FIRST 封裝在 action 中,當父類的 update 方法被調用時,我們會將計數器需要的 action(存儲在 data 屬性中)轉發到正確的計數器,并調用計數器的 update 方法。
下面看看 update 函數的實現,并導出組件的所有屬性。
function update(model, action) { return action.type === RESET ? { first : counter.init(), second: counter.init() } : action.type === UPDATE_FIRST ? {...model, first : counter.update(model.first, action.data) } : action.type === UPDATE_SECOND ? {...model, second : counter.update(model.second, action.data) } : model; } export default { view, init, update, actions : { UPDATE_FIRST, UPDATE_SECOND, RESET } }
update 函數處理3個操作:
RESET 操作會調用 init 將每個計數器重置到默認狀態。
UPDATE_FIRST 和 UPDATE_SECOND,會封裝一個計數器需要 action。函數將封裝好的 action 連同其 state 轉發給相關的子計數器。
{...model, prop: val}; 是 ES7 的對象擴展屬性(如object .assign),它總是返回一個新的對象。我們不修改參數中傳遞的 state ,而是始終返回一個相同屬性的新 state 對象,確保更新函數是一個純函數。
最后調用 main 方法,構造頂層組件:
main( twoCounters.init(), // the initial state document.getElementById("placeholder"), twoCounters );
“towCounters” 展示了經典的嵌套組件的使用模式:
組件通過類似于樹的層次結構進行組織。
main 函數調用頂層組件的 view 方法,并將全局的初始狀態和處理回調(main handler)作為參數。
在視圖渲染的時候,父組件調用子組件的 view 函數,并將子組件相關的 state 傳給子組件。
視圖將用戶事件轉化為對程序更有意義的 actions。
從子組件觸發的操作會通過父組件向上傳遞,直到頂層組件。與 DOM 事件的冒泡不同,父組件不會在此階段進行操作,它能做的就是將相關信息添加到 action 中。
在冒泡階段,父組件的 view 函數可以攔截子組件的 actions ,并擴展一些必要的數據。
該操作最終在主處理程序(main handler)中結束,主處理程序將通過調用頂部組件的 update 函數進行派發操作。
每個父組件的 update 函數負責將操作分派給其子組件的 update 函數。通常使用在冒泡階段添加了相關信息的 action。
案例三:計數器列表本例的源代碼在 counter-3 branch
讓我們繼續來看 Elm 的教程,我們將進一步擴展我們的示例,可以管理任意數量的計數器列表。此外還提供新增計數器和刪除計數器的按鈕。
“counter” 組件代碼保持不變,我們將定義一個新組件 counterList 來管理計數器數組。
我們先來定義模型,和一組關聯操作。
/* model : { counters: [{id: Number, counter: counter.model}], nextID : Number } */ const ADD = Symbol("add"); const UPDATE = Symbol("update counter"); const REMOVE = Symbol("remove"); const RESET = Symbol("reset");
組件的模型包括了兩個參數:
一個由對象(id,counter)組成的列表,id 屬性與前面實例的 first 和 second 屬性作用類似;它將標識每個計數器的唯一性。
nextID 用來維護一個做自動遞增的基數,每個新添加的計數器都會使用 nextID + 1 來作為它的 ID。
接下來,我們定義 init 方法,它將構造一個默認的 state。
function init() { return { nextID: 1, counters: [] }; }
下面定義一個 view 函數。
function view(model, handler) { return h("div", [ h("button", { on : { click: handler.bind(null, {type: ADD}) } }, "Add"), h("button", { on : { click: handler.bind(null, {type: RESET}) } }, "Reset"), h("hr"), h("div.counter-list", model.counters.map(item => counterItemView(item, handler))) ]); }
視圖提供了兩個按鈕來觸發“添加”和“重置”操作。每個計數器的都通過 counterItemView 函數來生成虛擬 DOM。
function counterItemView(item, handler) { return h("div.counter-item", {key: item.id }, [ h("button.remove", { on : { click: e => handler({ type: REMOVE, id: item.id}) } }, "Remove"), counter.view(item.counter, a => handler({type: UPDATE, id: item.id, data: a})), h("hr") ]); }
該函數添加了一個 remove 按鈕在視圖中,并引用了計數器的 id 添加到 remove 的 action 中。
接下來看看 update 函數。
const resetAction = {type: counter.actions.INIT, data: 0}; function update(model, action) { return action.type === ADD ? addCounter(model) : action.type === RESET ? resetCounters(model) : action.type === REMOVE ? removeCounter(model, action.id) : action.type === UPDATE ? updateCounter(model, action.id, action.data) : model; } export default { view, update, actions : { ADD, RESET, REMOVE, UPDATE } }
該代碼遵循上一個示例的相同的模式,使用冒泡階段存儲的 id 信息,將子節點的 actions 轉發到頂層組件。下面是 update 的一個分支 “updateCounter” 。
function updateCounter(model, id, action) { return {...model, counters : model.counters.map(item => item.id !== id ? item : { ...item, counter : counter.update(item.counter, action) } ) }; }
上面這種模式可以應用于任何樹結構嵌套的組件結構中,通過這種模式,我們讓整個應用程序的結構進行了統一。
在 actions 中使用 union 類型在前面的示例中,我們使用 ES6 的 Symbols 類型來表示操作類型。在視圖內部,我們創建了帶有操作類型和附加信息(id,子節點的 action)的對象。
在真實的場景中,我們必須將 action 的創建邏輯移動到一個多帶帶的工廠函數中(類似于React/Flux中的 Action Creators)。在這篇文章的剩余部分,我將提出一個更符合 FP 精神的替代方案:union 類型。它是 FP 語言(如Haskell)中使用的 代數數據類型 的子集,您可以將它們看作具有更強大功能的枚舉。
union類型可以為我們提供以下特性:
定義一個可描述所有可能的 actions 的類型。
為每個可能的值提供一個工廠函數。
提供一個可控的流來處理所有可能的變量。
union 類型在 JavaScript 中不是原生的,但是我們可以使用一個庫來模擬它。在我們的示例中,我們使用 union-type (github/union-type) ,這是 snabbdom 作者編寫的一個小而美的庫。
先讓我們安裝這個庫:
npm install --save union-type
下面我們來定義計數器的 actions:
import Type from "union-type"; const Action = Type({ Increment : [], Decrement : [] });
Type 是該庫導出的唯一函數。我們使用它來定義 union 類型 Action,其中包含兩個可能的 actions。
返回的 Action 具有一組工廠函數,用于創建所有可能的操作。
function view(count, handler) { return h("div", [ h("button", { on : { click: handler.bind(null, Action.Increment()) } }, "+"), h("button", { on : { click: handler.bind(null, Action.Decrement()) } }, "-"), h("div", `Count : ${count}`), ]); }
在 view 創建遞增和遞減兩種 action。update 函數展示了 uinon 如何對不同類型的 action 進行模式匹配。
function update(count, action) { return Action.case({ Increment : () => count + 1, Decrement : () => count - 1 }, action); }
Action 具有一個 case 方法,該方法接受兩個參數:
一個對象(變量名和一個回調函數)
要匹配的值
然后,case方法將提供的 action 與所有指定的變量名相匹配,并調用相應的處理函數。返回值是匹配的回調函數的返回值。
類似地,我們看看如何定義 counterList 的 actions
const Action = Type({ Add : [], Remove : [Number], Reset : [], Update : [Number, counter.Action], });
Add和Reset是空數組(即它們沒有任何字段),Remove只有一個字段(計數器的 id)。最后,Update 操作有兩個字段:計數器的 id 和計數器觸發時的 action。
與之前一樣,我們在 update 函數中進行模式匹配。
function update(model, action) { return Action.case({ Add : () => addCounter(model), Remove : id => removeCounter(model, id), Reset : () => resetCounters(model), Update : (id, action) => updateCounter(model, id, action) }, action); }
注意,Remove 和 Update 都會接受參數。如果匹配成功,case 方法將從 case 實例中提取字段并將它們傳遞給對應的回調函數。
所以典型的模式是:
將 actions 建模為union類型。
在 view 函數中,使用 union 類型提供的工廠函數創建 action (如果創建的邏輯更復雜,還可以將操作創建委托給多帶帶的函數)。
在 update 函數中,使用 case 方法來匹配 union 類型的可能值。
TodoMVC例子在這個倉庫中(github/yelouafi/snabbdom-todomvc),使用本文提到的規范進行了 todoMVC 應用的實現。應用程序由2個模塊組成:
task.js 定義一個呈現單個任務并更新其狀態的組件
todos.js,它管理任務列表以及過濾和更新
總結我們已經了解了如何使用小而美的虛 擬DOM 庫編寫應用程序。當我們不想被迫選擇使用React框架(尤其是 class),或者當我們需要一個小型 JavaScript 庫時,這將非常有用。
Elm architecture 提供了一個簡單的模式來編寫復雜的虛擬DOM應用,具有純函數的所有優點。這為我們的代碼提供了一個簡單而規范的結構。使用標準的模式使得應用程序更容易維護,特別是在成員頻繁更改的團隊中。新成員可以快速掌握代碼的總體架構。
由于完全用純函數實現的,我確信只要組件代碼遵守其約定,更改組件就不會產生不良的副作用。
想查看更多前端技術相關文章可以逛逛我的博客:自然醒的博客
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/104047.html
摘要:市面上竟然擁有多個虛擬庫。虛擬庫,就是出來后的一種新式庫,以虛擬與算法為核心,屏蔽操作,操作數據即操作視圖。及其他虛擬庫已經將虛擬的生成交由與處理了,因此不同點是,虛擬的結構與算法。因此虛擬庫是分為兩大派系算法派與擬態派。 去哪兒網迷你React是年初立項的新作品,在這前,去哪兒網已經深耕多年,擁有QRN(react-native的公司制定版),HY(基于React的hybird方案)...
摘要:閱讀源碼的時候,想了解虛擬結構的實現,發現在的地方。然而慢慢的人們發現,在我們的代碼中布滿了一系列操作的代碼。源碼解析系列源碼解析一準備工作源碼解析二函數源碼解析三對象源碼解析四方法源碼解析五鉤子源碼解析六模塊源碼解析七事件處理個人博客地址 前言 虛擬 DOM 結構概念隨著 react 的誕生而火起來,之后 vue2.0 也加入了虛擬 DOM 的概念。 閱讀 vue 源碼的時候,想了解...
摘要:毫無疑問的是算法的復雜度與效率是決定能夠帶來性能提升效果的關鍵因素。速度略有損失,但可讀性大大提高。因此目前的主流算法趨向一致,在主要思路上,與的方式基本相同。在里面實現了的算法與支持。是唯一添加的方法所以只發生在中。 VirtualDOM是react在組件化開發場景下,針對DOM重排重繪性能瓶頸作出的重要優化方案,而他最具價值的核心功能是如何識別并保存新舊節點數據結構之間差異的方法,...
摘要:很多人認為虛擬最大的優勢是算法,減少操作真實的帶來的性能消耗。雖然這一個虛擬帶來的一個優勢,但并不是全部?;氐阶铋_始的問題,虛擬到底是什么,說簡單點,就是一個普通的對象,包含了三個屬性。 是什么? 虛擬 DOM (Virtual DOM )這個概念相信大家都不陌生,從 React 到 Vue ,虛擬 DOM 為這兩個框架都帶來了跨平臺的能力(React-Native 和 Weex)。因...
閱讀 1076·2021-10-14 09:42
閱讀 1369·2021-09-22 15:11
閱讀 3285·2019-08-30 15:56
閱讀 1243·2019-08-30 15:55
閱讀 3612·2019-08-30 15:55
閱讀 889·2019-08-30 15:44
閱讀 2028·2019-08-29 17:17
閱讀 2072·2019-08-29 15:37