使用vite + vue3 + ant-design-vue + vue-router + vuex 创建一个管理应用的记录

使用vite 创建项目

我创建的node 版本是 v16.17.1

  1. 使用NPM 或者 YARN 安装中选择模板和定义项目名称
npm init vite@latest my-vue-app -- --template vue
yarn create vite my-vue-app -- --template vue
  1. 下载过程中会需要自己选择使用的语言和版本
  2. 下载完项目后,可以启动项目
"scripts": {
  "dev": "vite",
  "build": "vite build",
  "preview": "vite preview"
},
  1. 启动后能看到一个hello vue3 的模板。项目引用的插件基本没有。后面需要用到的自己来安装

安装项目中用到的插件

  1. 这里我简单安装了如下插件
"ant-design-vue": "^3.2.13",
"axios": "^1.1.3",
"c-scrollbar": "^1.0.2",
"vue": "^3.2.41",
"vue-router": "4",
"vuex": "^4.1.0"

"less": "^4.1.3",
"unplugin-vue-components": "^0.22.9",

项目使用了 ant-design-vue 先来说下UI 组件的配置

[文档] https://www.antdv.com/docs/vue/getting-started-cn

这里我介绍下 我使用的按需加载的配置。 在vite.congig.js 中

import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
import Components from 'unplugin-vue-components/vite'
export default defineConfig({
  plugins: [
    vue(),
    Components({
      resolvers: [
        AntDesignVueResolver()
      ]
    })
  ],
})

设置完,在组件中就可以直接使用框架提供的UI 组件, 写法上比较方便了。

项目请求服务端的跨域配置

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
import Components from 'unplugin-vue-components/vite'
import path from 'path'
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    Components({
      resolvers: [
        AntDesignVueResolver()
      ]
    })
  ],
  server: {
    proxy: {
      "/api": {
        target: "要请求的地址",
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ""),
      },
    },
  },
})

配置axios 统一请求拦截器添加的参数,和配置响应的分类

一般在发送服务请求的时候,会在请求头中添加验证的token ,配置请求地址中识别需要跨域处理的关键字端。
响应中根据返回的结果,按统一的结构返回到请求的API中

import axios from 'axios'
import store from '@/store'
import router from '@/route/index.js'
axios.defaults.timeout = 30000
// 返回其他状态吗
axios.defaults.validateStatus = function (status) {
  return status >= 200 && status <= 500 // 默认的
}
// 跨域请求,允许保存cookie
axios.defaults.withCredentials = true

axios.interceptors.request.use(config => {
  config.url = '/api' + config.url
  const token = store.state.token
  const TENANT_ID = 1
  if (token) {
    config.headers['Authorization'] = 'Bearer ' + token// token
  }
  if (TENANT_ID) {
    config.headers['TENANT-ID'] = TENANT_ID // 租户ID
  }
  return config
}, error => {
  return Promise.reject(error)
})

axios.interceptors.response.use(res => {
  const httpStatus = res.status
  if (httpStatus === 200) {
    const code = res.data.code
    if (code === 0) {
      return res.data.data
    }else{
      return Promise.reject(new Error(res.data.msg))
    }
  }else{
    router.push({
      path: '/login'
    })
    return Promise.reject(new Error(res.statusText))
  }
  
}, error => {
  // 处理 503 网络异常
  if (error.response.status === 503) {

  }
  return Promise.reject(new Error(error))
})

export default axios

vuex 在项目中的配置,使用

[文档地址]https://vuex.vuejs.org/zh/guide/
上面的请求拦截器中我们使用到了token, token保存在了vuex 的store 中。
下面介绍下 vuex 的配置和使用

  1. 首先下载vuex ,在开始的时候 已经将用到的依赖插件下载好了,版本是 4.1.0
  2. 创建store.js 包含state, mutatins, actions, getter
import {createStore} from 'vuex'
const  state = {
    allRoutes: [],
    token: '',
};
const mutations = {
    set_allRoutes(state,payload){
        state.allRoutes = payload
    },
    set_token(state, token) {
      state.token = token
    }
}
const actions = {
  set_access_token(context, token) {
    return new Promise(resolve => {
      context.commit('set_token', token)
      resolve()
    })
  }
}
const getter = {
  allRoutes(state) {
    return state.allRoutes
  }
}
const options = {
    state,
    mutations,
    actions,
    getter
}
const  store = createStore(options)
export default store
  1. 在main.js中
import { createApp } from 'vue'
import App from './App.vue'
import store from '@/store/index'
const app = createApp(App)
app.use(router).use(store).mount('#app')
  1. 使用中可以使用 commit, dispatch 来调用mutations, commit 中修改数据的方法
    4.1 使用dispatch 异步修改数据
