Vue2 与 Vue3 的区别
1. 生命周期
beforeDestory
与destoryed
名称的改变- 在组合
API
中没有beforeCreate
与created
生命周期,而是使用setup
方法
2. 多根节点
组件中不需要在最外层写个标签将内容包裹
3. Composition API
Vue2
选项式 API
(Options Api
),一个逻辑会分散在不同的属性中,如data、props、computed、watch、method、生命周期
,导致代码可读性差,书写与维护困难。
Vue3
组合式 API
(Composition API
),很好地解决了这个问题,可将同一逻辑的内容写到一起,增强了代码的可读性、内聚性,其还提供了较为完美的逻辑复用性方案。
优点:
- 更好的逻辑复用
- 更灵活的代码组织
- 更好的类型推导
- 更小的生产包体积
组合式 API:
- 响应式 API:
ref()
:把基本类型的数据变为响应式数据。接受一个参数值并返回一个响应式且可改变的ref
对象。ref
对象拥有一个指向内部值的单一属性.value
,如果传入的是一个引用类型,会在内部调用reactive()
去处理。reactive()
:将引用类型数据转为响应式数据,参数必须是对象或数组
- 生命周期
- 在
setup
中没有beforeCreate
与created
生命周期 setup
中使用生命周期需要加上前缀on
,如onMounted
setup
比beforeCreate、created
生命周期更早,也就是说在当前直接用this
去获取data
中的数据打出来的还是undefined
- 在
import { ref, toRefs, toRef } from 'vue'
export default {
props: {
title: String
},
// 注意 props 不能解构,否则丢失响应性
setup(props, context) {
// 将 `props` 转为一个其中全是 ref 的对象,然后解构
const { title } = toRefs(props)
// `title` 是一个追踪着 `props.title` 的 ref
console.log(title.value)
// 或者,将 `props` 的单个属性转为一个 ref
const title = toRef(props, 'title')
const count = ref(0) // count.value = 0
onMounted(()=>{
const posts = await res.json()
})
return {
count,
posts
}
},
mounted() {
console.log(this.count)
}
}
import { ref, toRefs, toRef } from 'vue'
export default {
props: {
title: String
},
// 注意 props 不能解构,否则丢失响应性
setup(props, context) {
// 将 `props` 转为一个其中全是 ref 的对象,然后解构
const { title } = toRefs(props)
// `title` 是一个追踪着 `props.title` 的 ref
console.log(title.value)
// 或者,将 `props` 的单个属性转为一个 ref
const title = toRef(props, 'title')
const count = ref(0) // count.value = 0
onMounted(()=>{
const posts = await res.json()
})
return {
count,
posts
}
},
mounted() {
console.log(this.count)
}
}
Setup 上下文,上下文对象暴露了其他一些在 setup 中可能会用到的值:
export default {
setup(props, context) {
// 透传 Attributes(非响应式的对象,等价于 $attrs)
console.log(context.attrs)
// 插槽(非响应式的对象,等价于 $slots)
console.log(context.slots)
// 触发事件(函数,等价于 $emit)
console.log(context.emit)
// 暴露公共属性(函数)
console.log(context.expose)
}
}
export default {
setup(props, context) {
// 透传 Attributes(非响应式的对象,等价于 $attrs)
console.log(context.attrs)
// 插槽(非响应式的对象,等价于 $slots)
console.log(context.slots)
// 触发事件(函数,等价于 $emit)
console.log(context.emit)
// 暴露公共属性(函数)
console.log(context.expose)
}
}
该上下文对象是非响应式的,可以安全地解构:
export default {
setup(props, { attrs, slots, emit, expose }) {
...
}
}
export default {
setup(props, { attrs, slots, emit, expose }) {
...
}
}
watchEffect()
watchEffect(() => {
// 用来做响应性追踪
console.log(copy.count)
})
watchEffect(() => {
// 用来做响应性追踪
console.log(copy.count)
})
computed
4. 异步组件 Suspense
5. 内置组件 Teleport
该组件可以将其所在组件模板的部分内容移动到指定的 DOM
位置,比如 body
或者其他任意位置,而 Vue2
中只能通过 $el
操作 DOM
方式实现。
场景:modal、toast
等需要插入到 body
中的组件
参数:
to
:需要移动的位置,选择器或者DOM
节点,内部会使用querySelector
方法查找该节点disabled
:是否禁用to
功能
<teleport :to="body" :disabled="false">
<div>需要移动的内容</div>
</teleport>
<teleport :to="body" :disabled="false">
<div>需要移动的内容</div>
</teleport>
注意点:
- 其组件样式不会受被挂载的父组件样式影响(因为其 dom 已不在父级下)
- 父级无法捕捉
Teleport
节点的事件冒泡(同上) - 多个
teleport
在同一个目标元素时会将内容追加到目标元素
6. 响应式原理
Vue2
响应式原理基础是Object.defineProperty
,需要劫持每个属性,所以对新增以及数组索引的变更无法监听Vue3
响应式原理基础是Proxy
,监听的是整个对象,所以能知道对象的每个属性变更
Object.defineProperty
问题:
- 无法监听对象或数组的新增、删除元素操作,主要原因是无法监听
.length
,它无法被劫持监听且修改
解决方案:
- 对
push、pop、shift、unshift、splice、sort、reverse
方法进行重写 - 提供
Vue.set
方法新增或删除对象属性或数组元素
以上方法是在其方法内部调用了对应的 dep.notify()
方法触发视图层的更新
7. 虚拟 DOM
Vue3
相比于 Vue2
,虚拟 DOM
上增加 patchFlag
字段
9. diff 算法优化
依赖 babel
编译出来的静态节点提升了 Diff
性能
10. 打包优化 Tree-Shaking
Tree-Shaking 是一种基于 ES Module 规范的 Dead Code Elimination 技术,它会在运行过程中静态分析模块之间的导入导出,确定 ESM 模块中哪些导出值未曾其它模块使用,并将其删除,以此实现打包产物的优化。
Vue3
中全局 API
只能通过 ES
模块显示导入进行访问,所以 webpack
可以检测代码模块是否被导出、导入,且被 JavaScript
文件使用。
内置 API
比如 transition、v-model
等标签或者指令被命名导出。只有在程序真正使用才会被捆绑打包。Vue3
将所有运行功能打包也只有约22.5kb,比 Vue2
轻量很多。
8. 事件缓存
Vue3
的 cacheHandler
可在第一次渲染后缓存我们的事件。相比于 Vue2
无需每次渲染都传递一个新函数。
11. TypeScript 支持
12. 全局 API 优化
全局 API
只能作为 ES
模块构建的命名导出进行访问。
import { nextTick } from 'vue'
nextTick(() => {
// 一些和DOM有关的东西
})
import { nextTick } from 'vue'
nextTick(() => {
// 一些和DOM有关的东西
})
13. router
vue2
中使用 this.$router.push()
等方法进行路由跳转,vue3
中如下:
import { useRouter, useRoute } from "vue-router"
setup(){
const router = useRouter()
const route = useRoute()
function fn(){
router.push('/about')
}
onMounted(()=>{
console.log(route.query.code)
})
return{
fn
}
}
import { useRouter, useRoute } from "vue-router"
setup(){
const router = useRouter()
const route = useRoute()
function fn(){
router.push('/about')
}
onMounted(()=>{
console.log(route.query.code)
})
return{
fn
}
}
方法的使用上一致,只是需要导入对应的方法。
新增了两个方法:
router.hasRoute()
:检查路由是否存在。router.getRoutes()
:获取一个包含所有路由记录的数组。
14. 全局 API 转移
vue2
中可以通过 Vue.prototype
去操作原型,在 vue3
中只能通过 app.config.globalProperties
2.x 全局 API( Vue) | 3.x 实例 API(app) |
---|---|
Vue.config.xxxx | app.config.xxxx |
Vue.config.productionTip | 移除 |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use |
Vue.prototype | app.config.globalProperties |
15. 响应式判断
isRef
: 检查值是否为一个ref
对象。isReactive
:检查对象是否是由reactive
创建的响应式代理。isReadonly
: 检查对象是否是由readonly
创建的只读代理。isProxy
:检查对象是否是由reactive
或readonly
创建的proxy
。
16. 指令与插槽
v-if
与v-for
的使用问题Vue3
移除了filter
(用方法调用或计算属性替换过滤器)vue3
移除v-on.native
修饰符
指令生命周期变更
Vue2 | Vue3 |
---|---|
bind | beforeMount |
inserted | mounted |
update | beforeUpdate |
componentUpdated | updated |
- | beforeUnmount |
unbind | unmounted |
17. 移除了一些属性与方法
$listeners
:将$listeners
事件内容合并到了属性传递的$attrs
下$scopedSlots
:将其统一到$slots
下v-on:native
:增加emits
属性用于定义需要触发的事件
Vue3 优点
- 性能提升
- 打包体积减少 41%
- 初次渲染快 55%,更新快 133%
- 内存使用减少 54%
Composition API
实现逻辑模块化和复用- 增加了
Teleport
组件 - 可以多个根元素
- 对数组的原生操作