总结
- Vue.js 是声明式的宽街,直接描述结果,用户不用关注过程。
- 虚拟DOM 比模板更加灵活,模板比 虚拟DOM 更加直观。
- 渲染器 Render 将 虚拟DOM 渲染为 真实DOM。本质是用我们熟悉的DOM操作API来创建、更新元素。渲染器的精髓是在更新节点阶段,用 Diff 算法找出变更点,只更新变更内容。
- 编译器 Compiler 将HTML描述文本 编译为 虚拟DOM。与渲染器之间通过 虚拟DOM 进行信息交流,相互配合提高性能。
- 组件的本质是一组 DOM元素 的封装,表达形式可以很多种,只要渲染器配合即可。本质都是记录了渲染的内容,返回 虚拟DOM 或 render 函数
1. 声明式的描述UI
Vue.js3 是一个声明式的UI框架。
1.1 声明式描述UI的关键元素
前端页面涉及:
- DOM元素
- 属性
- 事件
- 层级结构
框架的设计者需要思考如何用 模板 来描述上述的内容,这便是声明式的描述UI。
1.2 声明式描述UI的方式
声明式描述UI有两种:
- 模板(HTML描述文本)
- js对象描述
js对象描述更加灵活,也称为 虚拟DOM。(虚拟DOM是用js对象来描述真实的DOM结构)
在Vue中,两者都有采用,只是最终模板都转成 虚拟DOM。
在 Vue 的组件中,手写的渲染函数就是使用 虚拟DOM 来描述UI的。
js
import { h } from 'vue'
export default {
render() {
return h('h1', { onClick: handler }) // 虚拟DOM
}
}
h函数 是一个辅助创建 虚拟DOM 的工具函数。
2. 初识渲染器
渲染器的目的:将 虚拟DOM 渲染为 真实的DOM。
渲染器实际上是用我们熟悉的DOM操作API来创建、更新元素,完成渲染工作。
渲染器的精髓是在更新节点阶段,这个放到后面章节讲。目前只要掌握上面两个要点。
3. 组件的本质
组件就是一组 DOM元素 的封装。
或者说组件可以用一个函数表示,组件函数的返回值是一个 虚拟DOM,这个 虚拟DOM 就是组件想要渲染的内容。
组件也可以是一个对象,对象内包含 render函数 作为函数想要渲染的内容,返回 虚拟DOM。
总而言之,组件的表达形式可以很多种,只要渲染器配合即可,本质都是记录了渲染的内容,返回 虚拟DOM。
在 虚拟DOM 中,可以将组件函数放在 tag属性 中,渲染器根据 tag属性 的类型,运行 创造元素的函数 或者 递归调用render渲染器。
js
// 组件
const MyConponent = function () {
return {
tab: 'div',
props: {
onClick: () => {alert('hello')}
},
children: 'click me'
}
}
// 虚拟DOM 的 tag 存储组件
const vnode = {
tag: MyConponent
}
// render渲染器
function render(vnode, container) {
if (typeof vnode.tag === 'string') {
// 渲染标签元素 这里就不写它的声明了,本质就是DOM创造元素,挂载事件,render函数递归调用其children。
mountElement(vnode, container)
} else if (typeof vnode.tag === 'function') {
// 渲染组件
mountComponent(vnode, container)
}
}
// 渲染组件
function mountComponent(vnode, container) {
// 调用组件函数,获取组件 虚拟DOM
const subTree = vnode.tag()
// 递归调用 render渲染器
renderer(subTree, container)
}
4. 模板的工作原理
模板是HTML描述文本,只是声明式描述UI其中的一种形式。可以通过 编译器 将模板编译为渲染函数 render。
vue
<template>
<div @click="handler">
click me
</div>
</template>
// 经过编译器,最终变成组件对象中的 render 函数。
export default {
data() {/** codes */},
methods: {
handler() {/** codes */}
},
render() {
return h('div', { onClick: handler }, 'click me')
}
}
5. Vue.js 是各个模块组成的有机整体
- 编译器 Compiler: 将HTML描述文本 编译为 虚拟DOM
- 渲染器 Render: 将 虚拟DOM 渲染为 真实DOM
渲染器其中一个作用就是寻找并只更新变化的内容,对于渲染器而言找到“更新内容”很费劲,但对于编译器这却很容易。
为了减少渲染器的工作量,提高性能,编译器和渲染器之间通过 虚拟DOM 进行信息交流,在 虚拟DOM 上添加多种数据字段代表相关含义,这便可以使得性能进一步提升。
vue
<template>
<!-- 由于约定的写法不同,编译器可以知道id是常量,而class是变量 -->
<div id="foo" :class="cls"></div>
</template>
export default {
render() {
return h('div', { id: 'foo', class: cls })
},
// 假设编译器编译出来的虚拟DOM,通过patchFlags数据字段为1来表示class是变量,这样就节省了渲染器寻找变更点的工作量了。
patchFlags: 1
}