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

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

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

1. Vite 项目初始化配置

初始化

两者速度比较

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

开箱即用

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

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

  • 安装 vite

    1
    yarn create vite

    按照提示输入就ok了

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

项目初始化

配置别名

修改 vite.config.ts 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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”

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

    在 tsconfig.json 中配置 baseUrl 和 paths

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    {
    "compilerOptions": {
    "baseUrl": "./",
    "paths": {
    "@": ["src/*"],
    "comps/*": ["src/components/*"],
    "store/*": ["src/store/*"]
    }
    }
    }

2. 清空默认文件

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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 -> 粘贴以下代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    {
    "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版本

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import { createStore } from "vuex";

    export interface State { }

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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

  • 安装

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <template>
    <router-view></router-view>
    </template>

    <script lang="ts">
    export default {
    name: "App",
    };
    </script>

    <style></style>

  • 在 src 目录下新建 router/index.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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 修改

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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

1
yarn add less

6. Ant Design of Vue 安装及配置

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

  • 安装

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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);
  • 局部导入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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

    1
    yarn add vite-plugin-imp -D

    修改 vite.config.ts 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    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 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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差不多

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

  • 安装

    1
    yarn add element-plus
  • 自动导入

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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网址:https://github.com/axios/axios

  • 安装

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

    对axios进行二次封装

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    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.loading('请稍等……', 0)
    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进行分模块编写

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import { request } from "../request";

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <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>

    u1s1 还写了挺久的。。。写了大半天时间ε=(´ο`*)))

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