第一章 框架的定位和方向
命令式 | 声明式 | |
---|---|---|
差异 | 关注过程,利用原生JavaScript 进行Dom操作(jQuery) | 关注结果,类 HTML 的模板(Vue) |
优缺点 | 理论上可以做到极致优化,但心智负担大 | 封装了过程,损失一部分性能,心智负担小,可维护性更强 |
更新性能消耗 | 直接修改 | 找出差异(虚拟Dom) + 直接修改 |
纯运行时 | 纯编译时 | |
---|---|---|
解释 | Render函数,将虚拟Dom -> 真实Dom。 | 把 HTML 标签编译成树型结构的数据对象,或直接编译成命令式代码 |
缺点 | 没办法分析用户提供的内容,哪些内容未来可能会改变,哪些内容永远不会改变 | 由于不需要任何运行时,而是直接编译成可执行的 JavaScript 代码,因此性能可能会更好,但是这种做法有损灵活性 |
Vue.js 3 是一个编译时 + 运行时的声明式框架。
第二章 设计核心
开发体验是衡量一个框架的重要指标之一,提供友好的警告信息至关重要。提供了registerErrorHandler
函数,用户可以使用它注册错误处理程序。
框架的大小也是衡量框架的标准之一。利用 Tree-Shaking 机制,定义__DEV__
常量,生产环境去除警告信息。添加 /*#__PURE__*/
注释,标记不会产生副作用的函数,辅助Tree-Shaking。定义__VUE_OPTIONS_API__
常量等决定一些特性的开关,减少打包体积。
输出格式:
- IIFE 形式。立即执行的函数表达式,通过
<script>
标签引入。 - ESM 格式。带有 -bundler 字样的 ESM 资源是给 rollup.js 或 webpack 等打包工具使用的,而带有 -browser 字样的 ESM 资源是直接给
<script type="module">
使用的。前者需要(process.env.NODE_ENV !== ‘production’) 替换__DEV__
常量。 - cjs 格式。服务端渲染中,资源的模块格式应该是 CommonJS。
第三章 设计思路
介绍了编译器、渲染器的基本实现、组件的本质。
- 渲染器:虚拟Dom -> 真实Dom。
// 虚拟Dom
const vnode = {
tag: "div",
props: {
onClick: () => alert("hello"),
},
children: "click me",
};
// 渲染器
function renderer(vnode, container) {
const el = document.createElement(vnode.tag);
// 遍历 vnode.props,将属性、事件添加到 DOM 元素
for (const key in vnode.props) {
if (/^on/.test(key)) {
el.addEventListener(
key.substr(2).toLowerCase(), // onClick ---> click
vnode.props[key] // 事件处理函数
);
}
}
// 处理 children
if (typeof vnode.children === "string") {
// 文本子节点
el.appendChild(document.createTextNode(vnode.children));
} else if (Array.isArray(vnode.children)) {
// 递归地调用 renderer 函数渲染子节点,使用当前元素 el 作为挂载点
vnode.children.forEach((child) => renderer(child, el));
}
container.appendChild(el);
}
- 编译器:模板 -> 虚拟Dom。
const template = '<div id="foo" :class="cls"></div>';
// 虚拟Dom
render() {
return {
tag: 'div',
props: {
id: 'foo',
class: cls
},
patchFlags: 1 // 假设数字 1 代表 class 是动态的,通过标记知道哪些是动态的,提高性能
}
}
- 组件:DOM 元素的封装。
const vnode = {
tag: myComponent,
props: {},
};
const myComponent = {
tag: "div",
props: {},
}
// 渲染器添加判断
function renderer(vnode, container) {
if (typeof vnode.tag === 'string') {
mountElement(vnode, container)
} else if (typeof vnode.tag === 'object') { // 如果是对象,说明 vnode 描述的是组件
mountComponent(vnode, container)
}
}
更新中…