Vue.js设计与实现学习总结(第三章) 组件渲染原理虚拟DOM

文章介绍了虚拟DOM的概念,通过JavaScript对象描述DOM结构,并详细解释了一个简单的渲染器如何将虚拟DOM转换为真实DOM,包括处理属性、事件和子节点。同时,文章提到了组件的概念,组件是DOM元素的封装,可以通过函数返回虚拟DOM来描述。在渲染过程中,渲染器需要判断tag是标签还是组件函数并分别进行处理。

渲染器


虚拟DOM: 使用Javascript对象描述真实的 DOM 结构. 例如:

const vNode = {

// 标签名tag: 'div',

// 标签属性prop: {

onClick () {

alert('点击')

}

},

// 子节点children: [

{

tag: 'span',

children: 'hello world'

}

]

}

使用渲染器将虚拟 DOM 变成真实 DOM 并渲染到浏览器页面中, 并寻找并且只更新变化的内容, 如图1:

因此需要编写一个渲染器, 将上面的虚拟 DOM 渲染为真实 DOM:

functionRender(vNode, root) {

// 使用 vNode.tag 作为标签名创建 DOM 对象const el = document.createElement(vNode.tag)

// 遍历 vNode.props 将属性, 事件添加到 DOM 对象for (const key in vNode.props) {

if (/^on/.test(key)) {

// 如果 key 以 on 卡头, 说明它是个事件, 注册相应事件

el.addEventListener(key.substr(2).toLocaleLowerCase(), vNode.props[key])

}

}

// 处理 childrenif (typeof vNode.children == 'string') {

// 为字符串说明它是元素的文本子节点let text = document.createTextNode(vNode.children)

el.appendChild(text)

} elseif (Array.isArray(vNode.children)) {

// 递归调用 Render 函数渲染子节点, 使用当前元素 el 作为挂载点for (let item of vNode.children) {

Render(item, el)

}

}

// 元素添加到挂载点下

root.appendChild(el)

}

渲染器 Render 实现思路, 总体为三步:

  1. 创建元素: 把 vNode.tag 作为标签名创建 DOM 元素

  1. 为元素添加属性和事件: 遍历 vNode.props 对象, 如果 key 以 on 字符开头, 说明它是一个事件, 把字符 on 截取后再调用 addEventListener 绑定事件处理函数

  1. 处理 children: 如果 children 是一个数组, 就用递归调动 Render 继续渲染, 注意此时要把刚刚创建的元素作为挂载点(父节点); 如果 children 是字符串, 则使用 createTextNode 函数创建一个文本节点, 并将其添加到新创建的元素内

组件

组件本质就是一组 DOM 元素的封装, 可以定义一个函数代表组件(也可以使用对象, 该对象添加一个函数返回值为虚拟 DOM 即可), 函数的返回值是组件要渲染的内容

constMyComponent = function () {

return {

tag: 'div',

props: {

onClick: () =>alert('组件')

},

children: 'click me'

}

}

因此就可以使用虚拟 DOM 描述组件:

const vNode = {

tag: MyComponent

}

因此在渲染器渲染时就需要判断 tag 的类型:

functionRender (vNode, container) {

if (typeof vNode.tag === 'string') {

// 说明 vNode 描述的是标签元素mountElement(vNode, container)

} elseif (typeof vNode.tag === 'function') {

// 说明 vNode 描述的是组件mountComponent(vNode, container)

}

}

若为字符串就可以使用 mountElement 函数进行渲染, 内容与上文的 Render 函数一样

functionmountElement(vNode, root) {

// 使用 vNode.tag 作为标签名创建 DOM 对象const el = document.createElement(vNode.tag)

// 遍历 vNode.props 将属性, 事件添加到 DOM 对象for (const key in vNode.props) {

if (/^on/.test(key)) {

// 如果 key 以 on 卡头, 说明它是个事件, 注册相应事件

el.addEventListener(key.substr(2).toLocaleLowerCase(), vNode.props[key])

}

}

// 处理 childrenif (typeof vNode.children == 'string') {

// 为字符串说明它是元素的文本子节点let text = document.createTextNode(vNode.children)

el.appendChild(text)

} elseif (Array.isArray(vNode.children)) {

// 递归调用 Render 函数渲染子节点, 使用当前元素 el 作为挂载点for (let item of vNode.children) {

// 进行递归时, 依然需要判断 tag 的类型, 因此是调用 RenderRender(item, el)

}

}

// 元素添加到挂载点下

root.appendChild(el)

}

若为函数则使用 mountComponent 函数渲染:

functionmountComponent (vNode, container) {

// 调用组件函数, 获取组件要渲染的内容(虚拟DOM)const subtree = vNode.tag()

// 递归调用 Render 渲染 subtrssRender(subtree, container)

}

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值