首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

【前端】一文讲清楚Vue 2→Vue 3 必须更新的知识点

  • 25-04-24 21:24
  • 2719
  • 6057
blog.csdn.net

不定期更新,建议关注收藏点赞。


目录

  • 用于迁移的构建版本(@vue/compat)
  • 组件注册与应用启动
  • 响应式系统升级(核心)
  • 废弃与不推荐再使用的功能
  • 性能提升和 TypeScript 支持更好
  • 生命周期钩子
  • Composition API 替代 Options API(推荐,但非强制)
  • 模版语法&绑定
  • 组件通信
  • 新语法推荐

从 Vue 2 升级到 Vue 3,确实有很多需要更新的地方,特别是那些已废弃、不推荐再使用的特性,一不注意就会踩坑。别再使用它们啦!

用于迁移的构建版本(@vue/compat)

Vue 提供了 @vue/compat 构建版本,旨在帮助开发者逐步迁移项目。
默认以 Vue 2 模式运行,大多数 API 行为保持一致。
使用已更改或废弃的特性时,会在运行时发出警告。
可按组件或全局配置兼容性特性。

  • 适用场景
    将 Vue 2 应用升级到 Vue 3。
    迁移库以支持 Vue 3。
    帮助 Vue 2 开发者了解两个版本之间的差异。​
  • 注意
    依赖于 Vue 2 内部 API 或未记录行为的项目可能无法顺利迁移。
    Vue 3 不再支持 IE11。
    服务器端渲染(SSR)配置需进行调整,例如使用 @vue/server-renderer 替代 vue-server-renderer。
  • 升级流程
    升级构建工具,如将 vue-loader 升级至 ^16.0.0。
    在 package.json 中,将 vue 更新到 3.1,并安装相同版本的 @vue/compat。npm install @vue/compat
    配置构建设置,为 vue 设置别名 @vue/compat,并通过 Vue 编译器选项开启兼容模式。
/*
如果你使用的是 Vue CLI,需要在项目根目录下的 
vue.config.js 文件中配置别名:
*/
// vue.config.js
module.exports = {
  chainWebpack: config => {
    config.resolve.alias.set('vue', '@vue/compat')
  }
}

/*
如果你使用的是 Vite,需要在 vite.config.js 中配置别名:
*/
// vite.config.js
import { defineConfig } from 'vite'

export default defineConfig({
  resolve: {
    alias: {
      vue: '@vue/compat'
    }
  }
})

//启用兼容模式
//在 main.js 或 main.ts 文件中
import { createApp } from 'vue'
import App from './App.vue'

// 使用 Vue 3 和兼容模式
createApp(App).config.compatConfig = {
  MODE: 2 // 开启兼容模式
}.mount('#app')

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

根据需要,配置全局或组件级的兼容性特性。

组件注册与应用启动

