// Vue2
import Vue from 'vue'
import App from './App.vue'
new Vue({
render: h => h(App)
}).$mount('#app')
// Vue3
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
<!-- Vue2 -->
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
data() {
return { message: 'Hello' }
},
mounted() {
console.log('mounted')
}
}
</script>
<!-- Vue3 Options API(兼容) -->
<script>
export default {
data() {
return { message: 'Hello' }
},
mounted() {
console.log('mounted')
}
}
</script>
<!-- Vue3 Composition API(推荐) -->
<script setup>
import { ref, onMounted } from 'vue'
const message = ref('Hello')
onMounted(() => {
console.log('mounted')
})
</script>
// Vue2
export default {
data() {
return {
count: 0,
user: { name: '张三' },
list: [1, 2, 3]
}
},
methods: {
addCount() {
this.count++
},
addUserProp() {
this.$set(this.user, 'age', 18) // 需要 $set
},
updateListItem() {
this.$set(this.list, 0, 100) // 需要 $set
}
}
}
// Vue3 Options API
export default {
data() {
return {
count: 0,
user: { name: '张三' },
list: [1, 2, 3]
}
},
methods: {
addCount() {
this.count++ // 直接修改
},
addUserProp() {
this.user.age = 18 // 直接添加,无需 $set
},
updateListItem() {
this.list[0] = 100 // 直接修改索引
}
}
}
// Vue3 Composition API
<script setup>
import { reactive, ref } from 'vue'
const count = ref(0) // 基本类型用 ref
const user = reactive({ name: '张三' }) // 对象用 reactive
const list = ref([1, 2, 3])
const addCount = () => count.value++ // ref 需要 .value
const addUserProp = () => user.age = 18 // 直接添加
const updateListItem = () => list.value[0] = 100
</script>
// Vue2
export default {
data() {
return { firstName: '张', lastName: '三' }
},
computed: {
fullName() {
return this.firstName + this.lastName
}
},
watch: {
firstName(newVal, oldVal) {
console.log(newVal)
}
}
}
// Vue3 Composition API
<script setup>
import { ref, computed, watch } from 'vue'
const firstName = ref('张')
const lastName = ref('三')
const fullName = computed(() => firstName.value + lastName.value)
watch(firstName, (newVal, oldVal) => {
console.log(newVal)
})
// 深度监听
watch(() => user.value, (newVal) => {
console.log(newVal)
}, { deep: true })
</script>
<!-- Vue2 父组件 -->
<template>
<Child :title="title" @update="handleUpdate" />
</template>
<script>
import Child from './Child.vue'
export default {
components: { Child },
data() {
return { title: '标题' }
},
methods: {
handleUpdate(val) {
this.title = val
}
}
}
</script>
<!-- Vue2 子组件 -->
<script>
export default {
props: {
title: { type: String, required: true }
},
methods: {
changeTitle() {
this.$emit('update', '新标题')
}
}
}
</script>
<!-- Vue3 父组件(支持多个 v-model) -->
<template>
<Child v-model:title="title" v-model:content="content" />
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const title = ref('标题')
const content = ref('内容')
</script>
<!-- Vue3 子组件(Composition API) -->
<script setup>
// 定义 props
const props = defineProps({
title: { type: String, required: true },
content: { type: String, default: '' }
})
// 定义 emits
const emit = defineEmits(['update:title', 'update:content'])
const changeTitle = () => {
emit('update:title', '新标题')
}
</script>
// Vue2
export default {
beforeCreate() {},
created() {},
beforeMount() {},
mounted() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {}
}
// Vue3 Options API(名称变化)
export default {
beforeCreate() {},
created() {},
beforeMount() {},
mounted() {},
beforeUpdate() {},
updated() {},
beforeUnmount() {}, // beforeDestroy 改名
unmounted() {} // destroyed 改名
}
// Vue3 Composition API
<script setup>
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'
onBeforeMount(() => {})
onMounted(() => {})
onBeforeUpdate(() => {})
onUpdated(() => {})
onBeforeUnmount(() => {})
onUnmounted(() => {})
// setup 中无 beforeCreate 和 created,直接用 setup 内的代码代替
</script>
<!-- Vue2 -->
<template>
<div ref="myDiv">内容</div>
<Child ref="childComp" />
</template>
<script>
export default {
mounted() {
console.log(this.$refs.myDiv)
console.log(this.$refs.childComp)
}
}
</script>
<!-- Vue3 Options API -->
<template>
<div ref="myDiv">内容</div>
</template>
<script>
export default {
mounted() {
console.log(this.$refs.myDiv)
}
}
</script>
<!-- Vue3 Composition API -->
<template>
<div ref="myDiv">内容</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const myDiv = ref(null) // 名称必须与模板中 ref 一致
onMounted(() => {
console.log(myDiv.value)
})
</script>
// Vue2
Vue.directive('focus', {
bind(el) { // 首次绑定
el.focus()
},
inserted(el) { // 插入父节点
el.focus()
},
update(el) {}, // 组件更新
componentUpdated(el) {},
unbind(el) {}
})
// Vue3
const app = createApp(App)
app.directive('focus', {
beforeMount(el) {}, // bind → beforeMount
mounted(el) { // inserted → mounted
el.focus()
},
beforeUpdate(el) {}, // update → beforeUpdate
updated(el) {}, // componentUpdated → updated
beforeUnmount(el) {}, // unbind → beforeUnmount
unmounted(el) {}
})
// Vue2(使用事件总线)
// eventBus.js
import Vue from 'vue'
export const EventBus = new Vue()
// 发送事件
import { EventBus } from './eventBus'
EventBus.$emit('custom-event', data)
// 接收事件
EventBus.$on('custom-event', (data) => {})
// Vue3(使用 mitt 库替代)
// eventBus.js
import mitt from 'mitt'
export const emitter = mitt()
// 发送事件
import { emitter } from './eventBus'
emitter.emit('custom-event', data)
// 接收事件
emitter.on('custom-event', (data) => {})
// Vue2
import Vue from 'vue'
Vue.component('MyComponent', MyComponent)
Vue.directive('focus', focusDirective)
Vue.mixin(myMixin)
Vue.use(MyPlugin, { options })
Vue.prototype.$http = axios
Vue.config.productionTip = false
// Vue3
import { createApp } from 'vue'
const app = createApp(App)
app.component('MyComponent', MyComponent)
app.directive('focus', focusDirective)
app.mixin(myMixin)
app.use(MyPlugin, { options })
app.config.globalProperties.$http = axios
app.config.productionTip = false // 已移除,不再需要
<!-- Vue2 -->
<template>
<div>{{ price | currency }}</div>
</template>
<script>
export default {
filters: {
currency(val) {
return '¥' + val
}
}
}
</script>
<!-- Vue3(使用计算属性或方法替代) -->
<template>
<div>{{ formatCurrency(price) }}</div>
</template>
<script setup>
const formatCurrency = (val) => '¥' + val
</script>
<!-- 或使用全局方法 -->
<template>
<div>{{ $formatCurrency(price) }}</div>
</template>
<script setup>
import { getCurrentInstance } from 'vue'
const { appContext } = getCurrentInstance()
appContext.config.globalProperties.$formatCurrency = (val) => '¥' + val
</script>
// Vue2
const AsyncComponent = () => import('./MyComponent.vue')
// 带配置
const AsyncComponent = () => ({
component: import('./MyComponent.vue'),
loading: LoadingComponent,
error: ErrorComponent,
delay: 200,
timeout: 3000
})
// Vue3
import { defineAsyncComponent } from 'vue'
const AsyncComponent = defineAsyncComponent(() => import('./MyComponent.vue'))
// 带配置
const AsyncComponent = defineAsyncComponent({
loader: () => import('./MyComponent.vue'),
loadingComponent: LoadingComponent,
errorComponent: ErrorComponent,
delay: 200,
timeout: 3000
})
# 安装 vue-codemod
npm install -g vue-codemod
# 运行迁移
vue-codemod transform src/
建议优先使用 Vue3 Composition API + <script setup> 重写新组件,老组件可暂时保留 Options API 语法,逐步迁移。