Vue3路由守卫、vuex的使用、vuex模块化拆分、vite中自动导入模块文件


1. 路由守卫

vue3 中的导航守卫与 vue2 中的基本一致,不同的地方在于,vue3 中导航守卫取消了 next 参数,而是通过返回 false 来取消路由跳转。

以前置路由守卫为例,结合 sessionStorage 实现获取 token 值之后跳转到后台管理页面。

配置路由及守卫:

// vue-router中提供3种的路由模式
import {
    
     createWebHistory, createRouter } from 'vue-router'

const routes = [
  {
    
    
    path: '/login',
    component: () => import('@/views/login.vue')
  },
  {
    
    
    path: '/admin',
    component: () => import('@/views/admin.vue'),
    meta: {
    
    
      isLogin: true
    }
  }
] 

const router = createRouter({
    
    
  // 路由的模式
  history: createWebHistory(),
  // 路由规则
  routes
})

// 全局前置守卫
router.beforeEach((to, from) => {
    
    
  // 验证指定的页面无须登录就可以访问
  // 1.可以通过path路由来区别
  // 2.meta来完成区别
  // console.log('前置全局守卫', to.path);
  // console.log('前置全局守卫', to.meta.isLogin);
  // 如果此页面需要登录才能访问到,就需要来判断当前的本地存储中是否有token
  if (!to.meta.isLogin) {
    
    
    return true
  }
  // 如下的页面一定要求用户登录才能访问到
  if (!sessionStorage.getItem('token')) {
    
    
    // 跳转到登录页
    // return '/login'
    // 跳转后不能回退
    return {
    
     path: '/login', replace: true }
  }

  return true
})

export default router

mock 假数据:

import Mockjs from 'mockjs'

const mockData = [
  {
    
    
    url: '/api/login',
    method: 'post',
    response: () => ({
    
    
      code: 0,
      msg: 'ok',
      data: {
    
    
        uid: 1000,
        nickname: '张三',
        token: 'afewlfjewlfjewlfejlfejl;fejlf;e'
      }
    })
  }
]

export default mockData

登录页:

<template>
  <div>
    <h3>登录页面</h3>
    <div>
      <input type="text" v-model="username" />
    </div>
    <div>
      <input type="text" v-model="password" />
    </div>
    <div>
      <button @click="doLogin">进入系统</button>
    </div>
  </div>
</template>

<script setup>
import {
      
       reactive, toRefs } from 'vue'
import {
      
       useRouter } from 'vue-router'
import {
      
       doLoginApi } from '@/api/userApi'

const router = useRouter()

const state = reactive({
      
      
  username: '',
  password: ''
})
const {
      
       username, password } = {
      
       ...toRefs(state) }

// 登录成功
const doLogin = async () => {
      
      
  let ret = await doLoginApi(state)
  let token = ret.data.token
  sessionStorage.setItem('token', token)
  router.replace('/admin')
}
</script>

<style lang="scss" scoped>

</style>

后台管理页:

<template>
  <div>
    <h3>后台管理</h3>
  </div>
</template>

<script setup>

</script>

<style lang="scss" scoped>

</style>

在这里插入图片描述

2. vuex的使用

2.1 基本使用

安装:

yarn add vuex

使用:

首先为 vuex 创建 store 目录(store/index.js):

import {
    
     createStore } from 'vuex'

// const store = createStore({
    
    
//   state: {
    
    
//     num: 100
//   }
// })

// 如果你现在用的是基于vue的服务器端渲染,此时一定要写成回调函数方案
const store = createStore({
    
    
  state: () => ({
    
    
    num: 100
  }),
  mutations: {
    
    
    setNum(state, payload) {
    
    
      state.num += payload
    }
  },
  actions: {
    
    
    asyncSetNum({
     
      commit }, payload) {
    
    
      setTimeout(() => {
    
    
        commit('setNum', payload)
      }, 1000);
    }
  }
})

export default store

在入口文件(main.js)处引入 vuex:

// 创建vue入口程序,由原来的类实例,变成现在的函数方式,为了更好在打包时优化代码
import {
    
     createApp } from 'vue'