功能Vue 2Vue 3
创建实例new Vue({ … })createApp(App).mount(‘#app’)
注册全局组件Vue.component(‘MyComp’, Comp)app.component(‘MyComp’, Comp)
注册插件Vue.use(plugin)app.use(plugin)
注册混入(慎用)Vue.mixin(…)app.mixin(…)
注册指令Vue.directive(…)app.directive(…)
// Vue 3 启动方式:
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')
  • 1
  • 2
  • 3
  • 4
  • 5

app.mixin() 用来注册全局混入逻辑,会影响所有组件。它的作用是把某些通用的选项、生命周期钩子或方法混入到每一个组件中。但不会混入props、emits、setup。混入优先级低于组件自身定义。全局污染大,慎用!推荐用:局部 mixin(只混入给某几个组件)或者 组合函数 composables(Vue 3 更推荐)

//组合函数
// useLog.js
import { onMounted } from 'vue'

export function useLog() {
  onMounted(() => {
    console.log('组件 mounted')
  })
}

// MyComponent.vue
<script setup>
import { useLog } from './useLog'
useLog()
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

响应式系统升级(核心)

Vue 3 使用 ref 和 reactive 创建响应式变量,不再依赖老旧语法。

功能Vue 2Vue 3
响应式对象data() { return { x: 1 } }const state = reactive({ x: 1 })
响应式基本类型手动通过 data 实现const count = ref(0)
设置属性this.$set(obj, key, value)直接操作 reactive 对象
删除属性this.$delete(obj, key)直接 delete obj.key
新增/删除属性响应式不支持支持
数据驱动视图(data-driven UI)Object.definePropertyES6的Proxy
  • 数据驱动视图这里为啥升级
    Vue 会在你读取数据时自动追踪依赖,你修改数据时自动触发更新视图。
    Object.defineProperty 没有被废弃,它依然是 原生 JavaScript 的合法 API,而且在很多低层逻辑里依然有用。但vue 3 弃用了它作为响应式的核心方式,改用更强大的 Proxy。
场景Vue 2Vue 3
实现响应式使用 Object.defineProperty使用 Proxy
设置响应式对象属性必须 Vue.set(obj, ‘key’, val)直接操作即可 obj.key = val
  1. Vue 2:用 Object.defineProperty 实现响应式
    Vue 2 初始化时,会给每个属性都加上 getter 和 setter。
    缺点:新增/删除属性不会响应式, 无法监听数组的索引变化,深层嵌套需要递归处理,性能差
//js
const obj = {}
Object.defineProperty(obj, 'msg', {
  get() {
    console.log('有人访问 msg')
    return 'hello'
  },
  set(value) {
    console.log('msg 被改成了', value)
  }
})

obj.msg        // 打印:有人访问 msg
obj.msg = 'hi' // 打印:msg 被改成了 hi

//vue
// Vue 内部用 defineProperty 给 count 加上 getter/setter
data() {
  return {
    count: 0
  }
}
//原理
Object.defineProperty(data, 'count', {
  get() {
    // 收集依赖(谁用了我)
    return value
  },
  set(newVal) {
    // 通知视图更新
    render()
  }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

Object.defineProperty它只能劫持对象里已经存在的属性,不能动态监听“新增的东西”。所以vue2必须要手动加上Vue.set(obj, 'age', 18) // Vue 2 特有的“补丁”。所以 Vue 2 为了确保“所有属性都是响应式”,必须在初始化时就递归地给每一层加 getter/setter。

data() {
  return {
    user: {
      profile: {
        name: 'Tom'
      }
    }
  }
}
//如果 Vue 2 只给 user 加 getter/setter,
this.user.profile.name = 'Jerry'
// ❌ 不触发视图更新!因为 Vue 根本没劫持 profile 和 name

//所以vue2必须递归每一层 挨个加上监听
function observe(obj) {
  for (let key in obj) {
    defineReactive(obj, key, obj[key])

    if (typeof obj[key] === 'object') {
      observe(obj[key])  // ⬅️ 关键:递归每一层
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  1. Vue 3:用 Proxy 全面升级响应式
    优点:
    所有属性都能监听(包括新增 / 删除)
    无需递归,性能更优
    数组也能精准追踪变化
    更强大、现代,兼容性也没问题(IE 不支持 Proxy,但 Vue 3 本身也不支持 IE)
const state = reactive({ count: 0 })

//原理
const proxy = new Proxy(obj, {
  get(target, key) {
    track(target, key)   // 收集依赖
    return Reflect.get(target, key)
  },
  set(target, key, value) {
    target[key] = value
    trigger(target, key) // 触发更新
    return true
  }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

补充:什么是ES6 Proxy
Proxy 是 ES6 新特性,它可以整个“代理”一个对象,对所有操作都能拦截处理(增删改查都行)。Vue 3 就是靠 Proxy 实现新的响应式系统
优点:监听新增/删除属性;无需递归嵌套,一次性代理整个对象,性能高;数组索引变更也能监听;

const obj = { count: 0 }
const proxy = new Proxy(obj, {
  get(target, key) {
    console.log(`访问 ${key}`)
    return target[key]
  },
  set(target, key, value) {
    console.log(`修改 ${key} 为 ${value}`)
    target[key] = value
    return true
  }
})

proxy.count        // 打印:访问 count
proxy.count = 10   // 打印:修改 count 为 10

//Vue 3 用 Proxy 是这样处理嵌套对象的
const obj = {
  user: {
    name: 'Tom'
  }
}
const reactiveObj = reactive(obj)
// 当你访问 reactiveObj.user 时,才会给 user 创建 Proxy!

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

Proxy 不需要递归的关键:懒代理 (lazy observe)。它不在一开始递归所有层级,而是等你访问到那一层,再动态递归进去做代理,非常懒又聪明,性能爆表。

废弃与不推荐再使用的功能

功能状态替代
this.$set / Vue.set() /this.$delete❌ 废弃直接用 reactive 或 ref,delete
keyCode 修饰符(@keyup.enter)⚠️ 不推荐使用 @keyup.enter 字符
.sync 修饰符⚠️ 不推荐使用 v-model:propName 语法
$on, $off, $once❌ 废弃推荐使用事件总线库,emits+用 props/emit 或 mitt
filters(全局/局部)❌ 废弃推荐使用方法或计算属性代替computed/methods
inline-template❌ 废弃拆分成子组件
propsData(用于 new Vue 时)❌ 废弃用 createApp() 后传 props
手工总线事件eventBus = new Vue()❌ 废弃推荐用emits+ mitt或 props/emit
  • 不推荐再使用 keyCode 修饰符,理由不仅仅是“Vue 的风格改变”,而是更深层次的
    keyCode 是浏览器官方废弃的规范,event.keyCode 属于老旧的 DOM 事件标准,已经被 官方废弃。新的标准推荐使用 event.key 代替。
event.key === 'Enter'//推荐
event.keyCode === 13 //废弃 不具备可读性

<!-- Vue 2 写法 -->
<input @keydown.enter="onEnter" />
<input @keydown.13="onEnter" />  <!-- ✅ 但难读 -->

<!-- Vue 3 推荐写法 -->
<input @keydown.enter="onEnter" />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Vue 3 中仍支持 keyCode,但作为兼容功能存在。Vue 3 官方推荐使用 event.key 的形式:可以直接使用以下这些语义化的修饰符:
.enter
.tab
.delete
.esc / .escape
.space
.up / .down / .left / .right

  • prop(全称是 property)就是 父组件传给子组件的数据。
    .sync 修饰符是 Vue 2 中的一个经典“语法糖”,用于 子组件向父组件同步 prop 的值。在 Vue 3 中它也还支持,但使用场景和推荐方式略有不同。
//Vue 2 中没有 .sync 时
<!-- 父组件传值给子组件 -->
<Child :title="pageTitle" @update:title="val => pageTitle = val" />
//子组件内部这样触发:
this.$emit('update:title', '新标题')

//Vue 2 使用 .sync 写法更优雅
//.sync 其实是给你自动帮忙写了 @update:propName="..." 这一部分。
<Child :title.sync="pageTitle" />
//等于你写了:
:title="pageTitle"
@update:title="val => pageTitle = val"
//子组件内部触发同上
this.$emit('update:title', '新标题')

//Vue 3 推荐用 v-model 替代 .sync
<!-- 这是 Vue 3 中推荐的绑定子组件 prop 的方式 -->
<MyModal v-model:visible="isModalVisible" />
//子组件中:
// props: ['visible']
this.$emit('update:visible', false)//设置值为false
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

this.$emit() 是 Vue 实例的方法,用来 触发(发出)一个自定义事件,来自于 Vue.prototype,只要你是一个组件(export default {} 里的东西),就可以用this.$emit('事件名', 参数1, 参数2...)

  • $on / $emit / $off / $once 是 Vue 2 中组件通信或事件总线(EventBus)常用的写法。$off() 取消监听,或 $once() 只监听一次。
// event-bus.js
export const bus = new Vue()//这种手工事件总线 vue3已废弃

// A组件发消息
bus.$emit('custom-event', data)

// B组件监听消息
bus.$on('custom-event', (data) => { ... })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

为啥废弃?
它不够清晰明确(“谁发的谁听的?”难追踪)
全局事件多了容易乱、难维护
不符合 函数式组件通信风格
Vue 3 不是基于 Vue.prototype 创建组件实例的,所以没这些方法了

Vue 3 推荐的替代方式:

  1. 使用 props + emit 这是组件通信的官方推荐方式!
    组件更清晰;更容易自动提示 / 类型推导(对 TypeScript 支持也好);
<!-- 父组件 -->
<MyComponent @submit="handleSubmit" />

<!-- 子组件 -->
this.$emit('submit', data)

//Vue 3 新增emits:可以明确声明子组件会发什么事件
export default {
  emits: ['submit']
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

补充:emits 是 Vue 3 中专门用来 显式声明组件能触发哪些事件 的配置项。
就像你在 props 里声明你能“接收”哪些数据,emits 就是声明你能“发出”哪些事件。
优点:显式声明你这个组件会发出哪些事件、更利于维护和自动化(比如 eslint 检查、IDE 智能提示)、对typescript更友好

  1. 多组件/跨层通信,用第三方库:mitt
    优点:极简(仅 200 行)、更好组织、更强控制力
    全局通信慎用
npm i mitt //install

// event-bus.js
import mitt from 'mitt'
export const bus = mitt()

// A组件
bus.emit('save', data)
// B组件
bus.on('save', (data) => { ... })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

性能提升和 TypeScript 支持更好

更好的 Tree Shaking。
原生支持 TypeScript(setup + < script setup> 非常方便)。
更快的渲染和更小的包体积。

生命周期钩子

Composition API 中生命周期钩子改名了

生命周期Vue 2Vue 3(Composition API)
beforeCreatebeforeCreate()setup() 前运行,无等价钩子
createdcreated()setup() 里处理初始化逻辑
beforeMountbeforeMount()onBeforeMount()
mountedmounted()onMounted()
beforeUpdatebeforeUpdate()onBeforeUpdate()
updatedupdated()onUpdated()
beforeDestroybeforeDestroy()onBeforeUnmount()
destroyeddestroyed()onUnmounted()
//vue2
created() {
  console.log('组件创建')
}
//vue3
import { onMounted } from 'vue'

onMounted(() => {
  console.log('组件挂载')
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Composition API 替代 Options API(推荐,但非强制)

组合式替代了选项式风格
Vue 2 中,没有 .value,因为 Vue 2 使用的响应式系统与 Vue 3 不同,它直接使用 getter 和 setter 来实现数据绑定。

  • ref vs reactive:
    ref:用于创建 基本类型(如 string、number、boolean 等)的响应式引用。通过 .value 来访问和修改其值。如果在模板中直接使用名称没有.value,Vue 会自动解包 ref 并正确显示 x.value 的值。
    reactive:用于创建 对象和数组 的响应式引用。直接访问属性(无需 .value)。
  • 汇总
功能Vue 2Vue 3
响应式数据data()ref()、reactive()
方法methods: {}setup() { const fn = () => {} }
计算属性computed: {}computed(() => x.value + 1)
监听器watch: {}watch() / watchEffect()
生命周期钩子mounted() 等onMounted() 等详见上面章节
template 中使用this.countcount.value(注意 .value)
//vue2 类似于类的写法
export default {
  data() { return { count: 0 } },
  methods: {
    increment() { this.count++ }
  }
}

//vue3
import { ref } from 'vue'
export default {
  setup() {
    const count = ref(0)
    const increment = () => count.value++
    return { count, increment }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 计算属性(computed)是根据某些数据 自动计算出值 的方法,通常用来处理 复杂逻辑,并且 缓存结果,避免每次访问都重新计算。
  • Vue 3 中的 watch 和 watchEffect 主要是用来处理响应式数据变化时执行副作用的工具。它们在 Composition API 中尤其重要,因为它们是 Vue 3 中响应式系统的核心功能之一。

watch 的作用:watch 是一个 侦听器,用来观察一个或多个响应式数据的变化,触发一个副作用(通常是执行某些操作,如请求数据或修改其他数据)。
在 Vue 3 中,watch 是作为一个函数使用的,可以监听一个或多个响应式数据的变化。
在Vue 2 中。 watch 是“选项式 API”里一个配置项在 Vue 2 中,写在组件的配置对象里(比如 data, methods, computed 等并列),是一个 对象(Object),也就是我们说的“选项式 API”。

  • watch vs watchEffect
    watchEffect 是 Vue 3 中的另一个工具,和 watch 的区别在于它 自动收集依赖,只要你在函数体内使用响应式数据,它就会自动跟踪这些数据,并在这些数据发生变化时重新执行副作用。
    watchEffect 不需要显式指定监听的数据,它会自动收集依赖,只要你在回调函数内访问了响应式数据,它就会自动追踪。
    watchEffect 是 立即执行 的,监听的数据发生变化时会重新执行。
    适用于副作用代码,例如请求数据、计算值等。
特性watchwatchEffect
依赖收集手动指定要监听的数据自动收集依赖(自动追踪数据)
执行时机不立即执行,只有数据变化时才执行初次执行时立即执行,之后每次依赖变化时执行
用途适合处理 明确的数据监听,比如某个数据变化时执行特定操作适合处理 副作用操作,如数据变化触发的异步请求等
返回值返回停止侦听的函数(stop())返回一个停止侦听的函数(stop())

不能用watcheffect完全替换watch,这是因为:

特性watchEffect()watch()
是否自动追踪依赖✅ 自动❌ 手动指定
是否能拿到新旧值❌ 无法(没有 oldVal)✅ 能获取 (newVal, oldVal)
是否能监听多个数据✅ 可,但要写在 effect 内部✅ 明确传数组
是否可监听 getter 返回值✅ 自动✅ 明确写 getter
用途倾向✅ 处理副作用(如自动请求)✅ 处理逻辑清晰的监听需求
是否支持懒执行❌ 总是立即执行✅ 默认懒执行,可设 immediate: true

可以用 watchEffect 替代的情况:
适用于:
不关心具体新旧值,只要数据变了就触发逻辑
逻辑简单,函数体里自然访问了响应式变量
副作用自动触发即可,比如发请求、打印日志

在开发早期、快速调试时用 watchEffect。
后期逻辑明确或复杂时,切换为 watch。

//vue2
<template>
  <div>{{ count }}</div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  watch: {
    count(newVal, oldVal) {
      console.log(`count 从 ${oldVal} 变为 ${newVal}`);
    }
  }
}
</script>

//vue3
<template>
  <div>{{ count }}</div>
</template>

<script>
import { ref, watch } from 'vue';

export default {
  setup() {
    const count = ref(0);

    watch(count, (newVal, oldVal) => {
      console.log(`count 从 ${oldVal} 变为 ${newVal}`);
    });

    return {
      count
    };
  }
}
</script>

//watchEffect
<template>
  <div>{{ count }}</div>
</template>

<script>
import { ref, watchEffect } from 'vue';

export default {
  setup() {
    const count = ref(0);

    watchEffect(() => {
      console.log(`count 的值是: ${count.value}`);
    });

    return {
      count
    };
  }
}
</script>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

模版语法&绑定

功能Vue 2Vue 3
v-model 默认绑定v-model=“value”v-model=“modelValue”(等价)
多个 v-model不支持✅ v-model:title=“title”,多个 prop 可同时绑定
事件修饰符 .keyCode@keyup.13❌ 废弃,改用 @keyup.enter 等
.sync 修饰符✅ 用 v-model:title="msg" 替代
filters{{ msg | capitalize }}用计算属性(computed)或用方法(function)
  • {{ msg | capitalize }} 是 Vue 2 中的“过滤器(filters)语法”,但 在 Vue 3 中已被移除(废弃)。在 Vue 2 中,filters 是模板语法中的一个“管道式”语法,用于对数据进行格式化处理,类似于小型的数据处理函数。
<template>
  <p>{{ msg | capitalize }}</p>
</template>

<script>
export default {
  data() {
    return {
      msg: 'hello world'
    }
  },
  filters: {
    capitalize(value) {
      if (!value) return ''
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
  }
}
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

Vue 3 中为什么废弃 filters?官方理由:
不符合函数式编程思维:filters 看似语法糖,实则引入了“非显式依赖”。
容易误用:开发者容易把复杂逻辑放到 filters 中,使模板变得难以维护。
难以移植与复用:filters 是模板专用,不适用于 JS 或 setup 中逻辑。
Composition API 中移除 filters。

  • Vue 3 Composition API 的核心思想是:
    所有逻辑都可以复用、组合、可测试、可类型推导
    强调:逻辑 = 函数化,不要“藏”在模板里。
    而 filters:
特点为什么不被推荐
模板专属语法无法在 JS 代码中使用
不利于逻辑复用只能在当前组件中用
不透明IDE 不好识别、提示
和 setup() 不兼容setup 函数中没地方注册 filters
不利于类型检查缺少显式声明方式
存在更优替代方案用 computed / 函数更清晰

组件通信

功能Vue 2Vue 3
Props 传参props: [‘title’]props: [‘title’](保持不变)
子组件触发事件this.$emit(‘xxx’)没废弃,但适用选项式APIdefineEmits([‘xxx’])推荐组合式API使用
父组件监听事件(保持不变)
$on, $off✅ 可用❌ 已废弃,推荐使用 mitt 等第三方库

新语法推荐

  1. v-model 语法升级
  2. Fragment 支持(一个组件可以返回多个根节点)
<!-- Vue 3 终于不用写额外的 <div> 包裹了 -->
<template>
  <h1>Hello</h1>
  <p>World</p>
</template>
  • 1
  • 2
  • 3
  • 4
  • 5
功能Vue 2Vue 3 推荐写法
单文件组件