import { defineComponent, reactive } from 'vue';
import { useStore } from 'vuex'
import {useRouter} from 'vue-router'
export default defineComponent({
  setup() {
    const store = useStore()
    const route = useRouter()
    const onFinish = values => {
      let token = '12b02935-e967-4ef6-9276-6207f4fff6e8'
      store.dispatch('set_access_token', token).then(() => {
        route.push({
          path: '/'
        })
      })
    };
    return {
      onFinish,
    };
  },

});

4.2 使用commit 同步修改数据

const store = useStore()
store.commit('set_allRoutes', res);
  1. 组件中使用存储的数据
import {useStore} from 'vuex'
const store = useStore()
let menu = store.state.allRoutes

vue-router 的基本使用,和动态路由的添加

在应用中 vue-router 帮助开发者实现了前端路由,[参考文档]https://router.vuejs.org/zh/

  1. 基本的路由配置也比较简单,参考如下
route/index.js
import {createRouter,createWebHashHistory} from 'vue-router'
import Home from '../page/Home.vue'
import Login from '@/page/login/index.vue'
const routes = [
  {
    path: '/',
    redirect: '/index/hello'
  },
  {
    path: '/login',
    name: 'login',
    component:Login
  },
  { 
    path: '/index',
    component: Home,
    children: [
      { path: 'hello',name: 'hello', component: () => import ('../components/HelloWorld.vue') },
      
    ],
  }
]
const router = createRouter({
  // 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
  history: createWebHashHistory(),
  routes, // `routes: routes` 的缩写
})
export default router

根据上面的写法很容易实现路由和子路由的添加
2. 项目中,一般会有根据用户请求,返回自己拥有的路由权限来动态添加路由
在这里 我做了个简单的示例,
这里选用动态添加路由的方式为 router.addRoute

import router from './index'
import Home from '../page/Home.vue'
import {fetchMenuList} from '@/api/main.js'
const modules = import.meta.glob("../**/**/**.vue")
function addRoute (list) {
  list.forEach(item => {
    if (item.path === '/admin') {
      let routelist = []
      item.children.forEach(child => {
        let path = child.path.slice(0)
        let r = {path: path, name: child.name, component: modules[`../view${child.path}.vue`]}
        routelist.push(r)
      })

      router.addRoute(
        { 
          path: item.path,
          component: Home,
          children: routelist
        }
      )
    }
  })
  
}

上面代码中 fetchMenuList 是获取用户路由权限的接口,
Home 是要添加的二级路由的 父组件, 接口中获取的数据比较多, 这里只添加了 amdin 一级路由下的二级路由, 如果有多个,也是这个思路
在这需要注意的是 子路由的引用方式,观察代码和 最初简单的添加方式,路由地址引用的模式发生了变化, 这个是因为使用 vite 来编译代码,需要做的处理。 这里不做具体介绍。

下面是权限代码中 添加动态路由的代码

import router from './index'
import Home from '../page/Home.vue'
import store from '@/store'
import {fetchMenuList} from '@/api/main.js'
const modules = import.meta.glob("../**/**/**.vue")
function addRoute (list) {
  list.forEach(item => {
    if (item.path === '/admin') {
      let routelist = []
      item.children.forEach(child => {
        let path = child.path.slice(0)
        let r = {path: path, name: child.name, component: modules[`../view${child.path}.vue`]}
        routelist.push(r)
      })

      router.addRoute(
        { 
          path: item.path,
          component: Home,
          children: routelist
        }
      )
    }
  })
  
}
const getRouteInfo = () => {
  const allRoutes = store.state.allRoutes
  return new Promise(resolve => {
    if (allRoutes.length === 0) {

      fetchMenuList().then(res => {
        addRoute(res)
        store.commit('set_allRoutes', res);
        resolve()
      })
    }else {
      addRoute(allRoutes)
      resolve()
    }
  })
}
let menuRouter = false
router.beforeEach((to, from , next) => {
  if (store.state.token) {
    if (!menuRouter) {
      
      getRouteInfo().then(() => {
        menuRouter = true
        next({...to, replace: true});
      })
    }else{
      next()
    }
  }else {
    if (to.path == '/login') {
      next()
    }else{

      next('/login')
    }
  }
})

后面的文章,会接着介绍 项目其他相关内容, 欢迎点赞加关注

这里介绍下本人做的头像,壁纸小程序,欢迎大家体验,
热门头像|个性头像|高清头像|性感壁纸|美女壁纸|炫酷壁纸|省电壁纸|唯美壁纸

11-18 10:51