// 根组件
import App from './App.vue'
// 路由
import router from './router'
// vuex
import store from './store'

import createGlobalComponent from './components'
import globalProperties from './config/globalProperties'

// 实例化一个Vue顶层组件
// const app = createApp(App)

// 创建全局组件
// // createGlobalComponent(app)
// app.use(createGlobalComponent)
// app.use(globalProperties)
// // 路由
// app.use(router)
// // vuex
// app.use(store)
// app.mount('#app')

// 工作中常用写法:
createApp(App)
  .use(createGlobalComponent)
  .use(globalProperties)
  .use(router)
  .use(store)
  .mount('#app')

关于页面:

<template>
  <div>
    <h3>关于页面 -- {
   
   { num }}</h3>
    <button @click="setNum">++同步++</button>
    <button @click="asyncSetNum">++异步++</button>
  </div>
</template>

<script setup>
import {
      
       computed } from 'vue'
// vuex提供hook函数
import {
      
       useStore } from 'vuex'
const store = useStore()

const num = computed(() => store.state.num)

const setNum = () => {
      
      
  store.commit('setNum', 1)
}

const asyncSetNum = () => {
      
      
  store.dispatch('asyncSetNum', 2)
}
</script>

<style lang="scss" scoped></style>

在这里插入图片描述

2.2 模块化拆分

方式一

store/modules/count.js:

export default {
    
    
  // 开启强制命名空间
  namespaced: true,
  state: () => ({
    
    
    num: 100
  }),
  mutations: {
    
    
    setNum(state, payload) {
    
    
      state.num += payload
    }
  },
  actions: {
    
    
    asyncSetNum({
     
      commit }, payload) {
    
    
      setTimeout(() => {
    
    
        commit('setNum', payload)
      }, 1000);
    }
  }
}

store/modules/user.js:

export default {
    
    
  // 开启强制命名空间
  namespaced: true,
  state: () => ({
    
    
    uid: 0,
    nickname: '',
    token: ''
  }),
  mutations: {
    
    
    login(state, payload) {
    
    
      state.uid = payload.uid
      state.nickname = payload.nickname
      state.token = payload.token
      sessionStorage.setItem('token', payload.token)
    }
  },
  actions: {
    
    
  }
}

store/index.js:

import {
    
     createStore, useStore } from 'vuex'

import count from './modules/count'
import user from './modules/user'

// 如果你现在用的是基于vue的服务器端渲染,此时一定要写成回调函数方案
const store = createStore({
    
    
  modules: {
    
    
    count,
    user
  }
})

export default store

关于页面:

<template>
  <div>
    <h3>关于页面 -- {
   
   { num }}</h3>
    <button @click="setNum">++同步++</button>
    <button @click="asyncSetNum">++异步++</button>
  </div>
</template>

<script setup>
import {
      
       computed } from 'vue'
// vuex提供hook函数
// 方式一导入
import {
      
       useStore } from 'vuex'
const store = useStore()

const num = computed(() => store.state.count.num)

const setNum = () => {
      
      
  store.commit('count/setNum', 1)
}

const asyncSetNum = () => {
      
      
  store.dispatch('count/asyncSetNum', 2)
}
</script>

<style lang="scss" scoped></style>

登录页面:

<template>
  <div>
    <h3>登录页面</h3>
    <div>
      <input type="text" v-model="username" />
    </div>
    <div>
      <input type="text" v-model="password" />
    </div>
    <div>
      <button @click="doLogin">进入系统</button>
    </div>
  </div>
</template>

<script setup>
import {
      
       reactive, toRefs } from 'vue'
import {
      
       useRouter } from 'vue-router'
import {
      
       doLoginApi } from '@/api/userApi'

//方式一导入
import {
      
       useStore } from 'vuex'
const store = useStore()

const router = useRouter()

const state = reactive({
      
      
  username: '',
  password: ''
})
const {
      
       username, password } = {
      
       ...toRefs(state) }

// 登录成功
const doLogin = async () => {
      
      
  let ret = await doLoginApi(state)
  let token = ret.data.token
  sessionStorage.setItem('token', token)
  router.replace('/admin')
}
</script>

