Vue3.0
你知道有哪些改进
改进
概括一下有如下改进:
- 压缩包体积更小(
20Kb => 10Kb
) - 数据劫持的方式修改(
Object.defineProperty => Proxy
) Virtual DOM
重构,提供diff
算法效率- 组件定义方式变更(
Options API => Function_Based API
)
压缩包体积更小
当前最小化并被压缩的 Vue
运行时大小约为 20kB
(2.6.10
版为 22.8kB
)。Vue 3.0
捆绑包的大小大约会减少一半,即只有10kB
!
数据劫持的方式修改(Object.defineProperty
=> Proxy)
Vue 2.x
中使用的Object.defineProperty
有以下问题:
- 不能监听数组的变化,需要hack技术
- 只能监听目标对象一层的变化,需要递归调用
Object.defineProperty
来实现深入监听 - 通过
Object.defineProperty
来重新定义目标对象,造成了vm.$data
的体积膨胀,如果遇到定义大量且层级复杂的场景(如echarts
)会操作极大的性能消耗
Virtual DOM
重构,提供diff
算法效率
vue
的特点是底层为Virtual DOM
,上层包含有大量静态信息的模版。为了兼容手写 render function
,最大化利用模版静态信息,vue3.0
采用了动静结合的解决方案,将vdom
的操作颗粒度变小,每次触发更新不再以组件为单位进行遍历,主要更改如下
- 将模版基于动态节点指令切割为嵌套的区块
- 每个区块内部的节点结构是固定的
- 每个区块只需要以一个
Array
追踪自身包含的动态节点
组件定义方式变更(Options API => Function_Based API
)
function based API
的优势:
- 对
typescript
更加友好,typescript
对函数的参数和返回值都非常好,写Function-based API
既是javascript
又是typescript
,不需要任何的类型声明,typescript
可以自己做类型推导 - vue中的UI组件很少用到继承,一般都是组合,可以用Function-based API
options API
中所有配置项都是key-value
形式,不利于tree-shaking
工具对其进行瘦身
Section 1
认识 Vue3
Performance
比Vue2 runtime
快了2倍- 重写了虚拟
dom
的实现 - 编译模板的优化(运行时编译)
update
性能提高 (在编译时给到需要更新的信息,render
中_createVNode
的传参,把动态的标记为PROPS
)SSR
速度提高
- 重写了虚拟
Tree Shaking
按需编译代码TS Support
Composition Api
Customer Render API
自定义渲染器- 内置新特性组件
按需编译代码 (从 Vue
中到处函数来用)
用TypeScript
重写
ESlint Vue3.x
extends: [
'airbnb',
'plugin:vue/vue3-essential',
'plugin:vue/vue3-strongly-recommended',
],
VSCode
的 vetur
暂时还不支持 Vue3
或者我哪里配的不对,它总是强行把 Vue2.x
的 lint
给我过一遍,所以就,
settings.json
里:
"vetur.validation.template": false,
Fragment
<template>
<div>1</div>
<div>2</div>
</template>
Composition API
- 更好的逻辑服用与代码组织
- 更好的类型推导(函数对类型友好)
通过类型来组织代码,Options API
,当代码量小的时候很友好,但代码量大时很难维护,反复横跳,一个功能被拆分到很多地方。
Composition API
解决了这个难题。同事也解决 Mixin
带来的副作用。本质是函数。
// 函数组件,从原生命周期抽离出共用的逻辑,本质是函数,容易复用
const sayHello = () => {
onMounted(() => {
console.log('hello');
});
};
export default defineComponent({
setup() {
sayHello()
// 创建响应式对象的方式
// 3点
// ref
// 值类型
const count = ref(0)
console.log('count', count);
// reactive / readonly
// {} [] 引用类型
const state = reactive({ time: 0 })
console.log('state', state);
const handleClick = () => {
count.value++
state.time++
}
// 响应式系统 api
// computed
const double = computed(() => {
return count.value * 2
})
// 监听double
watch(double, (value) => {
console.log('watch double', value);
})
return {
count,
state,
double,
handleClick,
}
}
})
Vue Test Utils Next(VTU)
单元测试
测试功能,不是测试细节。主要是为了避免新功能影响旧功能。
一个组件:
- 输入 ->
props
/ 用户交互 - 输出 ->
dom
渲染 /Events(emit)
TDD:Test-driven development 从使用者角度思考
- 先写测试 (根据用户使用的方式 user story)
- 写逻辑让测试通过
- 重构
TDD模式 重构 Element UI Button
- 拆分需要实现的功能
- 可以显示
slot
的内容 - 设置
size
- 设置
type
- 是否 圆角、
plain、disabled
、…
Section 2
Custom Render API
Vue3
源码
Custom Render API
自定义渲染,用户可自定义渲染目标平台(比如Canvas)
createRenderer
- 接口
createElement
insert
patchProp
Vue3.0中比较重要的改进
1,压缩包体积更小
当前最小化并被压缩的 Vue
运行时大小约为 20kB
(2.6.10 版为 22.8kB
)。Vue 3.0
捆绑包的大小大约会减少一半,即只有10kB
!
2,Object.defineProperty -> Proxy
Object.defineProperty
是一个相对比较昂贵的操作,因为它直接操作对象的属性,颗粒度比较小。将它替换为es6的Proxy
,在目标对象之上架了一层拦截,代理的是对象而不是对象的属性。这样可以将原本对对象属性的操作变为对整个对象的操作,颗粒度变大。
javascript
引擎在解析的时候希望对象的结构越稳定越好,如果对象一直在变,可优化性降低,proxy
不需要对原始对象做太多操作。
3,Virtual DOM
重构
vdom
的本质是一个抽象层,用javascript
描述界面渲染成什么样子。react
用jsx
,没办法检测出可以优化的动态代码,所以做时间分片,vue
中足够快的话可以不用时间分片。
传统vdom
的性能瓶颈:
虽然 Vue 能够保证触发更新的组件最小化,但在单个组件内部依然需要遍历该组件的整个 vdom 树。
传统 vdom 的性能跟模版大小正相关,跟动态节点的数量无关。在一些组件整个模版内只有少量动态节点的情况下,这些遍历都是性能的浪费。
JSX
和手写的 render function
是完全动态的,过度的灵活性导致运行时可以用于优化的信息不足
那为什么不直接抛弃vdom
呢?
高级场景下手写 render function
获得更强的表达力
生成的代码更简洁
兼容2.x
vue
的特点是底层为Virtual DOM
,上层包含有大量静态信息的模版。为了兼容手写 render function
,最大化利用模版静态信息,vue3.0采用了动静结合的解决方案,将vdom
的操作颗粒度变小,每次触发更新不再以组件为单位进行遍历,主要更改如下
将模版基于动态节点指令切割为嵌套的区块
每个区块内部的节点结构是固定的
每个区块只需要以一个 Array 追踪自身包含的动态节点
vue3.0将 vdom 更新性能由与模版整体大小相关提升为与动态内容的数量相关
4, 更多编译时优化
Slot 默认编译为函数:父子之间不存在强耦合,提升性能
Monomorphic vnode factory:参数一致化,给它children信息,
Compiler-generated flags for vnode/children types
5,选用Function_based API
为什么撤销 Class API ?
1,更好地支持TypeScript
Props 和其它需要注入到 this 的属性导致类型声明依然存在问题
Decorators 提案的严重不稳定使得依赖它的方案具有重大风险
2,除了类型支持以外 Class API 并不带来任何新的优势
3,vue中的UI组件很少用到继承,一般都是组合,可以用Function-based API
Function_based API
示例如下
1,vue3.0将组件的逻辑都写在了函数内部,setup()会取代vue2.x的data()函数,返回一个对象,暴露给模板,而且只在初始化的时候调用一次,因为值可以被跟踪。
2,新的函数api:const count = value(0)
value是一个wrapper,是一个包装对象,会包含数字0,可以用count.value来获取这个值。在函数返回的时候会关心是value wrapper,一旦返回给模版,就不用关心了。
优点:即使count包含的是基本类型,例如数字和字符串,也可以在函数之间来回传递,当用count.value取值的时候会触发依赖,改值的时候会触发更新。
3,计算属性返回的也是这个值的包装。
4,onMounted生命周期函数直接注入。
Function-based API 对比Class-based API有以下优点
1,对typescript更加友好,typescript对函数的参数和返回值都非常好,写Function-based API既是javascript又是typescript,不需要任何的类型声明,typescript可以自己做类型推导。
2,静态的import和export是treeshaking的前提,Function-based API中的方法都是从全局的vue中import进来的。
3,函数内部的变量名和函数名都可以被压缩为单个字母,但是对象和类的属性和方法名默认不被压缩(为了防止引用出错)。
4,更灵活的逻辑复用。
目前如果我们要在组件之间共享一些代码,则有两个可用的选择:mixins 和作用域插槽( scoped slots),但是它们都存在一些缺陷:
1,mixins 的最大缺点在于我们对它实际上添加到组件中的行为一无所知。这不仅使代码变得难以理解,而且还可能导致名称与现有属性和函数发生冲突。
2,通过使用作用域插槽,我们确切地知道可以通过 v-slot 属性访问了哪些属性,因此代码更容易理解。这种方法的缺点是我们只能在模板中访问它,并且只能在组件作用域内使用。
高阶组件在vue中比较少,在react中引入是作为mixins的替代品,但是比mixins更糟糕,高阶组件可以将多个组件进行包装,子组件通过props
接收数据,多个高阶组件一起使用,不知道数据来自哪个高阶组件,存在命名空间的冲突。而且高阶组件嵌套得越多,额外的组件实例就越多,造成性能损耗。
下面以一个鼠标位置侦听的案例演示vue3.0中的逻辑复用
以上就是vue3.0中比较重要的改进,我会在后面的文章中结合具体代码详细为大家讲解vue3.0更多新的使用方法。希望大家持续关注,共勉。