摘要:閑聊在學(xué)的過(guò)程中,虛擬應(yīng)該是聽(tīng)的最多的概念之一,得知其是借鑒進(jìn)行開(kāi)發(fā),故習(xí)之。以我的觀點(diǎn)來(lái)看,多個(gè)相同元素渲染時(shí),則需要為每個(gè)元素添加值。
閑聊:在學(xué)vue的過(guò)程中,虛擬dom應(yīng)該是聽(tīng)的最多的概念之一,得知其是借鑒snabbdom.js進(jìn)行開(kāi)發(fā),故習(xí)之。由于我工作處于IE8的環(huán)境,對(duì)ES6,TS這些知識(shí)的練習(xí)也只是淺嘗輒止,而snabbdom.js從v.0.5.4這個(gè)版本后開(kāi)始使用TS,所以我下載了0.5.4這個(gè)版本進(jìn)行學(xué)習(xí)(后來(lái)才發(fā)現(xiàn)可以直接下載最新的版本,去dist目錄找編譯好的文件即可,而且這個(gè)版本還有BUG,在新版本中得到了修改,建議大家還是下載最新版本進(jìn)行學(xué)習(xí))
總共寫了四篇文章(都是自己的一些拙見(jiàn),僅供參考,請(qǐng)多多指教,我這邊也會(huì)持續(xù)修正加更新)
介紹一下snabbdom基本用法
介紹一下snabbdom渲染原理
介紹一下snabddom的diff算法和對(duì)key值的認(rèn)識(shí)
介紹一下對(duì)于兼容IE8的修改
github
ps:學(xué)習(xí)的目的是希望將snabbdom.js實(shí)踐到工作中去,思前想后,決定拿表格渲染來(lái)開(kāi)刀,而且兼容了IE8
當(dāng)然我也是站在巨人肩膀上進(jìn)行學(xué)習(xí),參考文章:
snabbdom入門使用
vue2源碼學(xué)習(xí)開(kāi)胃菜——snabbdom源碼學(xué)習(xí)(一)
vue2源碼學(xué)習(xí)開(kāi)胃菜——snabbdom源碼學(xué)習(xí)(二)
好了,前面說(shuō)了那么多‘廢話’,現(xiàn)在切入主題。
開(kāi)門見(jiàn)山,先總結(jié)一下,通過(guò)自己的實(shí)踐,個(gè)人認(rèn)為虛擬dom的實(shí)現(xiàn)思路為:
通過(guò)js對(duì)象模擬出一個(gè)我們需要渲染到頁(yè)面上的dom樹(shù)的結(jié)構(gòu),實(shí)現(xiàn)了一個(gè)修改js對(duì)象即可修改頁(yè)面dom的快捷途徑,避免了我們手動(dòng)再去一次次操作dom-api的繁瑣,而且其提供了算法可以使得用最少的dom操作進(jìn)行修改。
對(duì)于基礎(chǔ)用法的介紹,英語(yǔ)好的完全可以去看一下它github的內(nèi)容 snabbdom.js,我這邊主要是記錄自己在實(shí)踐過(guò)程中的一些筆記及踩坑。
1.如何引用我這邊還是以0.5.4版本進(jìn)行講解
核心文件是:
snabbdom.js
h.js
vnode.js(最新版本改為tovnode.js)
htmldomapi.js
is.js(這個(gè)文件是用來(lái)提供函數(shù)來(lái)判斷數(shù)據(jù)是否為undefined,最新版本已經(jīng)沒(méi)把它多帶帶拿出來(lái)了)
polyfill.js(我這邊為了兼容IE8自己添加的文件)
有了這幾個(gè)文件其實(shí)就可以使用snabbdom.js來(lái)渲染我們的頁(yè)面。
當(dāng)然還有很重要的模塊文件:
style.js
props.js
eventlistener.js
class.js
attribute.js
dataset.js
eventlistener.js
這些模塊規(guī)定了我們虛擬dom具備哪些能力,例如很重要的eventlistener.js使得我們可以在虛擬dom上添加事件,它們都是我們不可或缺的。作者將其分離出來(lái)應(yīng)該是想剝離出核心代碼,使得我們可以根據(jù)自己的需求來(lái)定制相應(yīng)的模塊。
引用的時(shí)候各個(gè)文件之間還是有一定順序的,我是這樣引用的:(snabbdom.js是最后引用,輔助型文件polyfill.js is.js得最早引用):
當(dāng)然你也可以把所有文件進(jìn)行壓縮合并,代碼中還可以使用模塊化的方式進(jìn)行引用相關(guān)模塊;
ps:由于我們這邊還沒(méi)有使用模塊化,所以我把源碼中使用模塊化的部分簡(jiǎn)單的修改了一下;
模塊化也就是將一個(gè)功能多帶帶寫在一個(gè)js文件中供其它文件使用,會(huì)使用一個(gè)對(duì)象進(jìn)行封裝導(dǎo)出,并通過(guò)立即執(zhí)行函數(shù)的閉包使得其不會(huì)污染其它作用域變量。
舉例:
導(dǎo)出
//a.js
aModule={}; (function(aModule){ aModule.init=function(){} })(aModule)
導(dǎo)入
var init=aModule.init;2.如何使用
先從最簡(jiǎn)單的例子來(lái)看看snabbdom.js是如何使用的;
代碼如下:
var snabbdom = SnabbdomModule; var patch = snabbdom.init([ //導(dǎo)入相應(yīng)的模塊 DatasetModule, ClassModule, AttributesModule, PropsModule, StyleModule, EventlistenerModule ]); var h = HModule.h; var app = document.getElementById("app"); var newVnode = h("div#divId.red", {}, [h("p", {},"已改變")]) var vnode = h("div#divId.red", {}, [h("p",{},"2S后改變")]) vnode = patch(app, vnode); setTimeout(function() { vnode=patch(vnode, newVnode); }, 2000)
上面代碼的主要功能就是渲染,通過(guò)snabbdom模塊的init方法返回的patch函數(shù)實(shí)現(xiàn),細(xì)分的話可以分為初始化渲染和對(duì)比渲染;
第一次是初始化的時(shí)候,vnode=patch(app,vnode),app作為一個(gè)被替換的真實(shí)dom傳入,返回一個(gè)當(dāng)前頁(yè)面的vnode,作為下一次渲染的對(duì)比虛擬dom。(這里需要注意的是,app是在這里作為一個(gè)替換dom,渲染后app將會(huì)被替換);
第二次是對(duì)比渲染,vnode=patch(vnode, newVnode);
上面的h函數(shù)是一個(gè)重點(diǎn),它里面的內(nèi)容其實(shí)就是頁(yè)面dom元素的一個(gè)抽象:
h("div#divId.red", {}, [h("p",{},"2S后改變")]) //通過(guò)從上面的這個(gè)例子,我們知道如何用snabbdom.js來(lái)渲染頁(yè)面了,不過(guò)漏了一個(gè)重點(diǎn),就是h函數(shù)的第二個(gè)參數(shù),模塊參數(shù)的使用,下面我們改造一下vnode;
vnode = h("div#divId.red", { "class": { "active": true }, "style": { "background": "#fff" }, "on": { "click": clickFn }, "dataset": { "name": "liuzj" }, "hook": { "init": function() { console.log("init") }, "create": function() { console.log("create") }, "insert": function() { console.log("insert") }, "prepatch": function() { console.log("beforePatch") }, "update": function() { console.log("update") }, "postpatch": function() { console.log("postPatch") }, "destroy": function() { console.log("destroy") }, "remove": function(ch, rm) { console.log("remove") rm(); } } }, [h("p", {}, "2S后改變")]) function clickFn() { console.log("click") } vnode = patch(app, vnode);下面是代碼的效果:
class:這里我們可以理解為動(dòng)態(tài)的類名,sel上的類可以理解為靜態(tài)的,例如上面class:{active:true}我們可以通過(guò)控制這個(gè)變量來(lái)表示此元素是否是當(dāng)前被點(diǎn)擊
style:內(nèi)聯(lián)樣式
on:綁定的事件類型
對(duì)于綁定事件的實(shí)踐:
綁定click事件,不傳自定義參數(shù)
var newVnode = h("div", { on: { "click":clickfn1 }},"div") function clickfn1(e,vnode) { console.log(e) console.log(vnode) }
綁定click事件,傳自定義參數(shù)
var newVnode = h("div", { on: { "click":[clickfn1,"arg1","arg2"] }},"div") function clickfn1(val1,val2,e,vnode) { console.log(val1) console.log(val2) console.log(e) console.log(vnode) }
為click事件綁定多個(gè)回調(diào)函數(shù)
var newVnode = h("div", { on: { "click":[[clickfn1,"arg1","arg2"],[clickfn2,"arg1","arg2"]] }},"div") function clickfn1(val1,val2,e,vnode) { console.log(val1) console.log(val2) console.log(e) console.log(vnode) } function clickfn2(val1,val2,e,vnode) { console.log(val1) console.log(val2) console.log(e) console.log(vnode) }在綁定多個(gè)回調(diào)函數(shù)時(shí),源碼存在一個(gè)問(wèn)題,回調(diào)參數(shù)中的event和vnode獲取不到,修改源碼即可:
eventlistener.js:
for (var i = 0; i < handler.length; i++) { invokeHandler(handler[i]); } 改為: for (var i = 0; i < handler.length; i++) { invokeHandler(handler[i], vnode, event); }dataset:data屬性
hook:鉤子函數(shù)
這些鉤子函數(shù)是在模塊中使用的: pre, create, update, destroy, remove, post.
這些鉤子函數(shù)是自己定義在虛擬dom中使用的: init, create, insert, prepatch, update, postpatch, destroy, remove.
在實(shí)踐鉤子函數(shù)的時(shí)候遇到的一些情況:如果你的vnode進(jìn)行patch的時(shí)候sel值不同時(shí),只會(huì)觸發(fā)init create destroy remove insert ,因?yàn)檫@理會(huì)將舊的vnode全部刪創(chuàng)建新的vnode 比如:sel:div --> sel:p
如果你的vnode進(jìn)行patch的時(shí)候sel值相同時(shí),只會(huì)觸發(fā)beforePatch update postPatch,因?yàn)檫@里只是在舊的vnode上進(jìn)行更新
在使用remove鉤子函數(shù)的時(shí)候需要注意的是,函數(shù)會(huì)返回一個(gè)rm函數(shù)參數(shù),我們需要執(zhí)行這個(gè)函數(shù)才能將刪除舊節(jié)點(diǎn)。
舉例說(shuō)明:
var newVnode = h("div#divId", [h("p", "已改變")]) var vnode = h("div#divId.red", { "hook": { "remove": function() { console.log("remove") } } }, [h("p", "2S后改變")]) vnode = patch(app, vnode); setTimeout(function() { patch(vnode, newVnode); }, 2000)正確使用的方法為:
"remove": function(ch, rm) { console.log("remove") rm(); }
props/attribute:設(shè)置元素自身的屬性
h("div#divId.red", [h("a",{ attrs:{ href:"http://baidu.com"}},"百度")]) h("div#divId.red", [h("a",{ props:{ href:"http://baidu.com"}},"百度")])不過(guò)對(duì)于disabled checked這樣的屬性最好是用props
h("div#divId.red", [h("button", {props: {disabled: true}}, "按鈕")])3.對(duì)于Key值的使用key值算是一個(gè)snabbdom中diff算法的一個(gè)核心內(nèi)容,關(guān)于diff算法的核心思想我會(huì)在下一篇介紹,這一篇主要是講一下使用。
以我的觀點(diǎn)來(lái)看,多個(gè)相同元素渲染時(shí),則需要為每個(gè)元素添加key值。
例如
- li1
- li2
- li2
-->- li3
- li3
- li4
var vnode = h("ul", [h("li", { key: 1 }, "li1"), h("li", { key: 2 }, "li2"), h("li", { key: 3 }, "li3")]) var newVnode = h("ul", [h("li", { key: 2 }, "li2"), h("li", { key: 3 }, "li3"), h("li", { key: 4 }, "li4")])當(dāng)然,在實(shí)際工作中,我們肯定不會(huì)像上面那樣寫,都是利用循環(huán)進(jìn)行動(dòng)態(tài)渲染。
var data1 = [{ name: "li1" }, { name: "li2" }, { name: "li3" }] var data2 = [{ name: "li2" }, { name: "li3" }, { name: "li4" }] var vnode = h("ul", data1.map(function(item) { return h("li", { key: item.name }, item.name) })) var newVnode = h("ul", data2.map(function(item) { return h("li", { key: item.name }, item.name) })) vnode = patch(app, vnode); setTimeout(function() { patch(vnode, newVnode); }, 2000)這里需要記住的是:這個(gè)key值要唯一,而且需要一一對(duì)應(yīng)
很多人喜歡在循環(huán)的數(shù)組中用index來(lái)作為key值,嚴(yán)格意義上來(lái)說(shuō)這樣做是不恰當(dāng)?shù)模琸ey值不僅需要唯一,還需要一一對(duì)應(yīng)(同一個(gè)節(jié)點(diǎn)舊vnode中和新vnode中的key值要一樣),當(dāng)然如果你使用key值的元素它不存在增刪和排序的需求,那么index作為key值沒(méi)有影響。
至于原因,下一篇我會(huì)說(shuō)一下;前面說(shuō)到了ul/li需要使用key,還有就是我目前做的表格渲染,也需要使用key,因?yàn)楸砀駮?huì)涉及到tbody/tr/td,其中tr和td都會(huì)存在多個(gè),而tr會(huì)有增刪和排序,td只是值的修改,位置不會(huì)發(fā)生變化,所以我在實(shí)際操作的過(guò)程中,tr的key值一一對(duì)應(yīng)的,而td的key值則是用index來(lái)賦值。
希望大家看完能有收獲,歡迎指正!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/100343.html
摘要:毫無(wú)疑問(wèn)的是算法的復(fù)雜度與效率是決定能夠帶來(lái)性能提升效果的關(guān)鍵因素。速度略有損失,但可讀性大大提高。因此目前的主流算法趨向一致,在主要思路上,與的方式基本相同。在里面實(shí)現(xiàn)了的算法與支持。是唯一添加的方法所以只發(fā)生在中。 VirtualDOM是react在組件化開(kāi)發(fā)場(chǎng)景下,針對(duì)DOM重排重繪性能瓶頸作出的重要優(yōu)化方案,而他最具價(jià)值的核心功能是如何識(shí)別并保存新舊節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)之間差異的方法,...
摘要:這個(gè)大概是的鉤子吧在每一次插入操作的時(shí)候都將節(jié)點(diǎn)這類型方法可以看出來(lái)是在調(diào)用對(duì)應(yīng)的方法因?yàn)殚_(kāi)始的時(shí)候就導(dǎo)入進(jìn)來(lái)了插入節(jié)點(diǎn)操作的時(shí)候都需要加入子節(jié)點(diǎn)有子元素也就是的時(shí)候遞歸調(diào)用循環(huán)子節(jié)點(diǎn)生成對(duì)應(yīng)著一些操作之后都要觸發(fā)鉤子函數(shù)。 snabbdom 本文的snabbdom源碼分析采用的是0.54版本(即未用ts重寫前的最后一版) 前期了解 snabbdom被用作vue的虛擬dom。本文的一個(gè)...
摘要:總共寫了四篇文章都是自己的一些拙見(jiàn),僅供參考,請(qǐng)多多指教,我這邊也會(huì)持續(xù)修正加更新介紹一下基本用法介紹一下渲染原理介紹一下的算法和對(duì)值的認(rèn)識(shí)介紹一下對(duì)于兼容的修改這篇主要是說(shuō)一下的算法在上一篇中我總結(jié)過(guò)對(duì)比渲染的流程大體分為通過(guò)來(lái)判斷兩個(gè)是 總共寫了四篇文章(都是自己的一些拙見(jiàn),僅供參考,請(qǐng)多多指教,我這邊也會(huì)持續(xù)修正加更新) 介紹一下snabbdom基本用法 介紹一下snabbdo...
摘要:總共寫了四篇文章都是自己的一些拙見(jiàn),僅供參考,請(qǐng)多多指教,我這邊也會(huì)持續(xù)修正加更新介紹一下基本用法介紹一下渲染原理介紹一下的算法和對(duì)值的認(rèn)識(shí)介紹一下對(duì)于兼容的修改這篇主要是記錄一下針對(duì)做了哪些修改增加用來(lái)兼容某些功能函數(shù),例如等將每個(gè)文件單 總共寫了四篇文章(都是自己的一些拙見(jiàn),僅供參考,請(qǐng)多多指教,我這邊也會(huì)持續(xù)修正加更新) 介紹一下snabbdom基本用法 介紹一下snabbdo...
摘要:如果新舊的和都相同,說(shuō)明兩個(gè)相似,我們就可以保留舊的節(jié)點(diǎn),再具體去比較其差異性,在舊的上進(jìn)行打補(bǔ)丁否則直接替換節(jié)點(diǎn)。 總共寫了四篇文章(都是自己的一些拙見(jiàn),僅供參考,請(qǐng)多多指教,我這邊也會(huì)持續(xù)修正加更新) 介紹一下snabbdom基本用法 介紹一下snabbdom渲染原理 介紹一下snabddom的diff算法和對(duì)key值的認(rèn)識(shí) 介紹一下對(duì)于兼容IE8的修改 這篇我將以自己的思路去...
閱讀 726·2021-11-15 11:37
閱讀 3347·2021-10-27 14:14
閱讀 6256·2021-09-13 10:30
閱讀 2998·2021-09-04 16:48
閱讀 1963·2021-08-18 10:22
閱讀 2154·2019-08-30 14:19
閱讀 762·2019-08-30 10:54
閱讀 1774·2019-08-29 18:40