Appearance
概述
命令式和声明式
命令式框架关注过程。
js
$('#app') // 获取 div
.text('hello world') // 设置文本内容
.on('click', () => alert('ok')) // 绑定点击事件
声明式框架更加关注结果。
vue
<div @click="() => alert('ok')">hello world</div>
注意
声明式代码的性能不优于命令式代码的性能。毕竟框架本身就是封装了命令式代码才实现了面向用户的声明式。对于框架来说,为了实现最优的更新性能,它需要找到前后的差异并只更新变化的地方。即便前后结果一致,也会执行比对函数。
声明式代码的更新性能消耗 = 找出差异的性能消耗 + 直接修改的性能消耗。
虽然声明式代码的性能不优于命令式代码的性能,甚至会降低性能,但声明式代码的可维护性更强。
运行时和编译时
编译时:提供一个 Compiler 函数,将 HTML 字符串编译为树型结构的数据对象。 运行时:供一个 Render 函数,将树型结构的数据对象递归地渲染成 DOM 元素。 纯编译时:提供一个 Compiler 函数,将 HTML 字符串编译为命令式代码。 运行时编译:代码运行的时候才开始编译。
Vue 就是运行时 + 编译时的架构,打包时编译,做一些优化,上线后只需要渲染器。
MVC
MVC
通过分离 Model
、View
和 Controller
的方式来组织代码结构。
View
负责页面的显示逻辑;Model
负责数据增删改查;Controller
负责业务处理;
当用户与页面产生交互时,触发 Controller
控制器,通过调用 Model
层,然后 Model 层再去通知 View
层更新。
MVVM
MVVM
分为 Model
、View
、ViewModel
:
Model
代表数据模型,数据和业务逻辑都在Model
层中定义;View
代表 UI 视图,负责数据的展示;ViewModel
负责监听Model
中数据的改变并且控制视图的更新,处理用户交互操作;
Model
和 View
并无直接关联,而是通过 ViewModel
来进行联系的,实现数据双向数据绑定。因此当 Model
中的数据改变时会触发 View
层的刷新,View
中由于用户交互操作而改变的数据也会在 Model
中同步。
编译器 Compiler
编译器的作用其实就是将模板编译为渲染函数。
html
<div @click="handler">click me</div>
对于编译器来说,模板就是一个普通的字符串,它会分析该字符串并生成一个功能与之相同的渲染函数:
js
function render() {
return h('div', { onClick: handler }, 'click me')
}
一个 .vue 文件 <template>
标签里的内容就是模板内容,编译器会把模板内容编译成渲染函数并添加到 <script>
标签块的组件对象上 render 函数。
渲染器 Renderer
渲染器的作用就是把虚拟 DOM 渲染为真实 DOM。渲染器还有一个重要的作用就是寻找并且只更新变化的内容(diff)。
- 根据虚拟 DOM 标签名创建 真实 DOM;
- 为真实 DOM 添加属性以及事件;
- 处理子节点,递归调用渲染器;
- 最后将真实 DOM 挂在的容器节点下;
js
function renderer(vnode, container) {
// 1. 使用 vnode.tag 作为标签名称创建 DOM 元素
const el = document.createElement(vnode.tag)
// 2. 遍历 vnode.props,将属性、事件添加到 DOM 元素
for (const key in vnode.props) {
// ...
}
// 3. 处理 children
if (typeof vnode.children === 'string') {
// 如果 children 是字符串,说明它是元素的文本子节点
el.appendChild(document.createTextNode(vnode.children))
} else if (Array.isArray(vnode.children)) {
// 递归地调用 renderer 函数渲染子节点,使用当前元素 el 作为挂载点
vnode.children.forEach(child => renderer(child, el))
}
// 4. 将元素添加到挂载点下
container.appendChild(el)
}
编译器和渲染器配合工作:
- 编译时做静态标记
patchFlags
,渲染时diff
跳过静态节点;
响应式 Reactivity
响应式的作用是让数据和视图保持同步。
Vue.js 的响应式系统基于 JavaScript 对象的 getter 和 setter 实现的,在 getter 中收集依赖,setter 中,通知视图会自动更新。
Vue 3 中的响应式系统使用了 ES6 的 Proxy 对象来实现,相比于 Vue 2 中的 Object.defineProperty 实现具有更好的性能、更丰富的 API 和更少的限制。
以 vue3 为例
js
const target = { name: '张三' }
const proxy = new Proxy(target, {
get(target, key) {
// 依赖收集
// ...
return target[key]
},
set(target, key, newVal) {
// 通知更新
// ...
target[key] = newVal
}
})
除了编译器、渲染器、响应式这些核心功能外,Vue.js 还提供了许多其他的功能,如组件系统、路由、状态管理等。