Vue 3 API 完全指南:从 Options 到 Composition 的进阶之路

Vue 3 API 完全指南:从 Options 到 Composition 的进阶之路

一份覆盖 Vue 3 全部核心 API 的深度文档------Composition API、响应式系统、生命周期、编译宏、内置组件、v-model 进化,附大量可直接复制的 Vue 2/3 对比代码。


目录

  1. [引言:Vue 3 带来了什么](#引言:Vue 3 带来了什么 "#%E5%BC%95%E8%A8%80")
  2. [Composition API vs Options API:范式之变](#Composition API vs Options API:范式之变 "#%E4%B8%80composition-api-vs-options-api%E8%8C%83%E5%BC%8F%E4%B9%8B%E5%8F%98")
  3. [响应式系统:从 defineProperty 到 Proxy](#响应式系统:从 defineProperty 到 Proxy "#%E4%BA%8C%E5%93%8D%E5%BA%94%E5%BC%8F%E7%B3%BB%E7%BB%9F%E4%BB%8E-defineproperty-%E5%88%B0-proxy")
  4. 生命周期钩子:新旧对照
  5. [script setup 编译宏详解](#script setup 编译宏详解 "#%E5%9B%9Bscript-setup-%E7%BC%96%E8%AF%91%E5%AE%8F%E8%AF%A6%E8%A7%A3")
  6. [新增内置组件:Teleport / Suspense / Fragment](#新增内置组件:Teleport / Suspense / Fragment "#%E4%BA%94%E6%96%B0%E5%A2%9E%E5%86%85%E7%BD%AE%E7%BB%84%E4%BB%B6teleport--suspense--fragment")
  7. [v-model 的进化](#v-model 的进化 "#%E5%85%ADv-model-%E7%9A%84%E8%BF%9B%E5%8C%96")
  8. [其他重要 API 变更](#其他重要 API 变更 "#%E4%B8%83%E5%85%B6%E4%BB%96%E9%87%8D%E8%A6%81-api-%E5%8F%98%E6%9B%B4")
  9. [迁移实战:从 Vue 2 平滑升级](#迁移实战:从 Vue 2 平滑升级 "#%E5%85%AB%E8%BF%81%E7%A7%BB%E5%AE%9E%E6%88%98%E4%BB%8E-vue-2-%E5%B9%B3%E6%BB%91%E5%8D%87%E7%BA%A7")
  10. 总结

引言:Vue 3 带来了什么

Vue 3 是 Vue.js 框架迄今为止最重要的一次重写。它不仅仅是 API 的增减,而是从底层响应式引擎到上层开发体验的全面革新。以下是 Vue 3 最核心的几大变化:

01 Composition API --- 全新的逻辑组织和复用模式,解决 Options API 在大型组件中代码分散、逻辑复用困难的问题。

02 Proxy 响应式 --- 用 Proxy 替代 Object.defineProperty,实现全属性监听,消除 Vue 2 的响应式盲区。

03 性能提升 --- 包体积减少约 40%,首屏渲染提升 30%+,更新性能提升约 40%,内存占用降低约 60%。

04 TypeScript 原生支持 --- Vue 3 源码用 TypeScript 重写,类型推断更完善,<script setup> 中无需额外类型声明。

Vue 2 与 Vue 3 性能对比(Vue 2 基准 = 100):

指标 Vue 2 Vue 3 提升幅度
包体积 (min+gzip) 20KB 12KB -40%
首屏渲染速度 100 130 +30%
更新性能 100 140 +40%
内存占用 100 60 -40%

一、Composition API vs Options API:范式之变

1.1 Options API 的痛点

Vue 2 的 Options API 通过 datamethodscomputedwatch 等选项组织代码。在小型组件中很直观,但随着组件变大,同一功能的代码被拆散到不同选项中,阅读时需要反复跳转。

更关键的问题是逻辑复用。Vue 2 的 mixin 虽然能复用逻辑,但存在命名冲突、数据来源不清晰、类型推断弱等问题。

1.2 Composition API 的设计理念

Composition API 的核心思想是:按功能逻辑组织代码,而不是按选项类型。相关联的响应式数据、计算属性、方法和监听器放在一起,形成一个内聚的逻辑单元。

Vue 2 Options API:

javascript 复制代码
export default {
  data() {
    return {
      count: 0,
      user: null,
    }
  },
  computed: {
    doubleCount() {
      return this.count * 2
    },
    userName() {
      return this.user?.name ?? '未登录'
    }
  },
  methods: {
    increment() {
      this.count++
    },
    fetchUser() { /* ... */ }
  },
  watch: {
    count(val) {
      console.log('count changed', val)
    }
  }
}

Vue 3 Composition API:

javascript 复制代码
import { ref, computed, watch } from 'vue'

// 计数器逻辑 ------ 内聚在一起
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
  count.value++
}
watch(count, (val) => {
  console.log('count changed', val)
})

// 用户逻辑 ------ 独立单元
const user = ref(null)
const userName = computed(() =>
  user.value?.name ?? '未登录'
)
function fetchUser() { /* ... */ }

可以看到,在 Composition API 中,计数器逻辑和用户逻辑各自独立,不再被拆散到 data、computed、methods、watch 中。如果需要复用计数器逻辑,直接提取成一个函数即可:

javascript 复制代码
// composables/useCounter.js
import { ref, computed } from 'vue'

export function useCounter(initial = 0) {
  const count = ref(initial)
  const double = computed(() => count.value * 2)
  function increment() { count.value++ }
  function decrement() { count.value-- }
  function reset() { count.value = initial }
  return { count, double, increment, decrement, reset }
}

// 在任意组件中使用
const { count, double, increment } = useCounter(10)

💡 Composables vs Mixins

Composition API 的逻辑复用方式叫做 Composables(组合式函数)。相比 Mixins,它有明确的来源、不会命名冲突、TypeScript 类型推断完美,是目前 Vue 官方推荐的逻辑复用方案。

1.3 <script setup> 语法糖

<script setup> 是 Vue 3.2 引入的编译期语法糖,它是目前编写组件的推荐方式。所有顶层变量和函数自动暴露给模板,无需 return。

标准 setup() 函数:

vue 复制代码
<script>
import { ref } from 'vue'

export default {
  setup() {
    const msg = ref('Hello')
    return { msg }
  }
}
</script>

<script setup>

vue 复制代码
<script setup>
import { ref } from 'vue'

const msg = ref('Hello')
// 自动暴露给模板,无需 return
</script>

Options API 与 Composition API 多维度对比:

维度 Options API (Vue 2) Composition API (Vue 3)
代码组织 6/10 9/10
逻辑复用 5/10 9.5/10
TypeScript 支持 4/10 9.5/10
Tree-shaking 3/10 9/10
学习曲线(易上手度) 9/10 6/10
打包体积 5/10 9/10

二、响应式系统:从 defineProperty 到 Proxy

2.1 底层原理对比

Vue 2 使用 Object.defineProperty 实现响应式,它有已知的三个盲区

  • 无法检测属性的新增和删除(需要 Vue.set / Vue.delete
  • 无法检测数组通过索引修改元素(arr[0] = newValue 不触发更新)
  • 无法检测数组长度修改(arr.length = 0 不触发更新)

Vue 3 使用 Proxy 实现响应式,一举消除了以上所有盲区,且性能更好(初始化时不需要递归遍历所有属性)。

javascript 复制代码
// Vue 3 响应式核心原理(简化版)
function reactive(target) {
  return new Proxy(target, {
    get(obj, key) {
      track(obj, key)  // 收集依赖
      return Reflect.get(obj, key)
    },
    set(obj, key, value) {
      const result = Reflect.set(obj, key, value)
      trigger(obj, key)  // 触发更新
      return result
    },
    deleteProperty(obj, key) {
      const result = Reflect.deleteProperty(obj, key)
      trigger(obj, key)
      return result
    }
  })
}

2.2 ref 与 reactive

Vue 3 提供了两种创建响应式数据的方式,适用场景不同:

特性 ref() reactive()
支持类型 任意类型(原始值 + 对象) 仅对象/数组
访问方式 需要 .value(模板中自动解包) 直接访问属性
解构响应性 解构后仍保持响应 解构后失去响应(需 toRefs)
整体替换 直接赋值 ref.value = newVal 不能整体替换,只能改属性
推荐场景 原始值、需要替换整体的对象 表单、配置对象等结构性数据
javascript 复制代码
import { ref, reactive } from 'vue'

// ref:适合原始值和需要整体替换的场景
const count = ref(0)
count.value++              // JS 中需要 .value
// <span>{{ count }}</span>  模板中自动解包,不需要 .value

const list = ref([1, 2, 3])
list.value = [4, 5, 6]    // 整体替换,保持响应性

// reactive:适合对象/数组,直接操作属性
const form = reactive({
  name: '',
  age: 0,
  hobbies: []
})
form.name = '张三'         // 直接赋值,响应式
form.hobbies.push('coding')  // 数组操作也响应式

⚠️ reactive 的解构陷阱

解构 reactive 对象会丢失响应性 。如果需要解构,必须使用 toRefs 包装:

javascript 复制代码
import { reactive, toRefs } from 'vue'

const state = reactive({
  name: '张三',
  age: 25
})

// ❌ 解构后失去响应性
const { name, age } = state

// ✅ 使用 toRefs 保持响应性
const { name, age } = toRefs(state)

2.3 computed 计算属性

Vue 3 的 computed 是一个函数,接收一个 getter 函数或一个带 get/set 的对象。

javascript 复制代码
import { ref, computed } from 'vue'

const firstName = ref('张')
const lastName = ref('三')

// 只读计算属性
const fullName = computed(() => `${firstName.value}${lastName.value}`)

// 可写计算属性
const fullName2 = computed({
  get() {
    return `${firstName.value}${lastName.value}`
  },
  set(newValue) {
    // 注意:中文姓名拆分仅为示例
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})

2.4 watch 与 watchEffect

特性 watch watchEffect
监听方式 显式指定监听源 自动收集依赖
初始执行 不执行(可设 immediate: true) 立即执行一次
获取旧值 可以 不可以
适用场景 需要在值变化时执行副作用,且需要旧值 只需根据依赖执行副作用,不关心旧值
javascript 复制代码
import { ref, watch, watchEffect } from 'vue'

const keyword = ref('')
const page = ref(1)

// watch:显式监听,可获取新旧值
watch(keyword, (newVal, oldVal) => {
  console.log(`关键词从 ${oldVal} 变为 ${newVal}`)
  search(newVal)
})

// 监听多个源
watch([keyword, page], ([newKeyword, newPage], [oldKeyword, oldPage]) => {
  fetchList(newKeyword, newPage)
})

// watchEffect:自动收集依赖,立即执行
watchEffect(() => {
  // 自动检测到 keyword 和 page 的使用
  fetchList(keyword.value, page.value)
})

// watch 高级选项
watch(keyword, doSearch, {
  immediate: true,      // 立即执行一次
  flush: 'post',        // 在 DOM 更新后执行
  once: true            // 只执行一次(Vue 3.4+)
})

💡 监听 reactive 对象的注意事项

监听 reactive 对象的某个属性时,需要用 getter 函数:watch(() => state.name, callback)。直接写 watch(state.name, ...) 不会工作,因为传递的是值而非响应式引用。

2.5 toRef / toRefs / toRaw

javascript 复制代码
import { reactive, toRef, toRefs, toRaw } from 'vue'

const state = reactive({
  name: '张三',
  age: 25
})

// toRef:将对象的某个属性转为 ref,保持双向响应
const nameRef = toRef(state, 'name')
nameRef.value = '李四'  // state.name 也变为 '李四'

// toRefs:将对象所有属性转为 ref,常用于解构
const { name, age } = toRefs(state)

// toRaw:获取响应式对象的原始对象(脱离响应式)
const raw = toRaw(state)
console.log(raw === state)  // false,raw 是原始对象

2.6 shallowRef / shallowReactive

当处理大型数据结构时,深层响应式会带来性能开销。Vue 3 提供浅层响应式 API:

javascript 复制代码
import { shallowRef, shallowReactive, triggerRef } from 'vue'

// shallowRef:只有 .value 替换才触发更新
const bigList = shallowRef([])
bigList.value.push('item')  // ❌ 不会触发更新
bigList.value = [...bigList.value, 'item']  // ✅ 触发更新

// 手动触发浅层 ref 的更新
bigList.value.push('item2')
triggerRef(bigList)  // 手动通知更新

// shallowReactive:只有根级属性是响应式的
const obj = shallowReactive({
  info: { name: '张三' }
})
obj.info = { name: '李四' }  // ✅ 触发更新(根级)
obj.info.name = '王五'       // ❌ 不触发更新(深层)

三、生命周期钩子:新旧对照

Vue 3 将生命周期钩子改为了函数形式,名称也有调整。主要变化:beforeDestroyonBeforeUnmountdestroyedonUnmountedbeforeCreatecreatedsetup() 本身替代。

Vue 2 Vue 3 (Composition API) 说明
beforeCreate setup() 开头 组件实例创建前,setup 替代
created setup() 中 组件实例创建后,setup 替代
beforeMount onBeforeMount DOM 挂载前
mounted onMounted DOM 挂载后
beforeUpdate onBeforeUpdate 数据更新前
updated onUpdated 数据更新后
beforeDestroy onBeforeUnmount 组件卸载前(重命名)
destroyed onUnmounted 组件卸载后(重命名)
--- onErrorCaptured 捕获后代组件错误(新增)
--- onActivated keep-alive 激活时
--- onDeactivated keep-alive 停用时
--- onServerPrefetch SSR 专属(新增)
vue 复制代码
<script setup>
import {
  onBeforeMount, onMounted,
  onBeforeUpdate, onUpdated,
  onBeforeUnmount, onUnmounted,
  onActivated, onDeactivated,
  onErrorCaptured
} from 'vue'

// setup() 本身等同于 beforeCreate + created
console.log('setup 执行 = beforeCreate + created')

onBeforeMount(() => {
  console.log('DOM 即将挂载')
})

onMounted(() => {
  console.log('DOM 已挂载,可以操作 DOM')
})

onBeforeUpdate(() => {
  console.log('数据即将更新')
})

onUpdated(() => {
  console.log('数据已更新,DOM 已重新渲染')
})

onBeforeUnmount(() => {
  console.log('组件即将卸载,清理定时器等')
})

onUnmounted(() => {
  console.log('组件已卸载')
})

// keep-alive 相关
onActivated(() => console.log('从缓存激活'))
onDeactivated(() => console.log('移入缓存'))

// 错误捕获
onErrorCaptured((err, instance, info) => {
  console.error('子组件错误:', err)
  return false  // 阻止错误继续向上传播
})
</script>

四、<script setup> 编译宏详解

<script setup> 中,Vue 提供了几个编译宏(编译期处理,不需要 import)来定义组件的 props、emits 等。

4.1 defineProps

vue 复制代码
<script setup>
// 运行时声明
const props = defineProps({
  title: String,
  count: {
    type: Number,
    default: 0,
    required: false,
    validator: (val) => val >= 0
  }
})

// TypeScript 类型声明(推荐)
const props = defineProps<{
  title: string
  count?: number
  items?: string[]
}>()

// 带默认值的 TS 声明(Vue 3.5+ 使用 withDefaults)
const props = withDefaults(defineProps<{
  title: string
  count?: number
  items?: string[]
}>(), {
  count: 0,
  items: () => ['默认项']
})
</script>

4.2 defineEmits

vue 复制代码
<script setup>
// 运行时声明
const emit = defineEmits(['change', 'submit'])
emit('change', newValue)

// TypeScript 类型声明(推荐)
const emit = defineEmits<{
  (e: 'change', value: string): void
  (e: 'submit', data: { name: string; age: number }): void
}>()
emit('submit', { name: '张三', age: 25 })

// Vue 3.3+ 更简洁的语法
const emit = defineEmits<{
  change: [value: string]
  submit: [data: { name: string; age: number }]
}>()
</script>

4.3 defineExpose

使用 <script setup> 的组件默认是封闭的 ------父组件通过 ref 无法访问子组件的内部变量。需要用 defineExpose 显式暴露。

vue 复制代码
<!-- 子组件 Child.vue -->
<script setup>
import { ref } from 'vue'

const count = ref(0)
const reset = () => { count.value = 0 }

// 暴露给父组件
defineExpose({ count, reset })
</script>
vue 复制代码
<!-- 父组件 -->
<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'

const childRef = ref()

onMounted(() => {
  console.log(childRef.value.count)  // 0
  childRef.value.reset()          // 调用子组件方法
})
</script>

<template>
  <Child ref="childRef" />
</template>

4.4 useSlots 与 useAttrs

vue 复制代码
<script setup>
import { useSlots, useAttrs } from 'vue'

const slots = useSlots()
const attrs = useAttrs()

// 判断是否传入了具名插槽
console.log(slots.header ? '有 header 插槽' : '无 header 插槽')

// 获取透传的属性(class、id、data-* 等)
console.log(attrs.class, attrs.id)
</script>

五、新增内置组件:Teleport / Suspense / Fragment

5.1 Teleport------传送门

Teleport 可以将组件的 DOM 渲染到指定位置,常见于弹窗、通知、下拉菜单等需要脱离当前 DOM 层级的场景。

vue 复制代码
<template>
  <button @click="showModal = true">打开弹窗</button>

  <!-- 将弹窗传送到 body 下,避免被父级 overflow/z-index 影响 -->
  <Teleport to="body">
    <div v-if="showModal" class="modal">
      <h2>弹窗标题</h2>
      <p>弹窗内容</p>
      <button @click="showModal = false">关闭</button>
    </div>
  </Teleport>
</template>

<script setup>
import { ref } from 'vue'
const showModal = ref(false)
</script>

💡 Teleport 的 to 属性

to 接受 CSS 选择器字符串或 DOM 元素。如果目标元素不存在,会抛出警告。也可以用 :disabled="true" 禁用传送,让内容回到原位。

5.2 Suspense------异步组件加载

Suspense 用于协调异步组件的加载状态,在异步组件就绪前显示 fallback 内容。

vue 复制代码
<template>
  <Suspense>
    <!-- 异步组件 -->
    <template #default>
      <AsyncDashboard />
    </template>

    <!-- 加载中状态 -->
    <template #fallback>
      <div class="loading">加载中...</div>
    </template>
  </Suspense>
</template>

<script setup>
import { defineAsyncComponent } from 'vue'

// 异步加载组件
const AsyncDashboard = defineAsyncComponent(() =>
  import('./Dashboard.vue')
)
</script>

Suspense 也可以配合异步 setup() 使用:

vue 复制代码
<!-- AsyncDashboard.vue ------ 异步 setup -->
<script setup>
import { ref } from 'vue'

// 顶层 await,组件变成异步组件
const data = await fetch('/api/dashboard').then(r => r.json())

const stats = ref(data)
</script>

⚠️ Suspense 仍是实验性功能

截至Vue 3 最新版本,Suspense 的 API 已趋于稳定但官方仍标记为实验性。生产环境使用时需注意错误处理------可以用 onErrorCaptured 捕获异步组件的加载错误。

5.3 Fragment------多根节点

Vue 2 要求模板必须有且仅有一个根节点。Vue 3 移除了这个限制,组件可以有多个根节点(Fragment)。

Vue 2:必须单根

vue 复制代码
<template>
  <div class="wrapper">
    <header>...</header>
    <main>...</main>
    <footer>...</footer>
  </div>
</template>

Vue 3:多根节点

vue 复制代码
<template>
  <header>...</header>
  <main>...</main>
  <footer>...</footer>
</template>

六、v-model 的进化

Vue 3 对 v-model 做了重要调整,核心变化是支持多个 v-model 绑定,并且统一了 prop 和 event 的命名。

特性 Vue 2 Vue 3
默认 prop 名 value modelValue
默认 event 名 input update:modelValue
多个 v-model 不支持(需用 .sync) 原生支持
.sync 修饰符 支持 移除(被多 v-model 替代)
自定义修饰符 不支持 支持

6.1 基本用法

Vue 2:

html 复制代码
<!-- 父组件 -->
<Child v-model="text" />
<!-- 等同于 -->
<Child
  :value="text"
  @input="text = $event"
/>

<!-- 子组件 -->
<script>
export default {
  props: ['value'],
  methods: {
    update(val) {
      this.$emit('input', val)
    }
  }
}
</script>

Vue 3:

vue 复制代码
<!-- 父组件 -->
<Child v-model="text" />
<!-- 等同于 -->
<Child
  :modelValue="text"
  @update:modelValue="text = $event"
/>

<!-- 子组件 -->
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

function update(val) {
  emit('update:modelValue', val)
}
</script>

6.2 多个 v-model 绑定

这是 Vue 3 最大的改进之一------一个组件可以绑定多个 v-model:

vue 复制代码
<!-- 父组件:同时绑定 firstName 和 lastName -->
<UserForm
  v-model:firstName="form.firstName"
  v-model:lastName="form.lastName"
/>

<!-- 子组件 UserForm.vue -->
<script setup>
const props = defineProps({
  firstName: String,
  lastName: String
})
const emit = defineEmits(['update:firstName', 'update:lastName'])
</script>

<template>
  <input
    :value="firstName"
    @input="emit('update:firstName', $event.target.value)"
  />
  <input
    :value="lastName"
    @input="emit('update:lastName', $event.target.value)"
  />
</template>

6.3 自定义修饰符

vue 复制代码
<!-- 父组件:使用 trim 修饰符 -->
<MyInput v-model.trim="text" />

<!-- 子组件 MyInput.vue -->
<script setup>
const props = defineProps({
  modelValue: String,
  modelModifiers: { default: () => ({}) }
})

const emit = defineEmits(['update:modelValue'])

function handleInput(e) {
  let value = e.target.value
  if (props.modelModifiers.trim) {
    value = value.trim()
  }
  emit('update:modelValue', value)
}
</script>

七、其他重要 API 变更

7.1 createApp 替代 new Vue

Vue 2:

javascript 复制代码
import Vue from 'vue'
import App from './App.vue'

new Vue({
  render: h => h(App)
}).$mount('#app')

// 全局注册
Vue.component('MyComp', MyComp)
Vue.directive('focus', { ... })
Vue.use(MyPlugin)

Vue 3:

javascript 复制代码
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)
app.mount('#app')

// 应用级注册(不影响其他 app)
app.component('MyComp', MyComp)
app.directive('focus', { ... })
app.use(MyPlugin)

📌 为什么用 createApp

Vue 2 的全局 API(Vue.componentVue.use 等)会污染全局状态,导致多个 Vue 应用互相影响。Vue 3 的 createApp 返回独立的应用实例,每个实例有自己的配置,真正做到应用隔离

7.2 provide / inject

Vue 3 的 provide/inject 升级为函数形式,且支持 TypeScript 类型安全。

vue 复制代码
<!-- 父组件 -->
<script setup>
import { provide, ref, readonly } from 'vue'

const theme = ref('dark')
const setTheme = (val) => { theme.value = val }

// 提供只读数据 + 修改方法,防止子组件直接修改
provide('theme', readonly(theme))
provide('setTheme', setTheme)
</script>
vue 复制代码
<!-- 子/孙组件 -->
<script setup>
import { inject } from 'vue'

const theme = inject('theme', 'light')  // 第二个参数是默认值
const setTheme = inject('setTheme')

// 使用 InjectionKey 实现类型安全(TS 项目推荐)
import type { InjectionKey, Ref } from 'vue'
const themeKey: InjectionKey<Ref<string>> = Symbol('theme')
const theme = inject(themeKey)  // 类型自动推断为 Ref<string> | undefined
</script>

7.3 移除的特性一览

移除的特性 Vue 2 用法 Vue 3 替代方案
过滤器 filter `{{ price formatPrice }}`
事件总线 new Vue() 作为 EventBus 使用 mitt 等第三方库
.sync 修饰符 :title.sync="text" v-model:title="text"
on/on / on/off / $once this.$on('event', fn) 使用 mitt / provide+inject
$children this.$children[0] 使用 $refs 或 template refs
$listeners v-on="$listeners" 合并到 $attrs 中
$scopedSlots this.$scopedSlots.default 统一为 $slots
函数式组件语法 functional: true 普通函数组件,移除 functional 选项

7.4 自定义指令变化

javascript 复制代码
// Vue 2 指令钩子
export default {
  bind(el, binding) { },        // 指令第一次绑定到元素
  inserted(el, binding) { },    // 元素插入父节点
  update(el, binding) { },      // 所在组件 VNode 更新
  componentUpdated() { },
  unbind(el, binding) { }       // 解绑
}

// Vue 3 指令钩子(与组件生命周期对齐)
export default {
  created(el, binding) { },         // 元素属性和事件监听设置之前
  beforeMount(el, binding) { },     // 挂载前
  mounted(el, binding) { },          // 挂载后
  beforeUpdate(el, binding) { },    // 更新前
  updated(el, binding) { },          // 更新后
  beforeUnmount(el, binding) { },   // 卸载前
  unmounted(el, binding) { }         // 卸载后
}

八、迁移实战:从 Vue 2 平滑升级

从 Vue 2 升级到 Vue 3 不是简单替换版本号。以下是推荐的迁移路径:

  1. 升级到 Vue 2.7 :Vue 2.7 已经内置了 Composition API、<script setup> 等核心特性,可以在 Vue 2 项目中提前使用新 API,降低迁移成本
  2. 移除废弃特性 :用迁移构建版本(@vue/compat)运行项目,它会警告所有不兼容的用法
  3. 替换第三方库:Vuex → Pinia、Vue Router 3 → 4、Element UI → Element Plus 等
  4. 逐步重写组件:不必一次性全部改成 Composition API,Options API 在 Vue 3 中仍然支持
  5. 升级到 Vue 3 :移除 @vue/compat,正式切换到 Vue 3

💡 迁移建议

不要为了迁移而迁移。如果你的 Vue 2 项目运行良好且没有维护压力,可以继续使用。但如果要启动新项目,毫不犹豫地选择 Vue 3 + <script setup> + TypeScript + Pinia,这是目前的最佳实践技术栈。

生态库对应关系:

功能 Vue 2 生态 Vue 3 生态
路由 Vue Router 3 Vue Router 4
状态管理 Vuex 3 / Vuex 4 Pinia(官方推荐)
构建工具 Vue CLI Vite(官方推荐)
UI 组件库 Element UI / Ant Design Vue 1.x Element Plus / Ant Design Vue 3+ / Naive UI
DevTools Vue DevTools (beta) Vue DevTools 6+
SSR Nuxt 2 Nuxt 3

总结

Vue 3 不是 Vue 2 的小版本升级,而是一次从底层到开发范式的全面革新 。Composition API 解决了逻辑组织和复用的痛点,Proxy 响应式消除了所有盲区,<script setup> 让代码更简洁,Teleport / Suspense / 多 v-model 等新特性大幅扩展了表达能力。

迁移到 Vue 3 意味着拥抱更好的 TypeScript 支持、更小的包体积、更快的渲染性能和更灵活的代码组织方式。虽然学习曲线略有上升,但一旦掌握 Composition API 的思维模式,你会发现开发体验有了质的飞跃。

📌 核心要点回顾

  1. Composition API 按功能逻辑组织代码,用 Composables 替代 Mixins
  2. ref 用于原始值和需整体替换的对象,reactive 用于结构性数据
  3. watch 获取新旧值,watchEffect 自动收集依赖
  4. <script setup> + 编译宏是编写组件的推荐方式
  5. Teleport 解决 DOM 层级问题,Suspense 处理异步加载
  6. 多 v-model 让双向绑定更灵活
  7. createApp 实现应用隔离,全局 API 不再污染

如果这篇文章对你有帮助,欢迎点赞收藏。有任何问题或补充,欢迎在评论区交流。

相关推荐
名字还没想好☜1 小时前
React 用 Portal + Context 实现全局 Toast:一次调用、自动消失与队列管理
前端·javascript·react.js·react·next.js
HjhIron1 小时前
React Router 通关指南:从入门到鉴权,一个项目全搞定
前端
kyson_1 小时前
如何做一款自适应的数据大屏:`vw/vh`、`rem`、`scale` 应该怎么选?
前端
雪隐1 小时前
WPF + MVVM 实战系列02-告别 INPC 手写时代,做个体面的现代 WPF 人
前端·后端·c#
肉肉不吃 肉1 小时前
无渲染组件和组合式函数
前端·javascript·vue.js
其美杰布-富贵-李1 小时前
01 从项目结构开始认识 Vue 3
javascript·vue.js·ecmascript
未来智慧谷1 小时前
从 Atlas 关停复盘:AI 应用的三种载体形态怎么选(独立应用 / 浏览器扩展 / 桌面宿主)
前端·人工智能·ai·架构·浏览器
sugar__salt1 小时前
前端路由技术详解:从传统多页到 React SPA 的完整进化之路
前端·javascript·react.js·前端框架
Canace2 小时前
Harness Engineering 到底在做什么
前端·人工智能·ai编程