概述
在 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 的作用,比如网站托管在 example.com,base 就设置为
/
;托管在 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 对象