1. 提供友好的信息
-
调用了
warn
函数, 本质是调用了 console.warn函数, 提供了友好的警告信息 -
初始化自定义了
formatter
的 initCustomerFormatter函数。(打开DevTools的设置,勾选 “Console” -> "Enable custom formatters"选项
), 打印 count.value(vue3)时,内容会变得非常直观
2. 如何支持Tree-Shaking
-
__DEV__
常量控制 -
添加注释
/*#__PURE__*/
, 支持摇掉副作用函数
3. 输出的构建产物
-
- 输出
IIFE(立即调用的函数表达式)
格式的资源, 满足<script>
标签的引入
<body> <script src="xx/vue.js"></script> </body>
- 输出
-
- 输出
vue.esm-browser.js
ESM 格式的资源, 满足<script type="module" src="xx/vue.esm-browser.js">
引入
- 输出
-
- 输出
vue.runtime.esm-bundler.js
资源, 给 rollup.js 或 webpack 等打包工具使用
- 输出
-
- 输出
cjs
资源, 在服务端使用
- 输出
4. 特性开关
对于关闭的特性,可以利用 Tree-Shaking
机制摇掉相关代码,使最终打包的资源体积最小化。
特性开关实现: 本质上是利用rollup.js的预定义常量插件来实现。比如
{
__FEATURE_OPTION_API__: isBundlerESMBuild ? `__VUE_OPITIONS_API__` : true
}
__VUE_OPITIONS_API__
是个特性开关,通过设置该值来控制是否要包含兼容vue2 选项API的调用方式
5. 错误处理
-
- 统一的错误处理接口
-
- 用户可以注册自定义的错误处理程序
let handleError = null
export default {
foo(fn) {
callWithErrorHandling(fn)
},
// 用户可以注册自定义的错误处理程序
registerErrorHandler(fn) {
handleError = fn
}
}
function callWithErrorHandling(fn) {
try {
fn && fn()
} catch (e) {
handleError(e)
}
}
以上就是vue错误处理的原理
6. 友好的TS支持
7. 声明式描述UI
有两种方式
-
- 模版
<div id="add" :title="title" @click="handle">
<span></span>
</div>
-
- Js对象
const title = {
tag: 'h1',
props: {
onClick: handler
},
children: [
{
tag: 'span'
}
]
}
Js对象描述UI比较灵活,其实就是虚拟DOM
组件中手写的渲染函数就是使用虚拟DOM来描述UI的
import { h } from 'vue'
export default {
render() {
// h函数返回的是对象
return h('h1', { onClick: handler }) // 虚拟DOM
}
}
8. 渲染器
作用:使用DOM操作API,把虚拟DOM渲染为真实DOM
9. 组件的本质
组件是一组DOM元素的封装,这组DOM元素就是组件要渲染的内容
const MyComponent = function() {
return {
tag: 'div'.
props: {
onClick: () => alert('hello')
},
children: 'click me'
}
}
组件的返回值也是虚拟DOM
10. 模版的工作原理
模版通过编译器, 编译为 渲染函数
<template>
<div @click="handler">
click me
</div>
</template>
<script>
export default {
data() {},
methods: {}
}
</script>
编译器会将<template>标签里的内容, 编译成渲染函数并添加到 <script>标签块的组件对象上
<script>
export default {
data() {},
methods: {},
render() {
return h('div', { onClick: handler }, 'click me')
}
}
</script>
总结: 组件(使用模版 或者 手写渲染函数)要渲染的内容都是通过渲染函数产生的,渲染器再把渲染函数返回的虚拟DOM渲染为真实DOM.
11. 渲染器和编译器的配合
模版通过编译器,编译为渲染函数。 这过程中, 会标记哪些是静态属性,哪些是动态属性,把这些信息附加到生成的渲染函数中,让渲染器省去寻早变更点的工作,提升性能
12.为什么选择“声明式”,而不是“命令式”
12.1 命令式关注的是过程
,比如“jQuery”,就是典型的命令式框架
分析下面这段jQuery代码
$('#app') // 获取div
.text('hello world') //设置文本内容
.on('click', () => { alert('ok') }) //绑定点击事件
描述了 “获取div” -> "设置文本内容" -> "绑定点击事件"
这一过程, 这就是命令式框架
的特点:关注过程
。
12.2 声明式关注的是结果
, 比如 “vue”
以上代码用vue实现如下:
<div @click="() => alert('ok')">hello world</div>
可以发现, 以上代码提供的是一个结果
,怎么实现这个“结果”,使用者并不关心。实现“结果”的过程,是在Vue.js内部完成的。也就是说, Vue.js帮我们封装了过程, 内部的实现是命令式
的,暴露给外部用户的是声明式
12.3 两种范式对比
12.3.1 性能
-
比如要修改div的内容,命令式代码代码如下
div.textContent = '你好, 我是修改后的内容'
直接调用相关命令
-
声明式代码代码如下
// 修改前的代码 <div @click="() => alert('ok')">你好, 我是修改前的内容</div> // 修改后的代码 <div @click="() => alert('ok')">你好, 我是修改后的内容</div>
找到前后差异,然后更新差异的地方,最终完成跟新的代码仍然是
div.textContent = '你好, 我是修改后的内容'
通过对比可以发现,声明式代码
比 命令式代码
多了一步 找出前后差异的性能消耗
。
性能: 命令式代码
> 声明式代码
12.3.2 可维护性
命令式代码需要维护整个“过程”,
声明式代码展示最终的结果,不关心过程
声明式代码
> 命令式代码
12.4 总结
vue选择了声明式代码
(维护性好),至于性能问题,做到损失最小化,也就是优化找出差异的性能消耗(比如虚拟DOM的出现就是为了最小化性能消耗)
13. 为什么要引入虚拟DOM, 虚拟DOM又是解决什么问题的
前面谈到, 声明式代码的更新性能消耗 = 找出差异的性能消耗 + 直接修改的性能消耗,虚拟DOM的出现就是为了最小化找出差异的性能消耗
虚拟DOM要解决的问题是: 写声明式代码且要保证程序的性能
13.1 性能、可维护性、心智负担对比
-
创建阶段:
虚拟DOM 和 原生DOM操作, 性能差别不大。
-
更新阶段:
虚拟DOM:
创建新的JavaScript对象 + Diff + 必要的DOM更新
原生DOM操作:
必要的DOM更新
原生DOM操作,需要手动创建、删除DOM元素,所以它的心智负担大,可维护性差,但是性能好
虚拟DOM,不需要手动创建、删除DOM元素,所以它的心智负担小, 可维护性好,加上有Diff,性能虽没原生DOM好,但也不错