Vue3 + Vite + Typescript + Ant Design Vue / Element-plus 搭建后台管理项目

6,812 阅读2分钟

前段时间使用了Vite + Typescript + Ant Design Vue搭建项目,写下搭建步骤,方便下次使用~

根据项目的实际情况进行配置

1. Vite 项目初始化配置

初始化

两者速度比较

对比 Vue-cli(基于 Webpack),Vite 非常非常快❤!

开箱即用

vite 官方中文文档:cn.vitejs.dev/guide/

个人喜欢用yarn,命令简单统一

  • 安装 vite

    yarn create vite
    

    按照提示输入就ok了

  • 浏览器打开查看效果:http://localhost:3000/

项目初始化

配置别名

修改 vite.config.ts 配置文件

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";
const resolve = (dir: string) => path.join(__dirname, dir);

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      "@/": resolve("src/*"),
      comps: resolve("src/components"),
      store: resolve("src/store"),
    },
  },
});

  • 报错:找不到模块“path”或其相应的类型声明;找不到名称“__dirname”

    yarn add @types/node --save-dev
    
  • 报错:Cannot find module 'store/index' or its corresponding type declarations.

    在 tsconfig.json 中配置 baseUrl 和 paths

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

2. 清空默认文件

新建 public/css/reset.css 文件,清除浏览器默认样式,并在main.ts引入 import '../public/css/reset.css'

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 100%;
	font: inherit;
	vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure, 
footer, header, hgroup, menu, nav, section {
	display: block;
}
body {
	line-height: 1;
}
ol, ul {
	list-style: none;
}
blockquote, q {
	quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
	content: '';
	content: none;
}
table {
	border-collapse: collapse;
	border-spacing: 0;
}

src/assetssrc/components目录下的所有文件都删除

打开App.vue文件,删除所有代码,使用快捷键vue生成模板

  • vscode设置模板

    【文件】 -> 【首选项】 -> 【用户片段】 -> 【新建全局代码片段文件】 -> 输入文件名 vue-ts-less -> 粘贴以下代码

    {
    	"Print to console": {
    		"prefix": "vue-ts-less",
    		"body": [
    			"<template></template>",
    			"",
    			"<script lang=\"ts\">",
    			"import { defineComponent } from \"vue\";",
    			"export default defineComponent({",
    			"  components: {},",
    			"  setup() {",
    			"    return {};",
    			"  },",
    			"});",
    			"</script>",
    			"",
    			"<style lang=\"less\" scoped>",
    			"</style>",
    			"$2"
    		],
    		"description": "Vue-ts-less模板"
    	}
    }
    

    在vue文件输入 vue-ts-less 后按Tab键生成对应的代码块

3. Vuex 配置

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

  • 多个组件使用相同数据的
  • 组件深嵌套

存在上述情况使用vuex会更为合适

“单向数据流”理念的简单示意

Vuex 官方文档:next.vuex.vuejs.org/

  • 安装Vuex,使用4.x版本

    yarn add vuex@next --save
    
  • 在 src 目录下创建 store/index.ts

    import { InjectionKey } from "vue";
    import { createStore, useStore as baseUseStore, Store } from "vuex";
    import type { App } from "vue";
    
    // 引入对应的模块
    import { users } from "./modules/users";
    
    // 手动声明 state 类型
    export interface State { } 
    
    // 定义注入类型
    const key: InjectionKey<Store<State>> = Symbol();
    
    const store = createStore<State>({
      state() { },
      mutations: {},
      actions: {},
      // 使用模块
      modules: { users },
    });
    
    // 将类型注入useStore,项目中引用的均为自定义的这个,覆盖了vuex提供的useStore
    export function useStore() {
      return baseUseStore(key);
    }
    
    export function setupStore(app: App<Element>) {
      app.use(store, key);
    }
    
    export default store;
    
    
  • 创建 store/modules文件夹,该文件夹里可以新建各个module文件

    例如 user.ts

    import { createStore } from "vuex";
    
    export interface State { }
    
    export const users = createStore<State>({
        state: () => ({}),
        getters: {},
        mutations: {},
        actions: {},
    })
    
  • 在main.ts中加入

    import { createApp } from "vue";
    import { setupStore } from "./store"; 
    import App from "./App.vue";
    
    const app = createApp(App);
    
    setupStore(app);
    
    app.mount("#app");
    

4. Vue Router 4.x 配置

