摘要:上集回顧從零開始手把手教你實(shí)現(xiàn)一個(gè)一上一集我們介紹了什么是,為什么要用,以及我們要怎樣來實(shí)現(xiàn)一個(gè)。完成后,在命令行中輸入安裝下依賴。最后返回這個(gè)目標(biāo)節(jié)點(diǎn)。明天,我們迎接挑戰(zhàn),開始處理數(shù)據(jù)變動(dòng)引起的重新渲染,我們要如何新舊,生成補(bǔ)丁,修改。
上集回顧
從零開始手把手教你實(shí)現(xiàn)一個(gè)Virtual DOM(一)
上一集我們介紹了什么是VDOM,為什么要用VDOM,以及我們要怎樣來實(shí)現(xiàn)一個(gè)VDOM。我們?cè)賮砜匆幌逻@張藍(lán)圖,今天我們要實(shí)現(xiàn)的是這張圖的左半部分。
{ "name": "vdom", "version": "1.0.0", "description": "", "scripts": { "compile": "babel index.js --out-file compiled.js" }, "author": "", "license": "", "devDependencies": { "babel-cli": "^6.23.0", "babel-plugin-transform-react-jsx": "^6.23.0" } }
這里主要主要兩點(diǎn):
devDependencies中依賴babel-cli和babel-plugin-transform-react-jsx這兩個(gè)庫(kù),前者提供Babel的命令行功能,后者主要幫我們把jsx轉(zhuǎn)化成js。
scripts中我們指定了一條命令:complile,每次當(dāng)我們?cè)诋?dāng)前目錄下的命令行中敲npm run compile時(shí),babal就會(huì)將我們的index.js轉(zhuǎn)化后新建一個(gè)compile.js文件。
完成后,在命令行中輸入npm install安裝下依賴。
.babelrc{ "plugins": [ ["transform-react-jsx", { "pragma": "h" // default pragma is React.createElement }] ] }
在babel的配置文件中,我們指定transform-react-jsx這個(gè)插件將轉(zhuǎn)化后的函數(shù)名設(shè)置為h。默認(rèn)的函數(shù)名是React.createElement,我們不依賴react,所以顯然換個(gè)自己的名字更合適。這里不清楚h是干什么的不要緊,等會(huì)看到代碼你就知道了。
index.htmlVDOM
這個(gè)HTML還是很直觀的,類似React,我們有一個(gè)根節(jié)點(diǎn)id是app。然后我們r(jià)ender函數(shù)最終生成的DOM會(huì)插入到app這個(gè)根節(jié)點(diǎn)里。注意我們引用的compile.js文件是babel根據(jù)等會(huì)要寫的index.js文件自動(dòng)生成的。
index.js首先,我們用JSX來編寫“模板”:
function view() { return
接下來,我們要將JSX編譯成js, 也就是hyperscript。我們先用Babel編譯一下,看這段JSX轉(zhuǎn)成js會(huì)是什么樣子,打開命令行,輸入npm run compile,得到的compile.js:
function view() { return h( "ul", { id: "filmList", className: "list" }, h( "li", { className: "main" }, "Detective Chinatown Vol 2" ), h( "li", null, "Ferdinand" ), h( "li", null, "Paddington 2" ) ); }
可以看出h函數(shù)接收的參數(shù),第一個(gè)參數(shù)是node的類型,比如ul,li,第二個(gè)參數(shù)是node的屬性,之后的參數(shù)是node的children,假如child又是一個(gè)node的話,就會(huì)繼續(xù)調(diào)用h函數(shù)。
清楚了Babel會(huì)將我們的JSX編譯成什么樣子后,接下來我們就可以繼續(xù)在index.js中來寫h函數(shù)了。
function flatten(arr) { return [].concat(...arr) } function h(type, props, ...children) { return { type, props: props || {}, children: flatten(children) } }
我們的h函數(shù)主要的工作就是返回我們真正需要的hyperscript對(duì)象,只有三個(gè)參數(shù),第一個(gè)參數(shù)是節(jié)點(diǎn)類型,第二個(gè)參數(shù)是屬性對(duì)象,第三個(gè)是子節(jié)點(diǎn)的數(shù)組。
這里主要用了ES6的rest, spread參數(shù),不清楚代碼中兩個(gè)...分別是什么意思的可以先去看我的介紹ES6文章30分鐘掌握ES6/ES2015核心內(nèi)容(上)。簡(jiǎn)單來說,rest就是上面的...children,它將函數(shù)多余的參數(shù)放到一個(gè)數(shù)組里,所以children此時(shí)變成了一個(gè)數(shù)組。而spread則是rest的逆運(yùn)算,也就是上面的...arr,它將一個(gè)數(shù)組轉(zhuǎn)為用逗號(hào)分隔的參數(shù)序列。
flatten(children)這個(gè)操作是因?yàn)閏hildren這個(gè)數(shù)組里的元素有可能也是個(gè)數(shù)組,那樣就成了一個(gè)二維數(shù)組,所以我們需要將數(shù)組拍平成一維數(shù)組。[].concat(...arr)是ES6寫法,傳統(tǒng)的寫法是[].concat.apply([], arr)
我們現(xiàn)在可以先來看一下h函數(shù)最終返回的對(duì)象長(zhǎng)什么樣子。
function render() { console.log(view()) }
我們?cè)趓ender函數(shù)中打印出執(zhí)行完view()的結(jié)果,再npm run compile后,用瀏覽器打開我們的index.html,看控制臺(tái)輸出的結(jié)果。
可以,很完美!這個(gè)對(duì)象就是我們的VDOM了!
下面我們就可以根據(jù)VDOM, 來渲染真實(shí)DOM了。先改寫render函數(shù):
function render(el) { el.appendChild(createElement(view(0))) }
createElement函數(shù)生成DOM,然后再插入到我們?cè)趇ndex.html中寫的根節(jié)點(diǎn)app。注意render函數(shù)式在index.html中被調(diào)用的。
function createElement(node) { if (typeof(node) === "string") { return document.createTextNode(node) } let { type, props, children } = node const el = document.createElement(type) setProps(el, props) children.map(createElement) .forEach(el.appendChild.bind(el)) return el } function setProp(target, name, value) { if (name === "className") { return target.setAttribute("class", value) } target.setAttribute(name, value) } function setProps(target, props) { Object.keys(props).forEach(key => { setProp(target, key, props[key]) }) }
我們來仔細(xì)看下createElement函數(shù)。假如說node,即VDOM的類型是文本,我們直接返回一個(gè)創(chuàng)建好的文本節(jié)點(diǎn)。否則的話,我們?nèi)〕鰊ode中類型,屬性和子節(jié)點(diǎn), 先根據(jù)類型創(chuàng)建相應(yīng)的目標(biāo)節(jié)點(diǎn),然后再調(diào)用setProps函數(shù)依次設(shè)置好目標(biāo)節(jié)點(diǎn)的屬性,最后遍歷子節(jié)點(diǎn),遞歸調(diào)用createElement方法,將返回的子節(jié)點(diǎn)插入到剛剛創(chuàng)建的目標(biāo)節(jié)點(diǎn)里。最后返回這個(gè)目標(biāo)節(jié)點(diǎn)。
還需要注意的一點(diǎn)是,jsx中class的寫成了className,所以我需要特殊處理一下。
大功告成,complie后瀏覽器打開index.html看看結(jié)果吧。
今天我們成功的完成了藍(lán)圖的左半部分,將JSX轉(zhuǎn)化成hyperscript,再轉(zhuǎn)化成VDOM,最后根據(jù)VDOM生成DOM,渲染到頁(yè)面。明天,我們迎接挑戰(zhàn),開始處理數(shù)據(jù)變動(dòng)引起的重新渲染,我們要如何DIFF新舊VDOM,生成補(bǔ)丁,修改DOM。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/94603.html
摘要:可實(shí)際上并不是創(chuàng)造的,將這個(gè)概念拿過來以后融會(huì)貫通慢慢地成為目前前端最炙手可熱的框架之一。則是將再抽象一層生成的簡(jiǎn)化版對(duì)象,這個(gè)對(duì)象也擁有上的一些屬性,比如等,但它是完全脫離于瀏覽器而存在的。所以今天我要手把手教大家怎么從零開始實(shí)現(xiàn)。 假如你的項(xiàng)目使用了React,你知道怎么做性能優(yōu)化嗎?你知道為什么React讓你寫shouldComponentUpdate或者React.PureCo...
摘要:函數(shù)依次做了這幾件事調(diào)用函數(shù),對(duì)比新舊兩個(gè),根據(jù)兩者的不同得到需要修改的補(bǔ)丁將補(bǔ)丁到真實(shí)上當(dāng)計(jì)數(shù)器小于等于的時(shí)候,將加,再繼續(xù)下一次當(dāng)計(jì)數(shù)器大于的時(shí)候,結(jié)束下面我們來實(shí)現(xiàn)函數(shù)和函數(shù)。 上集回顧 【React進(jìn)階系列】從零開始手把手教你實(shí)現(xiàn)一個(gè)Virtual DOM(二) 上集我們實(shí)現(xiàn)了首次渲染從JSX=>Hyperscript=>VDOM=>DOM的過程,今天我們來看一下當(dāng)數(shù)據(jù)變動(dòng)的時(shí)...
摘要:但如果你想更加高效地使用來開發(fā),成為大師,那下面我要傳授的這五招你一定得認(rèn)真學(xué)習(xí)一下了。雖然損失了一丟丟性能,但避免了無限的。所以我們需要設(shè)置,這些默認(rèn)行為將會(huì)被去掉以上兩點(diǎn)的優(yōu)化才能成功。陸續(xù)可能還會(huì)更新一些別的招數(shù),敬請(qǐng)期待。 本文面向?qū)ο笫怯幸欢╒ue.js編程經(jīng)驗(yàn)的開發(fā)者。如果有人需要Vue.js入門系列的文章可以在評(píng)論區(qū)告訴我,有空就給你們寫。 對(duì)大部分人來說,掌握Vue.j...
摘要:模塊化是隨著前端技術(shù)的發(fā)展,前端代碼爆炸式增長(zhǎng)后,工程化所采取的必然措施。目前模塊化的思想分為和。特別指出,事件不等同于異步,回調(diào)也不等同于異步。將會(huì)討論安全的類型檢測(cè)惰性載入函數(shù)凍結(jié)對(duì)象定時(shí)器等話題。 Vue.js 前后端同構(gòu)方案之準(zhǔn)備篇——代碼優(yōu)化 目前 Vue.js 的火爆不亞于當(dāng)初的 React,本人對(duì)寫代碼有潔癖,代碼也是藝術(shù)。此篇是準(zhǔn)備篇,工欲善其事,必先利其器。我們先在代...
摘要:哪吒別人的看法都是狗屁,你是誰只有你自己說了才算,這是爹教我的道理。哪吒去他個(gè)鳥命我命由我,不由天是魔是仙,我自己決定哪吒白白搭上一條人命,你傻不傻敖丙不傻誰和你做朋友太乙真人人是否能夠改變命運(yùn),我不曉得。我只曉得,不認(rèn)命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出處 查看github最新的Vue...
閱讀 2227·2021-11-11 16:55
閱讀 1745·2019-08-30 15:54
閱讀 2885·2019-08-30 15:53
閱讀 2277·2019-08-30 15:44
閱讀 1213·2019-08-30 15:43
閱讀 1015·2019-08-30 11:22
閱讀 2019·2019-08-29 17:20
閱讀 1619·2019-08-29 16:56