概述
在 Vue3.0 全面开放的大背景下,Vue 的周边生态迅速跟进,其中与 Vue 具有“血缘关系”的两个组件Vuex 和 Vue-Router 也相对应 Vue3.0 推出了全新的版本。
此次版本更新中,Vuex 大部分API都与之前的版本(Vuex3.0)相似,只有小部分发生了变动,Vue-Router相对来说发生了较大的变化
Vuex
Vuex 的变更首先体现在挂载方式和创建过程上,不再使用 Vuex.store 来实例化 store,直接使用 createStore 来创建 store
对比一下当前版本与上一版的区别
// vuex3
// /store/index.js
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 1
}
})
export default store
// main.js
import store from './store'
new Vue({store, render: h => h(App)}).$mount('#app')
// vuex4
// /store/index.ts
import { createStore, Store, useStore as baseUseStore } from 'vuex';
import { InjectionKey } from 'vue';
// import { moduleA } from "@/store/moduleA";
export interface State {
count: number;
// modules: {
// a: ReturnType<typeof moduleA>;
// };
}
export const key: InjectionKey<Store<State>> = Symbol();
export const store = createStore<State>({
state: {
count: 1,
},
mutations: {
COUNT_ADD(state) {
state.count++;
},
},
getters: {
count(state) {
return state.count;
},
},
// modules: {
// a: moduleA
// }
});
// 自定义 useStore
// 通过引入自定义的组合式函数,不用提供 injection key 和类型声明就可以直接得到类型化的 store
export function useStore() {
return baseUseStore(key);
}
// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import { key, store } from './store';
const app = createApp(App);
app.use(router)
.use(store, key)
.mount('#app');
注意:在 main.ts 挂载 store 的时候必须传入 key,获取 store 的时候使用 key 来获取,本质上,Vuex 将store 安装到 Vue 应用中使用了 Vue 的 Provide/Inject 特性,具体原理不在此处介绍
在使用时,由于没有将$store 挂载到 Vue 实例上,所以要是用 useStore 获取 store
<template>
<div class="about">
{{count}}
<button @click="countAdd">+</button>
</div>
</template>
<script setup lang="ts">
import { useStore } from "@/store";
import { computed } from "vue";
const store = useStore()
const count = computed(() => store.getters.count)
const countAdd = () => store.commit('COUNT_ADD')
</script>
这里需要将获取的值使用 computed包裹一下,否则不会实现响应式
Vuex4 的 typescript 类型支持并不是很友好,很多时候需要使用者自定义类型,这也是 vuex在社区中口碑急转直下的原因。
Vue-Router
初始化
第一点同 Vuex,同样是使用使用函数式代替了原有的类,不再使用原先的 new VueRouter 来创建路由,而是使用 createRouter 创建路由
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue')
}
]
})
路由模式由原来的 mode 换成了 history,值的类型从字符串变成了函数,替换关系如下:
"history"
:createWebHistory()
"hash"
:createWebHashHistory()
"abstract"
:createMemoryHistory()
原本的 base 项现在变成了 history 函数的第一个参数(代码中的 import.meta.env 是 Vite 提供的能力)
base 的作用,比如网站托管在 https://example.com,base 就设置为
/
;托管在 https://example.com/app/ 下,base 就是/app/
通配路由
再捕获 Not Found 的时候,原先的方法时使用*方道路有最后来匹配未命中的路由,在新版本中,Vue-Router 实现了自己的路由逻辑
// 原来的通配符
{path: '*'}
// 现在的通配
{path: '/:pathMatch(.*)*'}
// 配置 /user-开头的路由
{path: '/user-*'}
// 匹配 user= 开头的路由,可以通过$route.params.afterUser获取后面的值
{path: '/user-:afterUser(.*)'}
onReady 替换为 isReady
// 将
router.onReady(onSuccess, onError)
// 替换成
router.isReady().then(onSuccess).catch(onError)
// 或者使用 await:
try {
await router.isReady()
// 成功
} catch (err) {
// 报错
}
去除了 router-link 的部分属性
append
在 Vue-Router3.0 中,可以通过 append 属性实现追加路由的效果,如当前路由是在/app/下,点击这个 router-link 会就会跳转到/app/append
<router-link :to="{ path: 'append'}" append></router-link>
在Vue-Router4.0中, append 属性被移除(因为使用量不大,而且用户很容易实现这个效果),但是可以通过以下方法来实现相同的效果
<router-link :to="append(currentPath, 'child-route')">
append
</router-link>
tag
在 Vue-Router3.0 中,router-link 会默认渲染为 a 标签,可以通过使用 tag 属性来控制渲染的标签类型
<router-link to="/about" tag="span">About</router-link>
渲染出来的效果就是使用 span 标签
在新版中需要使用 s-slot 来实现 非默认tag渲染
<router-link to="/about" custom v-slot="{ navigate }">
<span>About</span>
</router-link>
event
在 Vue-Router3.0 中,可以通过event 属性来控制触发 router-link 的事件,如
<router-link to="/about" event="mouseover">About</router-link>
在新版中同样需要使用 v-slot 来实现
<router-link to="/about" custom v-slot="{ navigate }">
<span @mouseover="navigate" role="link">About</span>
</router-link>
exect
Vue-Router3.0 中使用 exect 属性来控制路由匹配时 router-link 显示为激活状态,新版本中移除了这个属性,现在的路由是基于它们所代表的路由记录来激活的,而不是路由地址对象及其 path
、query
和 hash
属性来激活的
router-view
transition
和 keep-alive
现在必须通过 v-slot
API 在 RouterView
内部使用:
<router-view v-slot="{ Component }">
<transition>
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
composition-api
为我们在 setup
里面没有访问 this
,所以我们使用 route 或 router 时需要通过 useRoute 和 useRouter 来获取 route 和 router 对象