Vue2->Vue3 知识迁移指南(官方文档无废话版)持续更新...
完整版请参考: v3-migration.vuejs.org/zh/
Vue 3 迁移指南
学习 Vue 3 的推荐方法是阅读新的文档。 谁说不是呢可那么长怎么弄?
值得注意的新特性
Vue 3 中需要关注的一些新特性包括:
- 组合式 API
- 单文件组件中的组合式 API 语法糖 (
<script setup>
) - Teleport 组件
- Fragments 片段
- Emits 组件选项
- 来自
@vue/runtime-core
的createRenderer
API 用来创建自定义渲染函数 - 单文件组件中的状态驱动的 CSS 变量 (
<style>
中的v-bind
) - SFC
<style scoped>
新增全局规则和针对插槽内容的规则 - Suspense 实验性
新的框架级别推荐
Vue 3 的支持库进行了重大更新。以下是新的默认建议的摘要: 这个重要,都得使
- 新版本的 Router, Devtools & test utils 来支持 Vue 3
- 构建工具链: Vue CLI -> Vite
- 状态管理: Vuex -> Pinia
- IDE 支持: Vetur -> Volar
- 新的 TypeScript 命令行工具: vue-tsc
- 静态网站生成: VuePress -> VitePress
- JSX:
@vue/babel-preset-jsx
->@vue/babel-plugin-jsx
新特性
组合式API
组合式 API (Composition API) 使用函数写 Vue 组件。
组合式 API 并不是函数式编程,组合式 API 是以 Vue 中数据可变的、细粒度的响应性系统为基础的,而函数式编程通常强调数据不可变。
基本上都会配合 <script setup>
语法在单文件组件中使用。
<script setup>
import { ref, onMounted } from 'vue'
// 响应式状态
const count = ref(0)
// 更改状态、触发更新的函数
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log(`计数器初始值为 ${count.value}。`)
})
</script>
<template>
<button @click="increment">点击了:{{ count }} 次</button>
</template>
API包括:
- 响应式 API:例如
ref()
和reactive()
,使我们可以直接创建响应式状态、计算属性和侦听器。 - 生命周期钩子:例如
onMounted()
和onUnmounted()
,使我们可以在组件各个生命周期阶段添加逻辑。 - 依赖注入:例如
provide()
和inject()
,使我们可以在使用响应式 API 时,利用 Vue 的依赖注入系统。
生命周期
生命周期的钩子(Hook)
这个是钩子不是生命周期
在捕获了后代组件传递的错误时调用。
错误可以从以下几个来源中捕获:
- 组件渲染
- 事件处理器
- 生命周期钩子
setup()
函数- 侦听器
- 自定义指令钩子
- 过渡钩子
你可以在 errorCaptured()
中更改组件状态来为用户显示一个错误状态。注意不要让错误状态再次渲染导致本次错误的内容,否则组件会陷入无限循环。
调试钩子,当组件渲染过程中追踪到响应式依赖时调用。
调试钩子,当响应式依赖的变更触发了组件渲染时调用。
onActivated() SSR 无效
若组件实例是 <KeepAlive>
缓存树的一部分,当组件被插入到 DOM 中时调用。
onDeactivated()SSR 无效
若组件实例是 <KeepAlive>
缓存树的一部分,当组件从 DOM 中被移除时调用。
异步函数,在组件实例在服务器上被渲染之前调用。SSR only
<script setup>
是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。
将被预处理为组件的 setup()
函数,这意味着它将为每一个组件实例都执行。
defineProps() 和 defineEmits()
为了在声明 props
和 emits
选项时获得完整的类型推导支持,我们可以使用 defineProps
和 defineEmits
defineExpose()
使用 <script setup>
的组件是默认关闭的——即通过模板引用或者 $parent
链获取到的组件的公开实例,不会暴露任何在 <script setup>
中声明的绑定。
可以通过 defineExpose
编译器宏来显式指定在 <script setup>
组件中要暴露出去的属性
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
defineExpose({
a,
b
})
//`{ a: number, b: number }` (ref 会和在普通实例中一样被自动解包)
</script>
defineOptions()
<script setup>
defineOptions({
inheritAttrs: false,
customOptions: {
/* ... */
}
})
</script>
defineSlots()
这个宏可以用于为 IDE 提供插槽名称和 props 类型检查的类型提示。
useSlots()
和 useAttrs()
在 <script setup>
使用 slots
和 attrs
的情况应该是相对来说较为罕见的,因为可以在模板中直接通过 $slots
和 $attrs
来访问它们。
顶层 await
<script setup>
中可以使用顶层 await
。结果代码会被编译成 async setup()
<script setup>
const post = await fetch(`/api/post/1`).then((r) => r.json())
</script>
限制
由于模块执行语义的差异,<script setup>
中的代码依赖单文件组件的上下文。当将其移动到外部的 .js
或者 .ts
文件中的时候,对于开发者和工具来说都会感到混乱。因此, <script setup>
不能和 src
attribute 一起使用。
全局API
createApp
解决了全局配置共享一个 Vue 副本
解决了污染其他测试用例
全局改变 Vue 行为的 API Vue3.x 移动到应用实例上
config.productionTip
移除
config.ignoredElements
替换为 config.isCustomElement
重点: Vue.prototype
替换为 config.globalProperties
// 之前 - Vue 2
Vue.prototype.$http = () => {}
// 之后 - Vue 3
const app = createApp({})
app.config.globalProperties.$http = () => {}
Vue.extend
移除
重点 使用 组合式 API 来替代继承与 mixin。
Vue.extend
vue2用于创建基于 Vue 构造函数“子类”,其参数为包含组件选项的对象。Vue 3.x 中使用 createApp
挂载组件
// 之前 - Vue 2
// 创建构造器
const Profile = Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data() {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 创建一个 Profile 的实例,并将它挂载到一个元素上
new Profile().$mount('#mount-point')
// 之后 - Vue 3
const Profile = {
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data() {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
}
Vue.createApp(Profile).mount('#mount-point')
使用Vue.use()会发出警告。必须在应用实例上显式指定插件如:
const app = createApp(MyApp)
app.use(VueRouter)
挂载 App 实例
const app = createApp(MyApp)
app.component('button-counter', {
data: () => ({
count: 0
}),
template: '<button @click="count++">Clicked {{ count }} times.</button>'
})
app.directive('focus', {
mounted: (el) => el.focus()
})
// 现在,所有通过 app.mount() 挂载的应用实例及其组件树,
// 将具有相同的 “button-counter” 组件和 “focus” 指令,
// 而不会污染全局环境
app.mount('#app')
Provide / Inject
在应用之间共享配置
创建工厂函数的好处是不用给每个实例分别插入指令或配置
import { createApp } from 'vue'
import Foo from './Foo.vue'
import Bar from './Bar.vue'
const createMyApp = (options) => {
const app = createApp(options)
app.directive('focus' /* ... */)
return app
}
createMyApp(Foo).mount('#foo')
createMyApp(Bar).mount('#bar')
全局 API具名导出与Treeshaking
Vue3 nextTick
等全局API支持 tree-shaking
使用方式如
import { nextTick } from 'vue'
nextTick(() => {
// 一些和 DOM 有关的东西
})
受此更改影响的API:
Vue.nextTick
Vue.observable
(用Vue.reactive
替换)Vue.version
Vue.compile
(仅完整构建版本)Vue.set
(仅兼容构建版本)Vue.delete
(仅兼容构建版本)
内部帮助器
内部组件/帮助器以具名方式导出。
编译器只在代码被使用到时才引入并输出它。
只有在应用实际使用了 Transition
组件它才会被导入。
<transition>
<div v-show="ok">hello</div>
</transition>
//将编译为类似于以下的内容:
import { h, Transition, withDirectives, vShow } from 'vue'
export function render() {
return h(Transition, [withDirectives(h('div', 'hello'), [[vShow, this.ok]])])
}
插件中的用法
如果使用了 webpack \rollup,可能会导致 Vue 的源代码输出打包到插件中。为了防止发生这种情况配置模块打包工具以将 Vue 从最终的打包产物中排除。
// webpack.config.js
module.exports = {
/*...*/
externals: {
vue: 'Vue'
}
}
// rollup.config.js
export default {
/*...*/
external: ['vue']
}
如果你的插件依赖到了受影响的 Vue 2.x 全局 API,在 Vue 3 中,必须显式导入例如:
import { nextTick } from 'vue'
const plugin = {
install: app => {
nextTick(() => {
// ...
})
}
}
函数式组件
2.x 中函数式组件带来的性能提升在 3.x 中已经可以忽略不计
import { h } from 'vue'
const DynamicHeading = (props, context) => {
return h(`h${props.level}`, context.attrs, context.slots)
}
DynamicHeading.props = ['level']
export default DynamicHeading
接收两个参数:props
和 context
。context
参数是一个对象,包含组件的 attrs
、slots
和 emit
property。
在 Vue 3 中,所有的函数式组件都是用普通函数创建的。换句话说,不需要定义 { functional: true }
组件选项。
单文件组件 (SFC)
在 3.x 中,有状态组件和函数式组件之间的性能差异已经大大减少,并且在大多数用例中是微不足道的。
<template>
<component
v-bind:is="`h${$props.level}`"
v-bind="$attrs"
/>
</template>
<script>
export default {
props: ['level']
}
</script>
转载自:https://juejin.cn/post/7277787934863048739