前端框架搭建——从零到一搭建一个高颜值Vue3后台管理系统(二)

上一篇我们已经把基础的工程化搭建了,下面就是集成项目所需要的插件,比如elementplus、unocss、mock之类的,因为我们使用的是vite,所以这些插件需要集成在里面。

vite配置

js 复制代码
//vite.config.ts
import { ConfigEnv, defineConfig, loadEnv } from 'vite';
import { setupVitePlugins } from './build';
import pkg from './package.json';
import { resolve } from 'path';
import dayjs from 'dayjs';

const { dependencies, devDependencies, name, version } = pkg;
const __APP_INFO__ = {
  pkg: { dependencies, devDependencies, name, version },
  lastBuildTime: dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss')
};

export default defineConfig((configEnv: ConfigEnv) => {
  const viteEnv = loadEnv(configEnv.mode, process.cwd()) as ImportMetaEnv;

  return {
    base: viteEnv.VITE_BASE_URL,
    resolve: {
      alias: {
        '@': resolve(__dirname, './src'),
        'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js'
      }
    },
    server: {
      host: '0.0.0.0',
      port: 8848,
      open: true,
      https: false,
      // 本地跨域代理 https://cn.vitejs.dev/config/server-options.html#server-proxy
      proxy: {}
    },
    plugins: setupVitePlugins(viteEnv),
    build: {
      sourcemap: false,
      chunkSizeWarningLimit: 2000,
      rollupOptions: {
        output: {
          chunkFileNames: 'static/js/[name]-[hash].js',
          entryFileNames: 'static/js/[name]-[hash].js',
          assetFileNames: 'static/[ext]/[name]-[hash].[ext]'
        }
      }
    },
    define: { __APP_INFO__: JSON.stringify(__APP_INFO__) }
  };
});

很简单的一个vite配置页,我们项目中采用将插件抽离到根目录下的build/plugins中,通过函数加载想要的插件。

下面说下这些子文件的内容

  • compress --->压缩插件
  • index --->入口插件
  • mock --->mock插件
  • unplugin --->其它
  • visualizer --->打包分析

build/index

import 复制代码
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import unocss from '@unocss/vite';
import unplugin from './unplugin'; //UI&Icon
import mock from './mock'; //mock(默认开启)
import compress from './compress'; //压缩工具
import visualizer from './visualizer'; //打包分析
import progress from 'vite-plugin-progress'; //打包进度显示
import VueDevtools from 'vite-plugin-vue-devtools'; //开发工具

/**
 * vite插件
 * @param viteEnv - 环境变量配置
 */
export function setupVitePlugins(viteEnv: ImportMetaEnv): (PluginOption | PluginOption[])[] {
  const plugins = [
    vue(),
    vueJsx(),
    VueDevtools(),
    ...unplugin(viteEnv),
    mock(viteEnv),
    unocss(),
    progress()
  ];
  if (viteEnv.VITE_COMPRESS === 'Y') {
    plugins.push(compress(viteEnv));
  }
  if (viteEnv.VITE_VISUALIZER === 'Y') {
    plugins.push(visualizer);
  }
  return plugins;
}

这里index文件的viteEnv.xxx这个变量,是在env里的配置项

对应elementplus,我们这里采用自动加载引用的插件unplugin-auto-import,对应在unplugin里,这个文件里我们只对elementplus进行自动加载,关于vue、vue-router等插件采用自己手动引用的方式,这里看个人习惯你也可以都自动引入

配置router

下面我们需要创建路由文件结构

下面说下这些子文件的内容

  • guard --->路由拦截
  • helpers --->帮助文件,主要是一些过滤路由的方法
  • modules --->静态路由
  • routes --->常量路由
  • index --->入口

从routes引入常量路由,比如登录、未匹配的页面等。我们首先透过import.meta.env里取出配置文件里的路由选择变量,根据变量判断使用hash还是history

javascript 复制代码
//index.ts
import { App } from 'vue';
import { createRouter, createWebHashHistory, createWebHistory, RouteRecordRaw } from 'vue-router';
import { LoginRoute, RootRoute, PathMatchRoute } from './routes';
import { createRouterGuard } from './guard';
/** 静态路由 */
export const constantRoutes: RouteRecordRaw[] = [LoginRoute, RootRoute, PathMatchRoute];

const { VITE_ROUTE_HASH = 'Y' } = import.meta.env;

const router = createRouter({
  history: VITE_ROUTE_HASH === 'Y' ? createWebHashHistory() : createWebHistory(),
  routes: constantRoutes,
  scrollBehavior: () => ({ left: 0, top: 0 })
});

export async function setupRouter(app: App) {
  app.use(router);
  createRouterGuard(router);
  await router.isReady();
}

export default router;

上面我们只是加载了常态路由,对于其它路由我们采用router.beforeEach通过routeStore里的initRoute函数加载路由,关于路由拦截下面的注释写的还算清楚了

php 复制代码
// guard.ts
import type { Router } from 'vue-router';
import NProgress from '@/utils/nprogress';
import { useUserStore, useRouteStore } from '@/store';

const whiteList = ['/login'];
/**
 * 路由守卫函数
 * @param router - 路由实例
 */
