手写一个 Vuex 和 Vue-Router

简介: 在 Vue3.0 全面开放的大背景下,Vue 的周边生态迅速跟进,其中与 Vue 具有“血缘关系”的两个组件Vuex 和 Vue-Router 也相对应 Vue3.0 推出了全新的版本。

概述


在 Vue3.0 全面开放的大背景下,Vue 的周边生态迅速跟进,其中与 Vue 具有“血缘关系”的两个组件Vuex 和 Vue-Router 也相对应 Vue3.0 推出了全新的版本。

此次版本更新中,Vuex 大部分API都与之前的版本(Vuex3.0)相似,只有小部分发生了变动,Vue-Router相对来说发生了较大的变化

1682522413(1).png


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 显示为激活状态,新版本中移除了这个属性,现在的路由是基于它们所代表的路由记录来激活的,而不是路由地址对象及其 pathqueryhash 属性来激活的

router-view

transitionkeep-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 对象

相关文章
|
安全 Java 数据处理
Java面试题:什么是Java中的Optional类及其使用场景?
Java面试题:什么是Java中的Optional类及其使用场景?
211 0
|
SQL 网络协议 安全
有哪些常见的网络攻击类型?
【8月更文挑战第8天】
683 15
|
JavaScript 数据可视化 搜索推荐
在Vue项目中使用Echarts图表库
这篇文章介绍了如何在Vue项目中集成ECharts图表库,并通过具体的代码示例展示了如何创建并配置一个饼图来展示数据。
328 0
在Vue项目中使用Echarts图表库
|
小程序 JavaScript Java
图书馆自习室|基于SSM的图书馆自习室座位预约小程序设计与实现(源码+数据库+文档)
图书馆自习室|基于SSM的图书馆自习室座位预约小程序设计与实现(源码+数据库+文档)
182 0
|
负载均衡 Dubbo NoSQL
Dubbo配置文件解密:从dubbo-consumer.xml到dubbo-provider.xml一网打尽【十】
Dubbo配置文件解密:从dubbo-consumer.xml到dubbo-provider.xml一网打尽【十】
521 0
|
Java 应用服务中间件 nginx
Spring Boot 学习研究笔记(二十)-docker部署SpringBoot使用nginx配置域名
Spring Boot 学习研究笔记(二十)-docker部署SpringBoot使用nginx配置域名
603 0
|
存储 SQL 安全
阿里云rds简介和购买流程
阿里云RDS是阿里巴巴集团推出的一种基于云计算技术的托管式关系型数据库服务。作为云端数据库的解决方案之一,阿里云RDS提供了稳定可靠、高性能、弹性扩展的数据库服务,为企业和个人用户提供了便捷的数据存储和管理方案。本文将详细介绍阿里云RDS的特点、功能以及在数据库行业中的优势。
|
Shell Linux
linux通过namespace技术实现user用户隔离的例子
在Linux中,可以使用namespace技术实现用户隔离。下面是一个简单的示例,演示如何使用namespace隔离用户。 1. 首先,我们可以创建一个新的用户和组。使用以下命令创建一个新用户和组: ```shell sudo useradd -m testuser ``` 这将创建一个名为testuser的新用户,并为其创建一个相应的用户目录。 2. 接下来,我们将在新的用户namespace中启动一个新的bash Shell。运行以下命令: ```shell sudo unshare -U /bin/bash ``` 该命令将创建一个新的用户namespace,并在该names
964 0
|
JSON 缓存 前端开发
PWA(渐进式Web应用)的开发与部署
PWA(渐进式Web应用)的开发与部署
681 0
|
JavaScript 前端开发 Java
响应式编程简介之:Reactor
响应式编程简介之:Reactor
响应式编程简介之:Reactor

热门文章

最新文章