Vue3.0
的设计目标可以概括为体积更小、速度更快、加强TypeScript
支持、加强API
设计一致性、提高自身可维护性、开放更多底层功能。
从Vue2
到Vue3
在一些比较重要的方面的详细对比。
Vue2 -> Vue3
beforeCreate -> setup
created -> setup
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
errorCaptured -> onErrorCaptured
renderTracked -> onRenderTracked
renderTriggered -> onRenderTriggered
在这里主要是增加了setup
这个生命周期,而其他的生命周期都是以API
的形式调用,实际上随着Composition API
的引入,我们访问这些钩子函数的方式已经改变,我们所有的生命周期都应该写在setup
中,此方法我们应该实现大多数组件代码,并处理响应式,生命周期钩子函数等。
Vue2
是通过数据劫持的方式来实现响应式的,其中最核心的方法便是通过Object.defineProperty()
来实现对属性的劫持,该方法允许精确地添加或修改对象的属性,对数据添加属性描述符中的getter
与setter
存取描述符实现劫持。Vue2
之所以只能兼容到IE8
主要就是因为defineProperty
无法兼容IE8
,其他浏览器也会存在轻微兼容问题。
Vue3
使用Proxy
实现数据劫持,Object.defineProperty
只能监听属性,而Proxy
能监听整个对象,通过调用new Proxy()
,可以创建一个代理用来替代另一个对象被称为目标,这个代理对目标对象进行了虚拟,因此该代理与该目标对象表面上可以被当作同一个对象来对待。代理允许拦截在目标对象上的底层操作,而这原本是Js
引擎的内部能力,拦截行为使用了一个能够响应特定操作的函数,即通过Proxy
去对一个对象进行代理之后,我们将得到一个和被代理对象几乎完全一样的对象,并且可以从底层实现对这个对象进行完全的监控。Proxy
对象是ES6
引入的新特性,Vue3
放弃使用了Object.defineProperty
,而选择了使用更快的原生Proxy
,即是在兼容性方面更偏向于现代浏览器。
diff
算法的基础是Virtual DOM
,Virtual DOM
是一棵以JavaScript
对象作为基础的树,每一个节点称为VNode
,用对象属性来描述节点,实际上它是一层对真实DOM
的抽象,最终可以通过渲染操作使这棵树映射到真实环境上,简单来说Virtual DOM
就是一个Js
对象,用以描述整个文档。
Vue2
框架通过深度递归遍历新旧两个虚拟DOM
树,并比较每个节点上的每个属性,来确定实际DOM
的哪些部分需要更新,由于现代JavaScript
引擎执行的高级优化,这种有点暴力的算法通常非常快速,但是DOM
的更新仍然涉及许多不必要的CPU
工作。
在这里引用尤大的描述,为了实现这一点,编译器和运行时需要协同工作:编译器分析模板并生成带有优化提示的代码,而运行时尽可能获取提示并采用快速路径,这里有三个主要的优化:
DOM
树级别,我们注意到,在没有动态改变节点结构的模板指令(例如v-if
和v-for
)的情况下,节点结构保持完全静态,如果我们将一个模板分成由这些结构指令分隔的嵌套块,则每个块中的节点结构将再次完全静态,当我们更新块中的节点时,我们不再需要递归遍历DOM
树,该块内的动态绑定可以在一个平面数组中跟踪,这种优化通过将需要执行的树遍历量减少一个数量级来规避虚拟DOM
的大部分开销。Vue2
中使用的都是Js
,其本身并没有类型系统这个概念,现如今TypeScript
异常火爆,对于规模很大的项目,没有类型声明,后期维护和代码的阅读都是头疼的事情,虽然Vue2
实际上也是可以使用TS
的,但是支持并不算特别完美,此外Vue2
的源码也是使用Facebook
的Flow
做类型检查。
最终Vue3
借鉴了React Hook
实现了更自由的编程方式,提出了Composition API
,Composition API
不需要通过指定一长串选项来定义组件,而是允许用户像编写函数一样自由地表达、组合和重用有状态的组件逻辑,同时提供出色的TypeScript
支持。
Vue2
官方说明运行时打包23k
,但这只是没安装依赖的时候,随着依赖包和框架特性的增多,有时候不必要的,未使用的代码文件都被打包了进去,所以后期项目大了,打包文件会特别多还很大。
在Vue3
中,通过将大多数全局API
和内部帮助程序移动到JavaScript
的module.exports
属性上实现这一点,这允许现代模式下的module bundler
能够静态地分析模块依赖关系,并删除与未使用的module.exports
属性相关的代码,模板编译器还生成了对Tree Shaking
摇树优化友好的代码,只有在模板中实际使用某个特性时,该代码才导入该特性的帮助程序,尽管增加了许多新特性,但Vue3
被压缩后的基线大小约为10KB
,不到Vue2
的一半。
Vue3
相对于Vue2
的非兼容的变更概括,详情可以查阅https://v3.cn.vuejs.org/guide/migration/introduction.html
。
Vue API
已更改为使用应用程序实例。API
已经被重构为可tree-shakable
。v-model
用法已更改,替换v-bind.sync
。<template v-for>
和非v-for
节点上key
用法已更改。v-if
和v-for
优先级已更改。v-bind="object"
现在排序敏感。v-on:event.native
修饰符已移除。v-for
中的ref
不再注册ref
数组。functional
属性在SFC
单文件组件<template>
和functional
组件选项被抛弃。defineAsyncComponent
方法来创建。emits
选项中声明。API
改变。$scopedSlots property
已删除,所有插槽都通过$slots
作为函数暴露。$listeners
被移除或整合到$attrs
。$attrs
现在包含class and style attribute
。is prop
的使用只严格限制在被保留的<component>
标记中。destroyed
生命周期选项被重命名为unmounted
。beforeDestroy
生命周期选项被重命名为beforeUnmount
。default prop
工厂函数不再可以访问this
上下文。API
已更改为与组件生命周期一致。data
选项应始终被声明为一个函数。mixin
的data
选项现在为浅合并。Attribute
强制策略已更改。class
被重命名。<TransitionGroup>
不再默认渲染包裹元素。deep
选项。v-if/else-if/else
、v-for
、v-slot
的<template>
现在被视为普通元素,并将生成原生的<template>
元素,而不是渲染其内部内容。Vue2
中,应用根容器的outerHTML
将替换为根组件模板,如果根组件没有模板/渲染选项,则最终编译为模板,Vue3
现在使用应用容器的innerHTML
,这意味着容器本身不再被视为模板的一部分。keyCode
支持作为v-on
的修饰符。$on
、$off
和$once
实例方法attribute
。$children
实例property
。$destroy
实例方法,用户不应再手动管理单个Vue
组件的生命周期。Vue3
的组件简单示例,可查看在线示例https://codesandbox.io/s/c1437?file=/src/App.vue
。