1. vue2迁移vue3记录 1.1. 挂载方式差别 1.2. 使用 vue-router
1.3. 使用 vuex
1.4. router-view
使用keep-alive
1.5. Data选项 1.6. emits选项 1.7. 事件api 1.8. 过滤器 1.9. 片段 1.10. 全局api 1.11. key attribute 1.12. 按键修饰符 1.13. propsData 1.14. 过渡的 class 名更改 1.15. Transition 作为 Root 1.16. 移除v-on.native修饰符 1.17. v-model 1.18. v-if和v-for优先级 1.19. v-bind合并行为 1.20. VNode 生命周期事件 1.21. watch Array
1. vue2迁移vue3记录
开始用Vue3
写项目时,查看V3 迁移指南发现由于全局 Vue API
已更改为使用应用程序实例,相应的很多操作诸如挂载App
实例,使用vue-router
、vuex
等都发生了一定的变化,为了以后更快的搭建Vue3
项目,把主要的差别简单记录如下:
1.1. 挂载方式差别
vue2:
// index.js
import Vue from ‘vue‘
import App from ‘./App.vue‘
new Vue({
render:h=>h(App)
}).$mount(‘#app‘)
vue3:
// index.js
import { createApp } from "vue";
import App from ‘./App.vue‘
createApp(App).mount("#app");
1.2. 使用vue-router
vue2:
// index.js
import Vue from ‘vue‘
import VueRouter from ‘vue-router‘
import routes from ‘@/router‘
Vue.use(VueRouter)
const router = new VueRouter({
routes,
mode: ‘history‘,
})
new Vue({
router,
render: h => h(App),
}).$mount(‘#app‘)
// src/router/index.js
const home = () => import(‘../pages/home/Home‘)
const routes = [
{
path: ‘‘,
redirect: ‘/home‘
},
{
path: ‘/home‘,
name: ‘home‘,
component: home
}
]
export default routes
vue3:
// index.js
import { createApp } from "vue";
import App from ‘./App.vue‘
import router from "./router";
createApp(App).use(router).mount("#app");
// src/router/index.js
import { createRouter, createWebHistory } from "vue-router";
const home = () => import("../views/home/Home");
const routes = [
{
path: "",
redirect: "/home",
},
{
path: "/home",
name: "home",
component: home,
},
];
const router = createRouter({
history: createWebHistory(),
routes: routes,
});
export default router;
1.3. 使用vuex
vue2:
// index.js
import Vue from ‘vue‘
import store from ‘@/store/‘;
new Vue({
store,
render:h=>h(App)
}).$mount(‘#app‘)
// src/store/index.js
import Vuex from ‘vuex‘
import Vue from ‘vue‘
import actions from ‘./actions‘
import getters from ‘./getters‘;
import mutations from ‘./mutations‘;
Vue.use(Vuex)
const state = {}
export default new Vuex.Store({
state,
actions,
mutations,
getters,
})
vue3:
// index.js
import { store } from "./store";
createApp(App).use(store).mount("#app");
// src/store/index.js
import { createStore } from "vuex";
import actions from "./actions";
import getters from "./getters";
import mutations from "./mutations";
const state = () => {};
export const store = createStore({
state,
actions,
getters,
mutations,
});
1.4. router-view
使用keep-alive
vue2:
// App.vue
<transition name="router-fade" mode="out-in">
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
</transition>
<transition name="router-fade" mode="out-in">
<router-view v-if="!$route.meta.keepAlive"></router-view>
</transition>
vue3:
// App.vue
<router-view v-slot="{ Component }">
<transition name="router-fade" mode="out-in">
<keep-alive>
<component :is="Component"/>
</keep-alive>
</transition>
</router-view>
1.5. Data选项
2.x
在根实例上可以声明为纯 JavaScript
object
Mixin
中 Data
选项合并像是取并集?深度合并
3.x
无论何时都应该声明为function
Mixin
中 Data
选项合并像是取交集?浅层次执行合并
1.6. emits选项
vue3
新增了 emits
选项,和 prop
类似,组件可触发的事件可以通过 emits
选项被定义:
<template>
<div>
<p>{{ text }}</p>
<button v-on:click="$emit(‘accepted‘)">OK</button>
</div>
</template>
<script>
export default {
props: [‘text‘],
emits: [‘accepted‘]
}
</script>
1.7. 事件api
2.x
中用于事件总线上的$on
/$off
/$once
实例方法已被移除
1.8. 过滤器
2.x
的过滤器已被移除,3.x
建议使用计算属性或方法来替换过滤器,全局过滤器可以通过全局属性在所有组件中使用它:
// main.js
const app = createApp(App)
app.config.globalProperties.$filters = {
currencyUSD(value) {
return ‘$‘ + value
}
}
然后,你可以通过 $filters
对象修改所有的模板,像下面这样:
<template>
<h1>Bank Account Balance</h1>
<p>{{ $filters.currencyUSD(accountBalance) }}</p>
</template>
注意,这种方式只能用于方法中,不可以在计算属性中使用,因为后者只有在单个组件的上下文中定义时才有意义。
1.9. 片段
3.x
支持多根节点的组件,但是要求开发者显式定义 attribute
应该分布在哪里。
<!-- Layout.vue -->
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
1.10. 全局api
3.x
中调用 createApp
返回一个应用实例,任何全局改变 Vue
行为的 API
现在都会移动到应用实例上,以下是当前 2.x
全局 API
及其相应实例 API
的表:
2.x 全局 API |
3.x 实例 API (app) |
---|---|
Vue.config | app.config |
Vue.config.productionTip | removed |
Vue.config.ignoredElements | app.config.isCustomElement |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use |
Vue.prototype | app.config.globalProperties |
1.11. key attribute
3.x
中在template
上设置v-for
时key
要加在template
1.12. 按键修饰符
3.x
不再支持使用数字 (即键码) 作为 v-on 修饰符,不再支持 config.keyCodes
配置按键别名
1.13. propsData
propsData
选项已经被移除。如果你需要在实例创建时向根组件传入 prop
,你应该使用 createApp
的第二个参数:
const app = createApp(
{
props: [‘username‘],
template: ‘<div>{{ username }}</div>‘
},
{ username: ‘Evan‘ }
)
1.14. 过渡的 class 名更改
3.x
中过渡类名 v-enter
修改为 v-enter-from
、过渡类名 v-leave
修改为 v-leave-from
。
1.15. Transition 作为 Root
一个 <transition>
原本希望是被其子元素触发的,而不是被 <transition>
自己切换。
换做向其组件传递一个 prop
就可以达到类似的效果:
<template>
<transition>
<div v-if="show" class="modal"><slot/></div>
</transition>
</template>
<script>
export default {
props: [‘show‘]
}
</script>
<!-- 用法 -->
<modal :show="showModal">hello</modal>
1.16. 移除v-on.native修饰符
删除 .native
修饰符的所有实例。确保所有组件都使用 emits
选项记录其事件。
1.17. v-model
在 3.x
中,自定义组件上的 v-model
相当于传递了 modelValue prop
并接收抛出的 update:modelValue
事件:
<ChildComponent v-model="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent
:modelValue="pageTitle"
@update:modelValue="pageTitle = $event"
/>
若需要更改 model
名称,作为组件内 model
选项的替代,现在我们可以将一个 argument
传递给 v-model
:
<ChildComponent v-model:title="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
而且允许我们在自定义组件上使用多个 v-model
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />
<!-- 是以下的简写: -->
<ChildComponent
:title="pageTitle"
@update:title="pageTitle = $event"
:content="pageContent"
@update:content="pageContent = $event"
/>
对于所有不带参数的 v-model
,请确保分别将 prop
和 event
命名更改为 modelValue
和 update:modelValue
<ChildComponent v-model="pageTitle" />
// ChildComponent.vue
export default {
props: {
modelValue: String // 以前是`value:String`
},
emits: [‘update:modelValue‘],
methods: {
changePageTitle(title) {
this.$emit(‘update:modelValue‘, title) // 以前是 `this.$emit(‘input‘, title)`
}
}
}
1.18. v-if和v-for优先级
2.x
版本中在一个元素上同时使用 v-if
和 v-for
时,v-for
会优先作用。
3.x
版本中 v-if 总是优先于 v-for
生效。
1.19. v-bind合并行为
在 2.x
,如果一个元素同时定义了 v-bind="object"
和一个相同的单独的 property
,那么这个单独的 property
总是会覆盖 object
中的绑定。
<!-- template -->
<div id="red" v-bind="{ id: ‘blue‘ }"></div>
<!-- result -->
<div id="red"></div>
在 3.x
,如果一个元素同时定义了 v-bind="object"
和一个相同的单独的 property
,那么声明绑定的顺序决定了它们如何合并。换句话说,相对于假设开发者总是希望单独的 property
覆盖 object
中定义的内容,现在开发者对自己所希望的合并行为有了更好的控制。
<!-- template -->
<div id="red" v-bind="{ id: ‘blue‘ }"></div>
<!-- result -->
<div id="blue"></div>
<!-- template -->
<div v-bind="{ id: ‘blue‘ }" id="red"></div>
<!-- result -->
<div id="red"></div>
1.20. VNode 生命周期事件
3.x
父组件可以通过vnode-生命周期钩子来监听子组件生命周期中的关键阶段。
<template>
<child-component @vnode-updated="onUpdated">
</template>
或者在驼峰命名法的情况下附带前缀 vnode
:
<template>
<child-component @vnodeUpdated="onUpdated">
</template>
1.21. watch Array
3.x
当使用 watch
选项侦听数组时,只有在数组被替换时才会触发回调。换句话说,在数组改变时 watch
回调将不再被触发。要想在数组改变时触发 watch
回调,必须指定 deep
选项。
watch: {
bookList: {
handler(val, oldVal) {
console.log(‘book list changed‘)
},
deep: true
},
}