<style lang="scss" scoped>

</style>

在这里插入图片描述

在这里插入图片描述

方式二

对 store 的入口文件中模块的导出作修改(对 useStore 做二次封装后导出)、对登录页面模块的导入作修改。

store/index.js:

import {
    
     createStore, useStore } from 'vuex'

import count from './modules/count'
import user from './modules/user'

// 如果你现在用的是基于vue的服务器端渲染,此时一定要写成回调函数方案
const store = createStore({
    
    
  modules: {
    
    
    count,
    user
  }
})

// 方式二导出
export const useUserStateStore = () => {
    
    
  let store = useStore()
  return [store.state.user, store.commit, store.dispatch]
}

export default store

登录页面:

<template>
  <div>
    <h3>登录页面</h3>
    <div>
      <input type="text" v-model="username" />
    </div>
    <div>
      <input type="text" v-model="password" />
    </div>
    <div>
      <button @click="doLogin">进入系统</button>
    </div>
  </div>
</template>

<script setup>
import {
      
       reactive, toRefs } from 'vue'
import {
      
       useRouter } from 'vue-router'
import {
      
       doLoginApi } from '@/api/userApi'

//方式二导入
import {
      
       useUserStateStore } from '@/store'
const [userState, commit] = useUserStateStore()

const router = useRouter()

const state = reactive({
      
      
  username: '',
  password: ''
})
const {
      
       username, password } = {
      
       ...toRefs(state) }

// 登录成功
const doLogin = async () => {
      
      
  let ret = await doLoginApi(state)
  let token = ret.data.token
  commit('user/login', ret.data)
  router.replace('/admin')
}
</script>

<style lang="scss" scoped>

</style>

方式三

在方式二的基础上,将 store/index.js 文件中的方法,拆分开导入。

store/index.js:

import {
    
     createStore, useStore } from 'vuex'

import count from './modules/count'
import user from './modules/user'

// 如果你现在用的是基于vue的服务器端渲染,此时一定要写成回调函数方案
const store = createStore({
    
    
  modules: {
    
    
    count,
    user
  }
})

export const useUserState = () => {
    
    
  let store = useStore()
  return store.state.user
}

export const useCommit = () => {
    
    
  let store = useStore()
  return store.commit
}

export const useDispatch = () => {
    
    
  let store = useStore()
  return store.dispatch
}

export default store

登录页面:

<template>
  <div>
    <h3>登录页面</h3>
    <div>
      <input type="text" v-model="username" />
    </div>
    <div>
      <input type="text" v-model="password" />
    </div>
    <div>
      <button @click="doLogin">进入系统</button>
    </div>
  </div>
</template>

<script setup>
import {
      
       reactive, toRefs } from 'vue'
import {
      
       useRouter } from 'vue-router'
import {
      
       doLoginApi } from '@/api/userApi'

const router = useRouter()

const state = reactive({
      
      
  username: '',
  password: ''
})
const {
      
       username, password } = {
      
       ...toRefs(state) }

// 登录成功
const doLogin = async () => {
      
      
  let ret = await doLoginApi(state)
  let token = ret.data.token
  commit('user/login', ret.data)
  router.replace('/admin')
}
</script>

<style lang="scss" scoped>

</style>

3. 在vite中自动导入模块文件

import {
    
     createStore, useStore } from 'vuex'

// 自动导入模块
// eager 同步,不能使用 promise
const moduleFiles = import.meta.glob('./modules/*', {
    
     eager: true })
let modules = {
    
    }
for (let key in moduleFiles) {
    
    
  let prop = /\.\/modules\/(\w+)\.js/.exec(key)[1]
  let value = moduleFiles[key].default
  modules[prop] = value
}

// 如果你现在用的是基于vue的服务器端渲染,此时一定要写成回调函数方案
const store = createStore({
    
    
  modules
})

export const useUserState = () => {
    
    
  let store = useStore()
  return store.state.user
}

export const useCommit = () => {
    
    
  let store = useStore()
  return store.commit
}

export const useDispatch = () => {
    
    
  let store = useStore()
  return store.dispatch
}

export default store

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45605541/article/details/128035371