功能包括:

  • 嵌套路由映射
  • 动态路由选择
  • 模块化、基于组件的路由配置
  • 路由参数、查询、通配符
  • 展示由 Vue.js 的过渡系统提供的过渡效果
  • 细致的导航控制
  • 自动激活 CSS 类的链接
  • HTML5 history 模式或 hash 模式
  • 可定制的滚动行为
  • URL 的正确编码

Vue Router 官方中文文档:next.router.vuejs.org

  • 安装

    yarn add vue-router@4
    
  • 修改App.vue文件

    加上 router-view 标签才会起作用

    <template>
      <router-view></router-view>
    </template>
    
    <script lang="ts">
      export default {
        name: "App",
      };
    </script>
    
    <style></style>
    
    
  • 在 src 目录下新建 router/index.ts

    import { createRouter, createWebHistory } from "vue-router";
    import type { App } from "vue";
    
    // 导入对应的模块
    const Home = () => import("../view/home.vue");
    const Login = () => import("../view/login.vue");
    
    // 写路由
    const routes = [
      { path: "/", component: Home },
      { path: "/login", component: Login },
    ];
    
    const router = createRouter({
      // createWebHashHistory (hash路由)
      // createWebHistory (history路,需要服务器配置支持)
      // createMemoryHistory 带缓存 history 路由
      // 添加baseUrl,createWebHistory(baseUrl)
      history: createWebHistory(),
      routes,
    });
    
    export function setupRouter(app: App<Element>) {
      app.use(router);
    }
    
    export default router;
    
    

    项目大的话可以将路由分模块抽离出来

  • main.ts 修改

    import { createApp } from "vue";
    import { setupStore } from "./store";
    import router, { setupRouter } from "./router"; 
    import App from "./App.vue";
    
    const app = createApp(App);
    
    setupRouter(app);
    setupStore(app); 
    
    router.isReady().then(() => {
      app.mount("#app");
    });
    
    

5. Scass / Scss / Less 预处理器

antd使用的是less

yarn add less

6. Ant Design of Vue 安装及配置

官网:2x.antdv.com/docs/vue

  • 安装

    yarn add ant-design-vue@next --save
    
  • 在 main.ts 完整引入(不推荐)

    import { createApp } from 'vue';
    import Antd from 'ant-design-vue';
    import App from './App';
    import 'ant-design-vue/dist/antd.css'; // 要引入才有样式
    
    const app = createApp();
    app.config.productionTip = false;
    
    app.use(Antd);
    
  • 局部导入

    import { createApp } from 'vue';
    import { Button } from 'ant-design-vue';
    import 'ant-design-vue/es/button/style'; //同样要引入less样式
    import "ant-design-vue/es/button/style/css"; // 加载 CSS
    
    import App from './App';
    
    const app = createApp(App);
    
    app.use(Button).mount('#app');
    
  • ==按需导入(推荐)==

    安装 vite-plugin-imp

    yarn add vite-plugin-imp -D
    

    修改 vite.config.ts 文件

    import { defineConfig } from "vite";
    import vitePluginImp from "vite-plugin-imp";
    
    export default defineConfig({
      plugins: [
        vue(),
        vitePluginImp({
          libList: [
            {
              libName: "ant-design-vue",
              // style: (name) => `ant-design-vue/es/${name}/style/css`, // 加载css
              style: (name) => `ant-design-vue/es/${name}/style`, // 加载less
            },
          ],
        }),
      ],
      css: {
        preprocessorOptions: {
          less: {
            // 自定义定制主题
            modifyVars: { "primary-color": "#1188ff" },
            javascriptEnabled: true,
          },
        },
      },
    });
    
    

    在 src 目录下新增 libs/antdv.ts 文件

    import type { App } from "vue";
    // 新增组件就在这里进行新增就可以了
    import { Button, Radio, Checkbox } from "ant-design-vue";
    const components = [Button, Radio, Checkbox];
    
    export function setupAntd(app: App<Element>): void {
      components.forEach((component: any) => {
        app.use(component);
      });
    }
    
    

    修改main.ts文件

    import { createApp } from "vue";
    import { setupStore } from "./store"; 
    import router, { setupRouter } from "./router"; 
    import { setupAntd } from "./libs/antdv";  // ++
    import App from "./App.vue";
    
    const app = createApp(App);
    
    setupRouter(app);
    setupStore(app);
    setupAntd(app);  // ++
    
    router.isReady().then(() => {
      app.mount("#app");
    });
    
    

