BaiqiCMS网站管理系统

Nginx/IIS/Apache PHP5.3+ MySQL5.7+

外贸型/营销型/品牌企业网站建设首选CMS

Vue2 升级到 Vue3 常见代码对比

一、实例创建与全局配置

// 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')

二、组件定义

2.1 基础组件结构

<!-- 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>

五、Props 与 Emits

<!-- 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>

七、Ref 与模板引用

<!-- 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) => {})

十、全局 API 迁移

// 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
})

十三、项目升级关键步骤

  1. 使用迁移工具
# 安装 vue-codemod
npm install -g vue-codemod

# 运行迁移
vue-codemod transform src/
  1. 修改入口文件(main.js → main.js/ts)
  2. 更新依赖(vue-router@4, vuex@4 → pinia)
  3. 处理破坏性变更(移除过滤器、$on等)
  4. 调整 webpack/vite 配置

建议优先使用 Vue3 Composition API + <script setup> 重写新组件,老组件可暂时保留 Options API 语法,逐步迁移。

×
BaiqiCMS官方客服

添加BaiqiCMS官方微信客服

关闭