成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

手寫簡易前端框架:Vdom 渲染和 jsx 編譯

開發(fā) 前端
本文我們實現(xiàn)了 vdom 的渲染。vdom 是描述界面的對象,它的渲染就是通過 createElement、createTextNode 等 api 來遞歸創(chuàng)建和組裝元素、文本等 dom 的過程。

作為前端工程師,前端框架幾乎每天都要用到,需要好好掌握,而對某項技術(shù)的掌握程度可以根據(jù)是否能實現(xiàn)一個來判斷。手寫一個前端框架對更好的掌握它是很有幫助的事情。

現(xiàn)代前端框架經(jīng)過多年的迭代都已經(jīng)變得很復(fù)雜,理清它們的實現(xiàn)原理變得困難重重。所以我想寫一個最簡單版本的前端框架來幫助大家理清思路。

一個完整的前端框架涉及到的內(nèi)容還是比較多的,我們一步步的來,這篇文章來實現(xiàn)下 vdom 的渲染。

vdom 的渲染

vdom 全稱 virtual dom,用來聲明式的描述頁面,現(xiàn)代前端框架很多都基于 vdom。前端框架負責(zé)把 vdom 轉(zhuǎn)為對真實 dom 的增刪改,也就是 vdom 的渲染。

那么 vdom 是什么樣的?又是怎么渲染的呢?

dom 主要是元素、屬性、文本,vdom 也是一樣,其中元素是 {type、props、children} 的結(jié)構(gòu),文本就是字符串、數(shù)字。

比如這樣一段 vdom:

{
type: 'ul',
props: {
className: 'list'
},
children: [
{
type: 'li',
props: {
className: 'item',
style: {
background: 'blue',
color: '#fff'
},
onClick: function() {
alert(1);
}
},
children: [
'aaaa'
]
},
{
type: 'li',
props: {
className: 'item'
},
children: [
'bbbbddd'
]
},
{
type: 'li',
props: {
className: 'item'
},
children: [
'cccc'
]
}
]
}

不難看出,它描述的是一個 ul 的元素、它有三個 li 子元素,其中第一個子元素有 style 的樣式、還有 onClick 的事件。

前端框架就是通過這樣的對象結(jié)構(gòu)來描述界面的,然后把它渲染到 dom。

這樣的對象結(jié)構(gòu)怎么渲染呢?

明顯要用遞歸,對不同的類型做不同的處理。

  • 如果是文本類型,那么就要用 document.createTextNode 來創(chuàng)建文本節(jié)點。
  • 如果是元素類型,那么就要用 document.createElement來創(chuàng)建元素節(jié)點,元素節(jié)點還有屬性要處理,并且要遞歸的渲染子節(jié)點。

所以,vdom 的 render 邏輯就是這樣的:

if (isTextVdom(vdom)) {
return mount(document.createTextNode(vdom));
} else if (isElementVdom(vdom)) {
const dom = mount(document.createElement(vdom.type));
for (const child of vdom.children) {
render(child, dom);
}
for (const prop in vdom.props) {
setAttribute(dom, prop, vdom.props[prop]);
}
return dom;
}

文本的判斷就是字符串和數(shù)字:

function isTextVdom(vdom) {
return typeof vdom == 'string' || typeof vdom == 'number';
}

元素的判斷就是對象,并且 type 為標(biāo)簽名的字符串:

function isElementVdom(vdom) {
return typeof vdom == 'object' && typeof vdom.type == 'string';
}

元素創(chuàng)建出來之后如果有父節(jié)點要掛載到父節(jié)點,組裝成 dom 樹:

const mount = parent ? (el => parent.appendChild(el)) : (el => el);

所以,完整的 render 函數(shù)就是這樣的:

const render = (vdom, parent = null) => {
const mount = parent ? (el => parent.appendChild(el)) : (el => el);
if (isTextVdom(vdom)) {
return mount(document.createTextNode(vdom));
} else if (isElementVdom(vdom)) {
const dom = mount(document.createElement(vdom.type));
for (const child of vdom.children) {
render(child, dom);
}
for (const prop in vdom.props) {
setAttribute(dom, prop, vdom.props[prop]);
}
return dom;
}
};

