声明式地描述ui
vuejs中使用模板来声明式地描述UI, 也支持通过h函数调用返回虚拟dom(组件的render函数)描述ui.
const title = {
tag: 'h1',
props: {
onClick: hanlder,
},
children: [
{ tag: 'span' }
]
}
// template 对应模版 h函数: 辅助创建虚拟dom的工具函数
<h1 @click="handler"><span></span></h1>
// 渲染函数
export default {
render(){
return h('h1', { onClick: hanlder })
}
}
初识渲染器
渲染器将虚拟dom渲染为真实dom, 分为三步:
- 创建元素: 把
vnode.tag作为标签名称来创建dom元素 - 添加属性和事件: 遍历
vnode.props对象,onXxx代表事件, 调用addEventListener绑定事件处理函数 - 处理children: 如果是数据, 则递归调用
renderer继续渲染; 如果是字符串则创建一个文本节点添加到元素内
更新节点时应该只更新有变化的地方, 而不需要重新走一遍创建流程. 后文会深入了解, 总归就是通过一些dom操作来完成渲染工作.
function renderer(vnode, container){
if(typeof vnode.tag === 'string'){
// vnode描述的是标签元素
mountElement(vnode, container)
}
// else if(typeof vnode.tag === 'function'){
// }
}
组件的本质
虚拟dom是用来描述真实ui的js对象, 而组件就是一组dom元素的封装
// 定义一个函数来代表组件, 而返回值就是组件要渲染的内容.
const MyComponent = function(){
return {
tag: 'div',
props: {
onClick(){ ... },
},
children: 'click me'
}
}
const vnode = {
tag: MyComponent
}
// 对 renderer 进行补充
function renderer(vnode, container){
if(typeof vnode.tag === 'string'){
// vnode描述的是标签元素
mountElement(vnode, container)
}
else if(typeof vnode.tag === 'function'){
// vnode描述的是组件
mountComponent(vnode, container)
}
}
function mountComponent(vnode, container){
// 获取组件要渲染的内容
const subtree = vnode.tag()
// 递归地调用 renderer 渲染 subtree
renderer(subtree, container)
}
// 组件也可以是对象而不是函数, 通过对象的render方法同样知道组件要渲染的内容(虚拟dom)
模板的工作原理
渲染器是将虚拟dom渲染成真实dom, 而编译器是将模版编译为渲染函数. 对于编译器来说,模板就是一个字符串, 它会分析该字符串并生成一个对应的渲染函数.
以熟悉的vue文件为例, 一个 .vue 文件就是一个组件.
<template>
<div @click="hanlder">click me</div>
</template>
<script>
export default {
data(){ ... },
methods: {
hanlder(){ ... }
},
// render(){ ... 编译器会把模板内容编译成渲染函数, 并添加到组件对象上. }
}
</script>
vuejs是各个模块组成的有机整体
组件的实现依赖渲染器, 模板的编译依赖编译器, 并且编译后的代码是根据渲染器和虚拟dom的设计决定的.
// 模版
<div id="foo" :class="cls" />
// 编译器将上述模版编译成渲染函数
render(){
// return h('div', { id: 'foo', class: cls })
return {
tag: 'div',
props: {
id: 'foo',
class: cls
},
patchFlags: 1 // 假设 1 代表class是动态的
}
}
cls是一个变量. 渲染器需要找到变化的内容进行更新, 经过编译器编译, 生成的虚拟dom中多出了一个patchFlags属性, 渲染器看到这个属性就可以快速定位哪些内容需要更新. 虚拟dom对象中包含多种数据字段, 每个字段都有一定的含义.
总结
模板会被编译器的程序编译为渲染函数, 虚拟dom对象会被渲染器渲染为真实dom元素. 他们是共同构成一个有机的整体, 相互配合进而提升性能.