使用虚拟 dom 渲染页面 《vue.js 设计与实现》
使用 js 对象描述 ui 更加灵活。假如我们要根据级别不同采用不同的标签。js 对象描述的话,只需要一个变量代表 h 标签即可。
// 当变量改变时,标签也会变化。
let level = 3
const title = {
tag: `h${level}` //h3
}
<h1 v-if="level === 1"></h1>
<h2 v-else-if="level === 2"></h2>
<h3 v-else-if="level === 3"></h3>
<h4 v-else-if="level === 4"></h4>
<h5 v-else-if="level === 5"></h5>
<h6 v-else-if="level === 6"></h6>
这远没有 js 对象灵活。其实就是所谓的虚拟 dom.vuejs 3 除了支持使用模板描述 ui 外,还支持使用虚拟 dom 描述 ui.其实我们在 vue.js 组建中手写的渲染函数就是使用虚拟 dom 来描述 ui 的。
import { h } from 'vue'
export default {
render() {
return h('h1', { onClick: handler })
}
}
渲染器的工作原理其实很简单,归根到底,都是使用一些我们熟悉的 DOM 操作 API 来完成渲染工作。渲染器的精髓都在更新节点的阶段。组件就是一组 dom 元素的封装,这组 dom 元素就是组件要渲染的内容,因此我们可以定义一个函数来代表组件。组件的返回值也是虚拟 dom,它代表组件要渲染的内容。搞清楚啦组件的本质,我们就可以定义用虚拟 dom 来描述组件来。
const MyComponent = function () {
return {
tag: 'div',
props: {
onClick: () => alert('hello')
},
children: 'click me'
}
}
const vnode = {
tag: MyComponent
}
function renderer(vnode, container) {
if (typeof vnode.tag === 'string') {
// vnode描述的是标签元素
mountElement(vnode, container)
} else if (typeof vnode.tag === 'function') {
// vnode描述的是组件
mountComponent(vnode, container)
}
}
function mountElement(vnode, container) {
const el = document.createElement(vnode.tag)
for (const key in vnode.props) {
if (/^on/.test(key)) {
el.addEventListener(key.substr(2).toLowerCase(), vnode.props[key])
}
}
if (typeof vnode.children === 'string') {
el.appendChild(document.createTextNode(vnode.children))
} else if (Array.isArray(vnode.children)) {
vnode.children.forEach((child) => renderer(child, el))
}
container.appendChild(el)
}
无论是手写虚拟 dom(渲染函数)还是使用模板,都属于声明式描述 ui,并且 vue.js 同时支持这两种描述 ui 的方式。编译器和渲染器一样,只是一段程序而已,不过它们的工作内容不同。编译器的作用其实就是将模版编译为渲染函数。
无论使用模版还是直接手写渲染函数,对于一个组件来说,它要渲染的内容最终都是通过渲染函数产生的,然后渲染器再把渲染函数返回的虚拟 dom 渲染为真实 dom,这就是模版的工作原理,也是 vue.js 渲染页面的流程。
编译器能识别出哪些是静态属性,哪些是动态属性,在生成代码的时候完全可以附带这些信息:
render(){
return {
tag:'div',
props:{
id:'foo',
class:cls
},
patchFlag:1 // 假设数字1代表class是动态的
}
}
在生成的虚拟dom对象中多处啦一个patchFlags属性,假设1代表class是动态的,这样渲染器看到这个标志时就知道。省去啦寻找变更点的工作,性能就提升啦。编译器和渲染器之间时存在信息交流的,它们互相配合使得性能进一步提升,而它们之间交流的媒介就是虚拟dom对象。一个虚拟dom对象中回包含多种数据字段,每个字段都代表一定的含义。