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

Vue.js設計與實現之十二-渲染器的核心功能:掛載與更新01

開發 前端
在本文中主要講述了如何實現虛擬DOM節點轉成真實DOM樹上,最終掛載到掛載點上。討論了虛擬節點是如何掛載到DOM樹,又是如何從DOM樹上卸載的,對于屬性又是如何在元素上進行設置的。

1、寫在前面

在本文中主要講述了如何實現虛擬DOM節點轉成真實DOM樹上,最終掛載到掛載點上。討論了虛擬節點是如何掛載到DOM樹,又是如何從DOM樹上卸載的,對于屬性又是如何在元素上進行設置的。

2、掛載子節點和元素的屬性

在上篇文章中,在vnode.children值為字符串時,將其設置為元素的文本內容,當vnode.children值為數組時,表示其有子節點需要遍歷設置。我們知道vnode是一個虛擬DOM節點,vnode.children是一個虛擬DOM樹,其每個元素都是一個虛擬DOM節點。

const vnode = {
type:"div",
//props是標簽的屬性
props:{
id:"super"
},
children:[
{
type:"p",
children:"pingping"
}
]
}

上面的代碼是虛擬DOM樹形結構,如果要實現將虛擬VNode轉為真實DOM,需要通過掛載和渲染??梢酝ㄟ^mountElement函數實現節點的渲染:

function mountElement(vnode, container){
const el = createElement(vnode.type);
//處理children
if(typeof vnode.children === "string"){
// 字符串轉為標簽的文本內容
setElementText(el, vnode.children)
}else if(Array.isArray(vnode.children)){
//虛擬DOM節點數組 需要遍歷每個節點進行掛載
vnode.children.forEach(child=>{
patch(null,child,el);
})
}
// 在標簽上添加屬性
if(vnode.props){
for(const key in vnode.props){
// 調用setAttribute在元素上設置屬性
el.setAttribute(key, vnode.props[key]);
// 也可以使用DOM對象直接設置屬性
// el[key] = vnode.props[key];
}
}
insert(el, container);
}

在上面代碼片段中,首先會根據vnode.type的值創建DOM節點,children的值判斷為string類型時,直接將其設置為元素的文本內容;children的值判斷數組時,則對數組內的虛擬DOM節點進行遍歷,調用patch函數掛載節點。

在掛載階段patch是沒有舊vnode的,對此傳遞的第一個參數是null,而在patch函數執行時會遞歸調用mountElement函數完成掛載。而patch傳遞的第三個參數是虛擬節點要掛載的根節點,完成掛載后需要給元素遍歷設置屬性。

3、正確地設置元素屬性

如何正確地設置元素屬性,就得先了解HTML Attribute和DOM Properties的差異和關聯。我們知道HTML Attribute是定義在HTML標簽元素上的屬性,瀏覽器會將其解析創建一個對應的DOM對象,這個對象上包含許多屬性(DOM Properties)。

HTML Attribute的作用是設置與之對應的DOM Properties初始值,當值發生改變時,DOM Properties始終存儲著當前值,那么通過getAttribute獲取到的也是初始值。

HTML Attribute和DOM Properties。然而在使用Vue.js的單文件模板不會被瀏覽器解析,此時需要框架自己進行解析。會影響DOM屬性的添加方式,瀏覽器會解析普通HTML元素代碼,自定分析HTML Attribute設置合適的DOM Properties。然而在使用Vue.js的單文件模板不會被瀏覽器解析,此時需要框架自己進行解析。

那么,看看在Vue.js是如何實現的:

const renderer = createRenderer({
//創建元素
createElement(tag){
return document.createElement(tag);
},
//設置元素的文本節點
setElementText(el, text){
el.textContent = text;
},
//用于給指定父節點添加指定元素
insert(el, parent, achor = null){
parent.insertBefore(el, anchor);
}
// 將屬性設置相關操作進行封裝,作為渲染器選項進行傳值
patchProps(el, key, prevValue, nextValue){
if(shouldSetAsProps(el, key, nextValue)){
const type = typeof el[key];
if(key === "class"){
//采用el.className方式設置clas,是因為其性能相對于setAttribute和el.classList更高
el.className = nextValue || "";
}else if(type === "boolean" && nextValue === ""){
el[key] = true;
}else{
el[key] = nextValue;
}
}else{
el.setAttribute(key, nextValue);
}
}
})

在上面代碼中,shouldSetAsProps函數用于分析屬性是否應該作為DOM Properties屬性被設置,返回的是一個布爾值。當返回一個true值時,表示應該作為DOM Properties被設置,否則就應該使用setAttribute函數設置屬性。在設置屬性時,需要優先設置元素的DOM Properties,當其值為空字符串時,需要將其矯正為true。

在mountElement函數中,只需要調用patchProps函數傳遞參數即可:

function mountElement(vnode, container){
const el = createElement(vnode.type);
//處理children
if(typeof vnode.children === "string"){
// 字符串轉為標簽的文本內容
setElementText(el, vnode.children)
}else if(Array.isArray(vnode.children)){
//虛擬DOM節點數組 需要遍歷每個節點進行掛載
vnode.children.forEach(child=>{
patch(null,child,el);
})
}

// 在標簽上添加屬性
if(vnode.props){
for(const key in vnode.props){
patchProps(el, key, null, vnode.props[key])
}
}
insert(el, container);
}

