从全局视角了解 Vue.js 3 的设计思路、工作机制及其重要的组成部分
声明式地描述 UI
如何设计一个声明式的UI框架
编写前端页面
- 具体页面内容
-
- DOM 元素:例如是 div 标签还是 a 标签
- 属性:如 a 标签的 href 属性,再如 id、class 等通用属性
- 事件:如 click、keydown 等
- 元素的层级结构:DOM 树的层级结构,既有子节点,又有父节点
- 声明式的描述页面的内容
-
- 模板式声明
- 使用与 HTML 标签一致的方式来描述 DOM 元素
-
-
- 例如描述一个div 标签时可以使用
-
-
- 使用与 HTML 标签一致的方式来描述属性
-
-
- 例如
-
-
- 使用 : 或 v-bind 来描述动态绑定的属性
-
-
- 例如
-
-
- 使用 @ 或 v-on 来描述事件
-
-
- 例如点击事件 <div @click="handler">
-
-
- 使用与 HTML 标签一致的方式来描述层级结构
-
-
- 例如一个具有span 子节点的 div 标签
-
-
- JavaScript 对象描述
-
- 两者差异
-
-
- 使用 JavaScript 对象描述 UI 更加灵活
- 在 Vue.js 组件中手写的渲染函数就是使用虚拟 DOM 来描述 UI 的
-
-
-
- 渲染函数
-
-
-
-
- h 函数的返回值就是一个对象,其作用是让我们编写虚拟DOM 变得更加轻松
- 简单的渲染函数
-
-
初识渲染器
渲染器的作用就是把虚拟 DOM 渲染为真实 DOM
渲染器的作用
渲染器的简单实现
- 虚拟 DOM
-
- tag 用来描述标签名称,所以 tag: 'div' 描述的就是一个
标签 - props 是一个对象,用来描述
标签的属性、事件等内容。可以看到,给 div 绑定一个点击事件 - children 用来描述标签的子节点。在上面的代码中,children是一个字符串值,意思是 div 标签有一个文本子节点:
click me
- tag 用来描述标签名称,所以 tag: 'div' 描述的就是一个
- 简单渲染器
-
- renderer 函数接收如下两个参数
-
-
- vnode:虚拟 DOM 对象
- container:一个真实 DOM 元素,作为挂载点,渲染器会把虚拟 DOM 渲染到该挂载点下
-
- 渲染器 renderer 的实现思路
-
- 创建元素:把 vnode.tag 作为标签名称来创建 DOM 元素
- 为元素添加属性和事件:遍历 vnode.props 对象,如果 key 以on 字符开头,说明它是一个事件,把字符 on 截取掉后再调用toLowerCase 函数将事件名称小写化,最终得到合法的事件名称,例如 onClick 会变成 click,最后调用addEventListener 绑定事件处理函数
- 处理 children:如果 children 是一个数组,就递归地调用renderer 继续渲染,注意,此时我们要把刚刚创建的元素作为挂载点(父节点);如果 children 是字符串,则使用createTextNode 函数创建一个文本节点,并将其添加到新创建的元素内
渲染器数据更新
- 修改vnode
- 需要精确地找到 vnode 对象的变更点并且只更新变更的内容
组件的本质
组件是什么呢?组件和虚拟 DOM 有什么关系?渲染器如何渲染组件?
定义
- 组件就是一组 DOM 元素的封装
组件的渲染
- 定义一个函数来代表组件
-
- 返回值是虚拟DOM
- 让虚拟 DOM 对象中的 tag 属性来存储组件函数
- 修改前面的提到的 renderer 函数
-
-
- mountElement
-
-
-
-
- 与上文中 renderer 函数的内容一致
-
-
-
-
- mountComponent
-
模板的工作原理
模板是如何工作的呢?
编译器的作用
- 将模板编译为渲染函数
-
- 对于编译器来说,模板就是一个普通的字符串
- 会分析该字符串并生成一个功能与之相同的渲染函数
- 无论是使用模板还是直接手写渲染函数,对于一个组件来说,它要渲染的内容最终都是通过渲染函数产生
- 渲染器再把渲染函数返回的虚拟 DOM 渲染为真实 DOM,这就是模板的工作原理,也是 Vue.js 渲染页面的流程
Vue.js 是各个模块组成的有机整体
Vue.js 的各个模块之间是互相关联、互相制约的,共同构成一个有机整体
- 假设有如下模板
- 编译器会把这段代码编译成渲染函数
-
-
- cls 是一个变量,它可能会发生变化
- 渲染器的作用之一就是寻找并且只更新变化的内容,所以当变量 cls 的值发生变化时,渲染器会自行寻找变更点
- 编译器有能力分析动态内容,并在编译阶段把这些信息提取出来,然后直接交给渲染器
- 编译器能识别出哪些是静态属性,哪些是动态属性
-
-
-
- 渲染器看到这个标志时就知道:只有 class 属性会发生改变。对于渲染器来说,就相当于省去了寻找变更点的工作量,性能自然就提升了
-