其中,元素的 dom 還要設(shè)置屬性,比如上面 vdom 里有 style 和 onClick 的屬性要設(shè)置。

style 屬性是樣式,支持對象,要把對象合并之后設(shè)置到 style,而 onClick 屬性是事件監(jiān)聽器,用 addEventListener 設(shè)置,其余的屬性都用 setAttribute 來設(shè)置。

const setAttribute = (dom, key, value) => {
if (typeof value == 'function' && key.startsWith('on')) {
const eventType = key.slice(2).toLowerCase();
dom.addEventListener(eventType, value);
} else if (key == 'style' && typeof value == 'object') {
Object.assign(dom.style, value);
} else if (typeof value != 'object' && typeof value != 'function') {
dom.setAttribute(key, value);
}
}

就這樣,vdom 的渲染邏輯就完成了。

用上面那段 vdom 渲染試下效果:

render(vdom, document.getElementById('root'));

vdom 的渲染成功!

小結(jié)一下:

「vdom 會遞歸的進行渲染,根據(jù)類型的不同,元素、文本會分別用 createTextNode、createElement 來遞歸創(chuàng)建 dom 并組裝到一起,其中元素還要設(shè)置屬性,style、事件監(jiān)聽器和其他屬性分別用 addEventListener、setAttribute 等 api 進行設(shè)置。」

「通過不同的 api 創(chuàng)建 dom 和設(shè)置屬性,這就是 vdom 的渲染流程。」

但是,vdom 寫起來也太麻煩了,沒人會直接寫 vdom,一般是通過更友好的 DSL(領(lǐng)域特定語言) 來寫,然后編譯成 vdom,比如 jsx 和 template。

這里我們使用 jsx 的方式,因為可以直接用 babel 編譯。

jsx 編譯成 vdom

上面的 vdom 改為 jsx 來寫就是這樣的:

const jsx = <ul className="list">
<li className="item" style={{ background: 'blue', color: 'pink' }} onClick={() => alert(2)}>aaa</li>
<li className="item">bbbb</li>
<li className="item">cccc</li>
</ul>

render(jsx, document.getElementById('root'));

明顯比直接寫 vdom 緊湊了不少,但是需要做一次編譯。

配置下 babel 來編譯 jsx:

module.exports = {
presets: [
[
'@babel/preset-react',
{
pragma: 'createElement'
}
]
]
}

編譯產(chǎn)物是這樣的:

const jsx = createElement("ul", {
className: "list"
}, createElement("li", {
className: "item",
style: {
background: 'blue',
color: 'pink'
},
onClick: () => alert(2)
}, "aaa"), createElement("li", {
className: "item"
}, "bbbb"), createElement("li", {
className: "item"
}, "cccc"));
render(jsx, document.getElementById('root'));

為啥不直接是 vdom,而是一些函數(shù)呢?

因為這樣會有一次執(zhí)行的過程,可以放入一些動態(tài)邏輯,

比如從 data 取值:

const data = {
item1: 'bbb',
item2: 'ddd'
}
const jsx = <ul className="list">
<li className="item" style={{ background: 'blue', color: 'pink' }} onClick={() => alert(2)}>aaa</li>
<li className="item">{data.item1}</li>
<li className="item">{data.item2}</li>
</ul>

會編譯成:

const data = {
item1: 'bbb',
item2: 'ddd'
};
const jsx = createElement("ul", {
className: "list"
}, createElement("li", {
className: "item",
style: {
background: 'blue',
color: 'pink'
},
onClick: () => alert(2)
}, "aaa"), createElement("li", {
className: "item"
}, data.item1), createElement("li", {
className: "item"
}, data.item2));

這叫做 render function,它執(zhí)行的返回值就是 vdom。

這個 render function 名字之所以是 createElement,是因為我們上面 babel 配置里指定了 pragma 為 createElement。

render function 就是生成 vdom 的,所以實現(xiàn)很簡單:

const createElement = (type, props, ...children) => {
return {
type,
props,
children
};
};