export function createRouterGuard(router: Router) {
  const userStore = useUserStore();
  const routeStore = useRouteStore();
  router.beforeEach(async (to, from, next) => {
    NProgress.start();
    // 访问登录页,有token不做跳转,没有跳转登录页
    if (to.path === '/login') {
      if (userStore.isLogin) return next(from.fullPath);
      return next();
    }
    // 白名单放行
    if (whiteList.includes(to.path)) return next();

    // token不存在,跳登录页
    if (!userStore.isLogin) return next({ path: '/login', query: { redirect: to.fullPath } });

    // 未初始化路由,等待执行
    if (!routeStore.isInitRoute) {
      routeStore.initRoute();
      return next({ path: to.fullPath, replace: true, query: to.query, hash: to.hash });
    }

    // 匹配到未知路径,跳404
    if (to.name === 'not-found') return next({ path: '/error' });

    // 默认放行
    next();
  });

  router.afterEach(() => {
    NProgress.done();
  });
}

上面的文件里使用到了userStorerouteStore,那么就需要配置pinia了,当然你也可以不用pinia,对于vue3来说你可以使用一个响应式充当全局的变量,在本项目中我们需要使用pinia

pinia

我们需要在根目录下新建这样的结构,将模块化归于一个文件中统一暴露出来

pinia的store有两种配置方式,我们这里采用下面的这种方式,结构清晰。initStaticRoute里通过一个filterRoutesByRole函数根据角色来过滤出对应的路由,过滤出来的路由通过遍历使用addRoute追加,无论是静态的路由方式还是动态的路由方式最后其实都是通过addRoute最加进去的

import 复制代码
import router from '@/router';
import { useUserStore } from './user';
import { asyncRouter } from '@/router/modules';
import { filterRoutesByRole, filterRoutesToMenus } from '@/router/helpers';

interface IRouteState {
  /** 权限路由的模式(static|dynamic) */
  routeMode: ImportMetaEnv['VITE_ROUTE_MODE'];
  /** 是否初始化权限路由的生成 */
  isInitRoute: boolean;
  /** 菜单渲染数据 */
  menus: App.Menu[];
  /** 缓存的路由 */
  cacheList: string[];
}

export const useRouteStore = defineStore({
  id: 'route',
  state: (): IRouteState => ({
    routeMode: import.meta.env.VITE_ROUTE_MODE,
    isInitRoute: false,
    menus: [],
    cacheList: []
  }),
  getters: {},
  actions: {
    /** 初始化权限路由 */
    initRoute() {
      if (this.routeMode === 'static') {
        this.initStaticRoute();
      } else {
        this.initDynamicRoute();
      }
    },
    /** 静态权限路由 */
    initStaticRoute() {
      const userStore = useUserStore();
      const routes = filterRoutesByRole(asyncRouter, userStore.userInfo.role);
      routes.forEach((route) => {
        route.children?.length ? router.addRoute(route) : router.addRoute('root', route);
      });
      const menus = filterRoutesToMenus(routes);
      this.setMenus(menus);
      this.isInitRoute = true;
    },
    /** 动态权限路由 */
    initDynamicRoute() {},
    /** 设置菜单 */
    setMenus(menus: App.Menu[]) {
      this.menus = menus;
    }
  }
});

汇总集成

上面我们举例了vite配置、store配置、router配置,但我们还没有把他们集成到项目上去。将build/index的setupVitePlugins、store/index的setupStore、router/index的setupRouter,将三个页面的暴露函数引入到main.ts

javascript 复制代码
import { createApp } from 'vue';
import App from './App.vue';
import install from './plugins';
import { setupRouter } from './router';
import { setupStore } from './store';

async function setupApp() {
  const app = createApp(App);

  // 插件注册&资源引入
  install(app);

  // 引入pinia
  setupStore(app);

  // 引入vue-router
  await setupRouter(app);

  app.mount('#app');
}

void setupApp();

这样看起来,是不是整个页面都简单化了许多,最近事情有点多,又在忙着考试没有更新,下一篇就开始介绍本项目的布局结构

相关推荐
问道飞鱼11 分钟前
【前端知识】强大的js动画组件anime.js
开发语言·前端·javascript·anime.js
k093313 分钟前
vue中proxy代理配置(测试一)
前端·javascript·vue.js
傻小胖14 分钟前
React 脚手架使用指南
前端·react.js·前端框架
程序员海军26 分钟前
2024 Nuxt3 年度生态总结
前端·nuxt.js
m0_7482567837 分钟前
SpringBoot 依赖之Spring Web
前端·spring boot·spring
web135085886351 小时前
前端node.js
前端·node.js·vim
m0_512744641 小时前
极客大挑战2024-web-wp(详细)
android·前端
若川1 小时前
Taro 源码揭秘:10. Taro 到底是怎样转换成小程序文件的?
前端·javascript·react.js
潜意识起点2 小时前
精通 CSS 阴影效果:从基础到高级应用
前端·css
奋斗吧程序媛2 小时前
删除VSCode上 origin/分支名,但GitLab上实际上不存在的分支
前端·vscode