7. Element-plus 安装及配置

这里只讲述使用插件按需导入,其他可以到官网看,和上面的antd差不多

官方文档:element-plus.gitee.io/zh-CN/

  • 安装

    yarn add element-plus
    
  • 自动导入

    yarn add unplugin-vue-components unplugin-auto-import
    
  • 修改 vite.config.ts

    import AutoImport from 'unplugin-auto-import/vite'
    import Components from 'unplugin-vue-components/vite'
    import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
    
    export default {
      plugins: [
        // ...
        AutoImport({
          resolvers: [ElementPlusResolver()],
        }),
        Components({
          resolvers: [ElementPlusResolver()],
        }),
      ],
    }
    
  • 同样的,在 src 目录下新增 libs/elem.ts 文件

    import type { App } from "vue";
    import { ElButton } from 'element-plus'
    
    const components = [ElButton];
    
    export function setupElem(app: App<Element>): void {
        components.forEach((component: any) => {
          app.use(component);
        });
      }
    

    修改main.ts文件

    import { createApp } from "vue";
    import { setupStore } from "./store"; 
    import router, { setupRouter } from "./router"; 
    import { setupElem } from "./libs/element-plus";  // ++
    import App from "./App.vue";
    
    const app = createApp(App);
    
    setupRouter(app);
    setupStore(app);
    setupElem(app);  // ++
    
    router.isReady().then(() => {
      app.mount("#app");
    });
    
    

8. axios 安装及二次封装

Axios 是一个基于 promise 的 HTTP 库,简单的讲就是可以发送get、post等请求

  • 可以在浏览器中发送 XMLHttpRequests
  • 可以在 node.js 发送 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 能够取消请求
  • 自动转换 JSON 数据
  • 客户端支持保护安全免受 XSRF 攻击

github网址:github.com/axios/axios

  • 安装

    yarn add axios
    
  • 在 src 目录下新建 src/service/request.ts

    对axios进行二次封装

    import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
    import { message } from 'ant-design-vue'
    import { useRouter } from "vue-router"
    
    const router = useRouter()
    
    export function request(config: AxiosRequestConfig) {
        // 1 创建实例
        const instance = axios.create({
            baseURL: "xxx",
            timeout: 5000,
            headers: {
                "Content-Type": "application/json",
            },
        });
        // 2 拦截器
        // 请求拦截
        instance.interceptors.request.use(
            (config) => {
                message.loading();
                return config;
            },
            (err) => {
                console.log(err);
            }
        );
        // 响应拦截
        instance.interceptors.response.use((response: AxiosResponse<any>) => {
            message.destroy();
            return response.data;
        }),
            (error: any) => {
                if (error?.response) {
                    switch (error.response.status) {
                        case 400: message.error('请求错误(400)'); break;
                        case 401: router.push({ name: 'login' }); break;
                        case 403: message.error('拒绝访问(403)'); break;
                        case 404: message.error('请求出错(404)'); break;
                        case 408: message.error('请求超时(408)'); break;
                        case 500: message.error('服务器错误(500)'); break;
                        case 501: message.error('服务未实现(501)'); break;
                        case 502: message.error('网络错误(502)'); break;
                        case 503: message.error('服务不可用(503)'); break;
                        case 504: message.error('网络超时(504)'); break;
                        case 505: message.error('HTTP版本不受支持(505)'); break;
                        default: message.error(`连接出错(${error.response.status})!`);
                    }
                } else {
                    message.error('连接服务器失败!');
                }
                message.destroy();
                console.log(error);
            };
        // 3 返回实例,instance本身就是一个pormise
        return instance(config);
    }
    
  • 使用:

    在 service 文件夹中新建api文件夹,在此文件夹中对api进行分模块编写

    import { request } from "../request";
    
    export function xxxx() {
        return request({
            url: `xxx`,
            method: 'get',
            data: {},
            params: {}
        });
    }
    
    

    在使用该api的文件中直接引入即可

    <template>
      <el-button @click="test">测试</el-button>
    </template>
    
    <script lang="ts">
    import { defineComponent } from "vue";
    import { getSometings } from "../service/api/user";
    
    export default defineComponent({
      setup() {
        const test = async () => {
          await getSometings().then((res: any) => {
            console.log(res.data);
          });
        };
        return { test };
      },
    });
    </script>
    
    <style>
    </style>
    

感谢作者:时光足迹 链接:juejin.cn/post/697391… 来源:稀土掘金