likes
comments
collection
share

vite+vue3+typescript+vuex+vue-router+element-plus构建管理系统前端架构

作者站长头像
站长
· 阅读数 18

一、使用vite初始化

我这里是使用yarn包管理工具安装,使用npm或者pnpm的同理 执行下面shell命令创建一个新的项目模板

yarn create vite 项目名称 --template vue-ts

创建成功后我们使用vscode编辑器打开项目,其中目录结构如下图:

vite+vue3+typescript+vuex+vue-router+element-plus构建管理系统前端架构 ~~ps:忽略main.js和preload.js~~

此时我们执行yarn install安装所需要的依赖包,安装完必执行yarn dev会打开vite+vue的欢迎界面

二、配置eslint,prettier

1:eslint初始化
// 执行下面命令
eslint --init

按照提示我们依次选择以下几个主要选项

? How would you like to use ESLint? ... 
  To check syntax only
  To check syntax and find problems
> To check syntax, find problems, and enforce code style
? What type of modules does your project use? ... 
> JavaScript modules (import/export)
  CommonJS (require/exports)
  None of these
? Which framework does your project use? ... 
  React
> Vue.js
  None of these
? Does your project use TypeScript? » No / Yes

选择推荐风格,这里我们直接选第一个跟着引导去选择aribn或者standard

> Use a popular style guide
  Answer questions about your style
  Inspect your JavaScript file(s)

选择完后,需要安装一些eslint相关的依赖包,默认是用npm安装,安装完会自动生成一个eslintrc.js,内容如下

{

    "env": {

        "browser": true,

        "es2021": true,

        "node": true

    },

    "extends": [

        "eslint:recommended",

        "plugin:vue/vue3-essential",

        "plugin:@typescript-eslint/recommended",

        "plugin:prettier/recommended"

    ],

    "overrides": [

    ],

    "parser": "vue-eslint-parser",

    "parserOptions": {

        "ecmaVersion": "latest",

        "parser": "@typescript-eslint/parser",

        "sourceType": "module"

    },

    "plugins": [

        "vue",

        "@typescript-eslint"

    ],

    "rules": {

        "@typescript-eslint/ban-types": [

            "error",

            {

                "extendDefaults": true,

                "types": {

                    "{}": false

                }

            }

        ],

        "vue/multi-word-component-names":"off"

    }

}

创建.eslintignore文件,忽略eslint不需要检查的文件

/index.html
/lambda/
/scripts
/config
/plugins
.history
/tsconfig.json
/src/vite-env.d.ts
2:配置prettier

安装prettier

yarn add prettier -D

解决 eslint 和 prettier 冲突 安装 eslint-config-prettier 解决 ESLint 中的样式规范和 prettier 中样式规范的冲突,以 prettier 的样式规范为准,使 ESLint 中的样式规范自动失效 eslint-plugin-prettier eslint-plugin-prettier插件会调用prettier对你的代码风格进行检查,其原理是先使用prettier对你的代码进行格式化,然后与格式化之前的代码进行对比,如果过出现了不一致,这个地方就会被prettier进行标记。 接下来,我们需要在rules中添加,"prettier/prettier": "error",表示被prettier标记的地方抛出错误信息。\

//.eslintrc.js
{
  "plugins": ["prettier"],
  "rules": {
    "prettier/prettier": "error"
  }
}

项目下新建 .prettierrc.json 文件

module.exports = {
  tabWidth: 2,
  jsxSingleQuote: true,
  jsxBracketSameLine: true,
  printWidth: 100,
  singleQuote: true,
  semi: false,
  overrides: [
    {
      files: '*.json',
      options: {
        printWidth: 200,
      },
    },
  ],
  arrowParens: 'always',
}

三、配置路径别名,处理import引入时ts报错

在vite-config.ts中添加如下配置

    base: './', // 设置打包路径
    resolve: {
        alias: {
            '@': path.resolve(__dirname, 'src'),
        },
        extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'],
    }

在tsconfig.json中增加配置

"compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    },
}    

此时的@就可以指向我们的src目录了

在src文件夹下创建shims-vue.d.ts文件 添加我们import引入时ts报错的模块

// 例如
declare module 'axios';
declare module 'element-plus';
declare module 'js-cookie';

四、配置element-plus

elementUi的配置非常简单这里我就不说了,直接参考官方的配置指南就可以了 element-plus.gitee.io/zh-CN/guide…

五、配置vuex、vue-route、less开发环境

// 我们一次把所有模块都安装上
yarn add vuex vue-router less less-loader

less使用跟vue2没有区别,安装完loader可以直接在style里面制定lang="less"

1:配置vuex