在上面代碼片段中,mountElement函數會檢查每個vnode.props中的屬性,調用patchProps函數去設置DOM Properties。

4、卸載操作

在前面兩節中,討論了如何將虛擬DOM掛載到掛載點上,是通過createRenderer函數結合mountElement實現的。而卸載操作發生在更新階段,即初次掛載完成之后,后續渲染觸發的更新。

//初次掛載
renderer.render(vnode, document.querySelector("#app"));
// 再次掛載新vnode,觸發更新 當傳遞的是null,則進行卸載之前的操作
renderer.render(vnode, document.querySelector("#app"));
//renderer.render(null, document.querySelector("#app"));

在初次渲染完畢后,后續渲染時如果傳遞的是null作為新vnode,則表示需要卸載當前所有渲染的內容。

在上一篇文章中,使用innerHTML設置為空作為清空容器元素內容的方案是存在缺陷的,因為它不會移除綁定在DOM元素上的事件處理函數。對此,需要先根據vnode對象獲取到與之關聯的真實DOM元素,使用原生DOM操作方法將其進行移除。

function unmount(vnode){
const parent = vnode.el.parentNode;
if(parent){
parent.removeChild(vnode.el);
}
}

unmount函數接收一個虛擬節點作為參數,并將該節點對應的真實DOM元素從父元素上移除。

注意:在新舊vnode描述內容不同時,即vnode.type的屬性不同時,兩個vnode之間就不存在打補丁的意義,此時應該使用unmount函數先將舊元素進行卸載,再將n1的值重置為null,最后將新元素進行掛載到容器中。

當然,即使新舊vnode描述內容相同,也要判斷兩者的類型是否相同,vnode可以描述普通標簽也可以描述組件,對于不同類型的vnode需要使用不同的掛載或打補丁方式。

function patch(n1, n2, container){
if(n1 && n2.type !== n1.type){
unmount(n1);
n1 = null;
}
const { type } = n2;
if(typeof type === "string"){
if(!n1){
mountElement(n2, container);
}else{
patchElement(n1, n2);
}
}else if(typeof type === "object"){
// n2.type是object對象類型,則描述的是組件
}else if(type === "xxx"){
//處理其他類型vnode
}
}

5、寫在最后

掛載子節點只需要遞歸調用patch函數即可實現掛載,而節點屬性的設置就取決于被設置屬性的特點。在卸載操作時,通過直接使用innerHTML清空容器元素是存在諸多問題的,對此封裝了一個新的卸載函數unmount。

責任編輯:姜華 來源: 前端一碼平川
相關推薦

2022-04-20 09:07:04

Vue.js的事件處理

2022-04-18 08:09:44

渲染器DOM掛載Vue.js

2022-04-01 08:08:27

Vue.js框架命令式

2022-04-04 16:53:56

Vue.js設計框架

2022-04-25 07:36:21

組件數據函數

2022-04-12 08:08:57

watch函數options封裝

2022-04-03 15:44:55

Vue.js框架設計設計與實現

2022-04-11 08:03:30

Vue.jscomputed計算屬性

2022-05-03 21:18:38

Vue.js組件KeepAlive

2022-04-14 09:35:03

Vue.js設計Reflect

2022-04-05 16:44:59

系統Vue.js響應式

2022-04-26 05:55:06

Vue.js異步組件

2022-04-17 09:18:11

響應式數據Vue.js

2022-04-09 17:53:56

Vue.js分支切換嵌套的effect

2022-04-12 08:09:22

Nodejs前端面試題

2022-04-16 13:59:34

Vue.jsJavascript

2019-04-01 19:38:28

Vue.jsJavascript前端

2010-08-13 11:02:27

Flex渲染器

2009-07-15 13:48:26

Swing模型和渲染器

2016-11-01 19:10:33

vue.js前端前端框架
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品一区二区av | 成人免费精品视频 | 国产精品99久久久久久www | 欧美日韩精品一区二区三区四区 | 久久一级| 久久精品国产一区二区电影 | 日韩欧美一区二区三区免费观看 | 福利色导航 | 国产区精品在线观看 | 日本在线免费视频 | 色成人免费网站 | 日韩欧美一区在线 | 国产精品久久视频 | 亚洲成人网在线 | 国产亚洲一区二区三区在线观看 | 人人玩人人干 | 国产99久久久国产精品 | 成人在线观看免费视频 | 欧产日产国产精品国产 | 免费av观看 | 成人精品一区二区三区中文字幕 | 国产精品美女久久久 | 久久一区二区免费视频 | 亚洲三区视频 | 欧美区日韩区 | 亚洲午夜精品久久久久久app | 亚洲日本激情 | 国产欧美一区二区三区日本久久久 | 国产精品亚洲欧美日韩一区在线 | 国产欧美一级二级三级在线视频 | 国内自拍视频在线观看 | 久久精品欧美一区二区三区麻豆 | 青青久在线视频 | 欧美日韩在线成人 | 99久久精品免费看国产四区 | 精品国产一区二区三区日日嗨 | 国产精品美女 | 在线观看国产视频 | 久久精品 | 超碰国产在线 | 精品国产一区二区在线 |