我們來測試下改為 jsx 之后的渲染:

渲染成功!

我們在 vdom 的基礎(chǔ)上更進了一步,通過 jsx 來寫一些動態(tài)邏輯,然后編譯成 render function,執(zhí)行之后產(chǎn)生 vdom。

這樣比直接寫 vdom 更簡單,可以做更靈活的 vdom 生成邏輯。

代碼上傳到了 github:https://github.com/QuarkGluonPlasma/frontend-framework-exercize

總結(jié)

手寫前端框架是更好的掌握它的最直接的方式,我們會逐步實現(xiàn)一個功能完整的前端框架。

本文我們實現(xiàn)了 vdom 的渲染。vdom 是描述界面的對象,它的渲染就是通過 createElement、createTextNode 等 api 來遞歸創(chuàng)建和組裝元素、文本等 dom 的過程,其中元素節(jié)點還需要設(shè)置屬性,style、event listener 等屬性會用不同的 api 設(shè)置。

雖然最終是 vdom 的渲染,但是開發(fā)時不會直接寫 vdom,而是通過 jsx 來描述頁面,然后編譯成 render function,執(zhí)行后產(chǎn)生 vdom。這樣寫起來更簡潔,而且支持動態(tài)邏輯。(jsx 的編譯使用 babel,可以指定 render function 的名字)

vdom 渲染和 jsx 是前端框架的基礎(chǔ),其他的功能比如組件是在這個基礎(chǔ)之上實現(xiàn)的,下篇文章我們就來實現(xiàn)組件的渲染。

責(zé)任編輯:姜華 來源: 神光的編程秘籍
相關(guān)推薦

2022-01-25 18:11:55

vdomclassfunction

2022-01-24 13:46:24

框架

2022-08-14 23:04:54

React前端框架

2022-07-06 08:30:36

vuereactvdom

2021-06-07 00:15:26

瀏覽器HtmlParser

2021-06-04 05:16:33

瀏覽器js源碼

2024-02-02 08:33:00

Vue模板性能

2017-04-12 11:46:46

前端瀏覽器渲染機制

2022-05-11 15:08:52

驅(qū)動開發(fā)系統(tǒng)移植

2010-12-29 09:51:29

前端基礎(chǔ)框架

2022-02-11 13:44:56

fiber架構(gòu)React

2016-12-08 10:57:08

渲染引擎前端優(yōu)化

2021-07-04 10:07:04

Virtual DO閱讀源碼虛擬DOM

2022-01-10 11:04:41

單鏈表面試編程

2019-08-01 15:19:26

前端開發(fā)技術(shù)

2024-07-01 00:00:03

2015-07-14 10:11:48

前端框架語言

2020-12-10 06:01:20

前端Compose方法

2023-02-27 09:38:36

Springbootstarter

2023-01-03 09:35:34

SpringbootStarter
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 国产日韩一区二区三区 | 嫩草视频网 | 最新国产精品精品视频 | h视频免费观看 | 免费国产成人av | 国产成人综合网 | 成人免费大片黄在线播放 | 日韩欧美二区 | 天天噜天天干 | 中文字幕一区二区三区乱码图片 | 天天精品综合 | 99re6在线视频精品免费 | 日韩欧美一级精品久久 | 久久久久久免费毛片精品 | 中文字幕av在线一二三区 | 国产日韩久久 | 精品动漫一区 | 精国产品一区二区三区四季综 | 久久精品免费观看 | 久久久青草婷婷精品综合日韩 | 夜久久 | 亚洲国产二区 | 亚洲三区在线播放 | 91av亚洲| 中文字幕在线一区二区三区 | 天堂中文在线播放 | 久久国产精品免费一区二区三区 | 久夜精品| 久久大| 久久久久一区 | 日韩精品av一区二区三区 | 国产色网站 | 玖玖国产 | 青青草免费在线视频 | 在线看免费的a | 91在线一区 | 成人在线视频网址 | 永久av| 国产成人免费在线观看 | 精品国产乱码一区二区三区 | 91亚洲精品在线 |