主题
渲染原理
渲染机制
虚拟 DOM
虚拟 DOM 是一种虚拟的,保存在内存中的数据结构,用来代表 UI 的表现,和 真实 DOM 节点保持同步。
Virtual DOM 是由一系列的 VNode 组成的。
js
const vnode = {
type: 'div',
props: {
id: 'hello',
},
children: [
/* 更多 vnode */
],
}
渲染管线
- 编译(Compile):Vue 模板被编译为渲染函数:即用来返回虚拟 DOM 树的函数。这一步骤可以通过构建步骤提前完成,也可以通过使用运行时编译器即时完成。
- 挂载(Mount):运行时渲染器调用渲染函数,遍历返回的虚拟 DOM 树,并基于它创建实际的 DOM 节点。这一步会作为响应式副作用执行,因此它会追踪其中所用到的所有响应式依赖。
- 更新(Patch):当一个依赖发生变化后,副作用会重新运行,这时候会创建一个更新后的虚拟 DOM 树。运行时渲染器遍历这棵新树,将它与旧树进行比较,然后将必要的更新应用到真实 DOM 上去。
渲染函数
提示
h()
Vue 提供了一个 h()
函数用于创建 VNodes:
js
import { h } from 'vue'
const vnode = h(
'div', // type
{ id: 'foo', class: 'bar' }, // props
[
/* children */
],
)
通过 isVNode()
判断是否为 VNode
ts
function isVNode(value: unknown): boolean
JSX / TSX
JSX 是 JavaScript 的一个类似 XML 的扩展,与 React 类似,例如:
jsx
const vnode = <div id={dynamicId}>hello, {userName}</div>
在 JSX 表达式中,使用大括号来嵌入动态值:
jsx
const vnode = <div id={dynamicId}>hello, {userName}</div>
注意:Vue 的 JSX 转换方式与 React 中 JSX 的转换方式不同:
- 可以使用 HTML attributes 比如
class
和for
作为 props - 不需要使用className
或htmlFor
。 - 传递子元素给组件 (比如 slots) 的方式不同。
函数式组件
函数式组件是一种定义自身没有任何状态的组件的方式。它们很像纯函数:接收 props,返回 vnodes。函数式组件在渲染过程中不会创建组件实例 (也就是说,没有 this),也不会触发常规的组件生命周期钩子。
示例:
js
function MyComponent(props, context) {
// ...
}
参数说明:
props:因为函数式组件里没有 this 引用,Vue 会把 props 当作第一个参数传入;
context:它包含三个属性:
attrs
、emit
和slots
。它们分别相当于组件实例的
$attrs
、$emit
和$slots
这几个属性。
大多数常规组件的配置选项在函数式组件中都不可用,除了 props
和 emits
。我们可以给函数式组件添加对应的属性来声明它们:
js
MyComponent.props = ['value']
MyComponent.emits = ['click']
如果这个 props 选项没有被定义,那么被传入函数的 props 对象就会像 attrs 一样会包含所有 attribute。除非指定了 props 选项,否则每个 prop 的名字将不会基于驼峰命名法被一般化处理。
对于有明确 props 的函数式组件,attribute 透传的原理与普通组件基本相同。然而,对于没有明确指定 props 的函数式组件,只有 class、style 和 onXxx 事件监听器将默认从 attrs 中继承。在这两种情况下,可以将 inheritAttrs 设置为 false 来禁用属性继承:
js
MyComponent.inheritAttrs = false
函数式组件可以像普通组件一样被注册和使用。如果你将一个函数作为第一个参数传入 h,它将会被当作一个函数式组件来对待。