非官方迁移指南:https://vue3.zcopy.site/guide/migration/introduction.html
总结
兼容性
Vue2 支持 IE11,Vue3不支持
更快
- 基于Proxy 的响应式
- 重写 Diff 算法
- 静态标记,通过 静态树提升、静态属性 减少 patch 过程,提高性能
- 优化 slots 的生成
- v-if 优先级比 v-for 高
更小
通过Tree Shaking 摇树 优化核心库体积。
在保持代码运行结果不变的前提下,去除无用(dead code)的代码。
在Vue2中,无论我们使用什么功能,它们最终都会出现在生产代码中。主要原因是Vue实例在项目中是单例的,捆绑程序无法检测到该对象的哪些属性在代码中被使用到。
而Vue3源码引入tree shaking特性,将全局 API 进行分块。如果你不使用其某些功能,它们将不会包含在你的基础包中。就是比如你只使用 watch
import {watch} from 'vue'
其他的 computed 没用到就不会打进包里,从而减少包体积
更友好
跨平台:编译器核心和运行时核心与平台无关,使得 Vue 更容易与任何平台(Web、
Android、iOS)一起使用
更易使用和维护性
-
Composition API(组合式 API ) 教程
-
支持 TypeScript,编辑器能提供强有力的类型检查和错误及警告
-
独立的响应化模块,许多包被解耦,更加模块化
-
更好的调试支持
-
单文件组件组合式 API 语法糖 (
<script setup>
) -
Fragments 片段,支持多个根节点 v3 迁移指南
移除
- 基于Proxy的响应式不再需要
Vue.set()
、Vue.delete()
- 移除
$children
,推荐使用ref
模板引用 v3 迁移指南 - 移除
$on
、$off
、$once
也不推荐eventBus
v3 迁移指南 - 移除
Vue.filter()
过滤器 v3 迁移指南 - 移除
inline-template
内联模板 Attribute v3 迁移指南
非兼容性
- Vue实例移除 propsData(
propsData
选项) v3 迁移指南 - 生命周期钩子
beforeDestroy
和destroyed
已经分别被重命名为beforeUnmount
和unmounted
$listeners
事件侦听变成$attrs
的一部分 v3 迁移指南$attrs
包含 class & style v3 迁移指南v-model
为Vue2v-model
与.sync
的结合 v3 迁移指南- 在同一元素上使用的
v-if
和v-for
优先级已更改 v3 迁移指南 - v-bind 合并行为 (
v-bind="object"
现在排序敏感) v3 迁移指南
扩展
基于 Proxy 的响应式系统
Vue 2 基于 Object.defineProperty()
的 getter 和 setter。Vue 3 使用 ES2015 Proxy
作为 其观察机制。
使用proxy带来如下变化:
- 组件实例初始化的速度提高 100%
- 使用 Proxy 节省以前一半的内存开销,加快速度,但是存在低浏览器版本的不兼容
- 为了继续支持 IE11,Vue 3 将发布一个支持旧观察者机制和新 Proxy 版本的构建
proxy
与 Object.defineProperty(obj, prop, desc)
方式相比有以下优势:
- proxy直接监听整个对象。对象响应式将无需遍历对象属性,并逐一进行监听。
- 可以监听对象动态新增和删除。不再需要通过额外的API,Vue.set、Vue.observerable、Vue.delete 来实现响应式。
- 可以监听数组变化。
- 不再像Vue2一样重写数组的7个修改方法 push、pop、shift、unshift、splice、sort、reverse。
- 还可以监听数组的索引和 length 属性;
- 代码更简化
// 丢掉麻烦的备份数据
let proxyObj = new Proxy(obj,{
get : function (target,prop) {
return prop in target ? target[prop] : 0
},
set : function (target,prop,value) {
target[prop] = 888;
}
})
虚拟 DOM 重写
期待更多的编译时提示来减少运行时开销,使用更有效的代码来创建虚拟节点。
组件快速路径+单个调用+子节点类型检测
-
跳过不必要的条件分支
-
JS 引擎更容易优化
静态标记
静态标记减少新旧节点对比过程
静态树提升(Static Tree Hoisting)
使用静态树提升,这意味着 Vue 3 的编译器将能够检测到什么是静态的,然后将其提升,从而降低了渲 染成本。
-
跳过修补整棵树,从而降低渲染成本
-
即使多次出现也能正常工作
静态属性提升
使用静态属性提升,Vue 3 打补丁时将跳过这些属性不会改变的节点。
优化 slots 的生成
vue3 中可以单独重新渲染父级和子级
-
确保实例正确的跟踪依赖关系
-
避免不必要的父子组件重新渲染
使用
写法
Vue2
<script>
export default {
inheritAttrs: false,
props: {
value: string
},
data() {
return {}
},
computed: {},
watch: {},
// lifecycle
beforeCreate() {},
created() {},
beforeMount() {},
mounted() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroy() {},
activated() {},
deactivated() {},
// 方法
methods: {}
}
</script>
Vue3 选项式
<script>
export default {
inheritAttrs: false,
props: {
modelValue: string,
modelModifiers: {
default: () => ({})
}
},
emits: [],
data() {
return {}
},
setup(props, ctx) {
const {attrs, emit, slot} = ctx
},
computed: {},
watch: {},
// lifecycle
beforeCreate() {},
created() {},
beforeMount() {},
mounted() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroy() {},
activated() {},
deactivated() {},
// 方法
methods: {}
}
</script>
Vue3 组合式
<script>
export default {
inheritAttrs: false
}
</script>
<script setup>
import {ref, reactive, useAttrs, computed, watch, onMounted, }
const attrs = useAttrs()
onMounted(() => {
console.log('attrs', attrs)
})
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const count = ref(0)
function add() {
count.value++
}
const double = computed(() => {
return count.value * 2
})
const obj = reactive({
title: '',
age: 18,
info: {}
})
// 监听多个值
watch(
[count, () => obj.title, () => obj.age],
([newCount, oldCount], [newTitle, oldTitle], [newAge, oldAge]) => {},
{
immediate: true,
deep: true,
flush: 'post'
}
)
watch(
obj, // 直接深层监听obj
([newObj, oldObj]) => {} // 注意 newObj 与 oldObj 一致
)
watch(
() => obj.info, // 只有在 obj.info 被替换时解发,使用 deep: true 解决
([newInfo, oldInfo]) => {},
{ deep: true } // 直接深层侦听如0bj
)
watchEffect(() => {})
watchPostEffect(() => {})
// 模板引用
const inputRef = ref(null)
const inputsRef = ref([])
// 子组件使用 <script setup> ,父组件无法直接通过ref访问子组件的所有属性和方法,需要子组件通过 defineExpose 暴露属性和方法才可以访问到
definedExpose([count, add])
</script>
Composition API 解决什么问题
Vue2.0 中,随着功能的增加,组件变得越来越复杂,越来越难维护。且 Vue2.0 缺少一种较为简洁的低成本的机制来完成逻辑复用,虽然可以使用 minxis 完成逻辑复用,但是当 mixin 变多的时候,data、computed 或者 method 将难以对应来源于哪个 mixin,使得类型推断难以进行。另外,多个 mixin 的 property 存在变量命名冲突的风险。
所以 Composition API 的出现,主要是也是为了解决 Option API 带来的问题。
Option API 选项式 对比 Composition API 组合式
Option API:
- 代码碎片化,代码量大
- 可复用性不高
- 可读性差,可维护性差
Composition API :
- 更好的代码组织
- 更好的逻辑提取和复⽤性
- 逻辑分明 可维护性 高
- Hook:基于函数组合的 API,可以将一个功能的所有状态、方法、都封装到一个函数里面,方便统一管理
SFC 中组合式 API 语法糖 <script setup>
Composition API 的写法在该语法糖都支持,除了 name 和 inheritAttrs
注意项: inheritAttrs: false
需要在 export default
中写,无法写在语法糖中
优点:
- 更灵活
- 逻辑复用
- 更好的类型推导
- 更小的生产包体积
改变:
- 没有 this
- 没有响应式数据 data,改为响应式 API:ref、reactive、shallowRef、shallowReactive
- 写法更改:
- 组件通信:props、emits、attrs、依赖注入 Provide/inject
- 响应式数据、computed、watch
- 生命周期、method
- nextTick 等
SFC 中的 <style scoped><style>
插槽内容(新增)
::v-slotted(.bar) {}
或 :slotted(.bar) {}
注意:是在子组件中设置
编译结果: .bar[data-v-xxx-s] {}
后缀-s 表示只针对 slot 插槽内容
优先级:
.bar[data-v-xxx] {}
优先于.bar[data-v-xxx-s] {}
- 父组件设置插槽内容样式优先于子组件设置插槽内容样式
一次性全局规则(新增)
Vue3:
通过 ::v-global(.bar) {}
或 :global(.bar) {}
设置全局样式。
编译结果:.bar {}
优先级:.bar {}
优先于 .bar[data-v-xxx] {}
Vue2 :
通过 <style></style>
来设置全局样式的。
缺点:这是永久修改,切换其他页面,样式为修改后的样式。
深度选择器(修改)
Vue3:
使用 ::v-deep(.bar) {}
或 :deep(.bar) {}
深度修改样式
编译结果: [data-v-xxx] .bar {}
优先级:
[data-v-xxx] .bar {}
优先于.bar[data-v-xxx] {}
;- deep 优先于 scoped
Vue2:
使用 ::v-deep .bar {}
或 :deep .bar {}
深度修改样式
不推荐 /deep/ .bar {}
和 >>> .bar {}
SFC 中状态驱动的 CSS 变量( style 中支持 v-bind
)
注意:
element-plus 的 Dialog 和 Drawer 中的样式不支持 v-bind
写法:
<script setup>
const theme = {
color: red,
};
</script>
<style>
.bar {
color: v-bind("theme.color");
}
</style>