我们在src目录下创建store文件夹,store文件夹下分别创建index.ts 和modules文件夹,其中modules下面是我们的每个不通的store

因为vuex修改了初始化的方法,这里给vue2使用时略有区别,使用createStore创建store仓库

import { createStore } from 'vuex';
import getters from './getters';
import userStore from './modules/user';
import systemStore from './modules/system';
import permissionStore from './modules/permission';
const storeObj: any = {
    modules: {
        user: userStore,
        system: systemStore,
        permission: permissionStore,

    },
    getters,
};
export default createStore(storeObj);

ps:我这里分别引入了三个store,还可以使用import.meta.globEager引入所有的文件,写个通用函数来解析最好,因为vite中乜有require,这个功能既类似于require.context

配置完以上回到src/main.ts中,引入store

挂在store

import store from './store';
app.use(store)
2:配置vue-router

在src文件夹下创建router文件夹

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
const constantRoutes: Array<RouteRecordRaw> = [
    {
        path: '/',
        name: 'layout',
        component: () => import('@/pages/layout/index.vue'),
    },
    {
        path: '/login',
        name: 'login',
        component: () => import('@/pages/login/index.vue'),
    },
    {
        path: '/404',
        name: '404',
        component: () => import('@/pages/error-page/404.vue'),
    },

];
// 权限用动态路由添加
export const asyncRoutes: any = [];
export const router = createRouter({
    history: createWebHistory(),
    routes: constantRoutes,
});
// 权限用动态路由移除
export function resetRouter() {
    asyncRoutes.forEach((route: any) => {
        router.removeRoute(route.name);
    });
}

回到main.ts,仍然是引入挂载

import router from './router';
app.use(router)

这里到了重点权限控制,我这里使用动态路由控制权限

// 角色权限,动态路由
const whiteList = ['/login', '/404'];
let pageRefresh = true;
router.beforeEach(async (to, from) => {
    // 如果是login或者404页面直接跳转过去
    if (whiteList.indexOf(to.path) !== -1) {
        if (to.path == '/login') {
            pageRefresh = true;
        }
        return true;
    }
    // 获取token,如果用户没登录,则跳转登录页面
    const token = store.getters.token;
    if (!token) {
        router.push({
            path: 'login',
            query: {
                redirect: to.path,
            },
        });
    } else {
        //如果为true说明已添加过直接跳过
        if (!pageRefresh) {
            return true;
        } else {
            //后端获取动态路由
            await store
                .dispatch('permission/generateRoutes') // 到store下的permission中执行路由添加
                .then((res: any) => {
                    console.log(router.getRoutes());
                    // 没有子路由时则回首页
                    if (res && to.matched.length === 0) {
                        router.push(to.fullPath);
                    }
                });
            pageRefresh = false;
        }
    }
});
import { asyncRoutes, router } from '@/router';
import { RouteRecordRaw } from 'vue-router';
const _modules = import.meta.glob('@/pages/**/*.vue');
/**
 * 后台查询的菜单数据拼装成路由格式的数据
 * @param routes
 */
export function generaMenu(routes: any, data: any) {
    data.forEach((item: any) => {
        const menu = {
            path: item.path,
            component: () => _modules[`/src/pages${item.component}.vue`],  // 这里有坑,动态引入的组件无法添加,我尝试了很多方式,最后使用## defineAsyncComponent引入解决的
            children: [],
            name: item.menuName,
            meta: {
                title: item.title,
                icon: item.icon,
                noCache: item.noCache,
            },
        };
        if (item.children) {
            generaMenu(menu.children, item.children);
        }
        routes.push(menu);
    });
}
const permission = {
    namespaced: true,
    state() {
        return {
            routes: [],
        };
    },
    mutations: {
        SET_ROUTES: (state: any, routes: any) => {
            state.routes = routes;
        },
    },
    actions: {
        generateRoutes({ commit }: any) {
            return new Promise((resolve) => {
                getRoutes().then((res: any) => {
                    if (res.code === 200) {
                        const { data } = res;
                        const list = data[0].children;
                        //获取router添加到asyncrouter中

                        generaMenu(asyncRoutes, list);
                        console.log('调整好的路由', asyncRoutes);
                        // 添加路由
                        asyncRoutes.forEach((itemRouter: RouteRecordRaw) => {
                            router.addRoute('layout', itemRouter);
                        });
                        commit('SET_ROUTES', list);
                        resolve(list);
                    } else {
                        ElMessage.error('菜单数据获取异常');
                    }
                });
            });
        },
    },
};
export default permission;

end:到这里我们基本完成了所有的配置,赶紧尝试一下吧!!!