Vue3 Bear Admin项目指南

项目初始化

  1. pnpm create vite vue3-bear-admin --template vue-ts
  2. 提交Git远程仓库
    1
    2
    3
    4
    5
    6
    
    git init
    git add .
    git commit -m "first commit"
    git branch -M main
    git remote add origin https://github.com/${你的用户名}/vue3-bear-admin.git
    git push -u origin main
    

目录介绍

package.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
  "name": "vue3-bear-admin",
  "private": true,
  "version": "0.0.0",
  // 声明整个项目是 ES 模块。
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc -b && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "vue": "^3.4.29"
  },
  "devDependencies": {
    "@types/node": "^20.14.9",
    "@vitejs/plugin-vue": "^5.0.5",
    "typescript": "^5.2.2",
    "vite": "^5.3.1",
    "vue-tsc": "^2.0.21"
  }
}

tsconfig.json

tsconfig.json 用于设置基本的配置、路径别名和项目引用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "files": [],
  "references": [
    {
      "path": "./tsconfig.app.json"
    },
    {
      "path": "./tsconfig.node.json"
    }
  ]
}

tsconfig.app.jsontsconfig.node.json 的使用取决于项目中不同的构建和运行场景。下面解释这两个配置文件在不同场景下的使用情况。

tsconfig.app.json

用于配置前端代码的 TypeScript 编译选项。例如:

  • 当你在 VSCode 或其他 IDE 中编辑前端 TypeScript 代码时,IDE 会根据 tsconfig.app.json 中的配置提供类型检查、代码补全和其他静态分析功能。

tsconfig.node.json

用于配置 vite.config.ts 代码的 TypeScript 编译选项。

项目配置

路径别名

配置 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
import vue from '@vitejs/plugin-vue';
import { resolve } from "path";
import { defineConfig } from 'vite';

/** 路径查找 */
const pathResolve = (dir: string): string => {
  return resolve(__dirname, ".", dir);
};

/** 设置别名 */
const alias: Record<string, string> = {
  "@": pathResolve("src"),
  "@build": pathResolve("build")
};

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias
  },
})

解决编辑器中的错误提示:无法找到模块“path”的声明文件

原因

出现这种警告的原因是 TypeScript 找不到 path 模块的类型声明文件。虽然 Node.js 本身提供了 path 模块,但在 TypeScript 项目中,我们需要额外安装类型声明文件来获得类型支持。

解决方案

可以通过以下步骤解决这个问题:

  1. 安装 @types/node

    path 模块的类型声明文件包含在 @types/node 包中。您可以使用以下命令安装它:

    如果您使用 npm

    1
    
    npm install --save-dev @types/node
    

    或者,如果您使用的是 pnpm

    1
    
    pnpm add -D @types/node
    

配置 tsconfig.app.json

新增compilerOptions.pathscompilerOptions.typesexcludebaseUrl属性配置

 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
{
  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
  "exclude": [
    "node_modules",
    "dist"
  ],
  "compilerOptions": {
    "composite": true,
    "types": ["node"], // 优化编译性能。只会加载 Node.js 的类型声明,而不会加载其他库(如 React、Lodash 等)的类型声明
    "baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
    "paths": { // 用于定义模块路径的别名。
      "@/*": ["src/*"],
      "@build/*": ["build/*"]
    },
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
    "target": "ES2020",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "moduleDetection": "force",
    "noEmit": true,
    "jsx": "preserve",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  
}

验证

NOTE: 一定重启您的开发环境(如 VSCode),以确保新的配置被正确加载。

App.vue组件中使用路径别名:

1
2
3
4
// src/App.vue
<script setup lang="ts">
import HelloWorld from '@/components/HelloWorld.vue';
</script>

兼容CommonJS 和 ES6 模块

新增compilerOptions.allowSyntheticDefaultImportscompilerOptions.esModuleInterop属性

启用 allowSyntheticDefaultImports 后,即使模块没有默认导出,你也可以使用默认导入语法。这在处理一些旧的或非标准的 JavaScript 模块时特别有用,因为这些模块可能没有使用 ES6 模块标准导出默认值。

1
2
3
4
5
6
7
8
9
 // tsconfig.json
{
  "compilerOptions": {
    // 当设置为 true 时,allowSyntheticDefaultImports 允许默认导入非 ES6 模块(如 CommonJS 模块)。默认情况下,TypeScript 会将 import something from 'module' 转换为 import something = require('module').default。如果模块没有默认导出,这会导致运行时错误。allowSyntheticDefaultImports 允许 TypeScript 假设每个模块都有一个默认导出,即使它不是用 ES6 语法编写的。
    "allowSyntheticDefaultImports": true,
    // esModuleInterop 配置项与 allowSyntheticDefaultImports 紧密相关。当设置为 true 时,它允许 TypeScript 更好地遵循 ES6 模块互操作性规范,特别是关于默认导入的行为。具体来说,它会确保 CommonJS 和 ES6 模块之间的互操作性,允许从 CommonJS 模块中导入默认成员,即使它们没有显式地声明默认导出。
    "esModuleInterop": true,
  }
}

按需导入

  1. 安装依赖

    1
    
    pnpm add -D unplugin-auto-import unplugin-vue-components
    

    其中:

    • unplugin-auto-import 自动导入API(在SFC内)。 例如vue的各种api。
      1
      2
      
      import {ref, reactive} from 'vue'// 配置自动导入,用来省略这句手动引入
      const test = ref('aaaa') 
      
    • unplugin-vue-components 按需自动加载组件,插件会自动解析模板中的使用到的组件,并导入组件。
  2. 配置vite.config.ts

    a. AutoImport 插件配置eslintrc 选项: 它的作用是自动生成 ESLint 配置文件,以确保自动导入的内容在代码质量检查中不会引起问题。

     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
    
    // vite.config.ts
    import { defineConfig } from 'vite'
    // 按需注册全局导入 Register global imports on demand
    import AutoImport from 'unplugin-auto-import/vite'
    // 按需导入组件 Components auto importing for Vue
    import Components from 'unplugin-vue-components/vite'
    
    
    export default defineConfig({
      // ...
      plugins: [
        // ...
        AutoImport({
          // 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
          imports: ["vue"],
          resolvers: [],
          // 生成自动导入的TS声明文件的路径
          dts: "/auto-import.d.ts",
          eslintrc: {
            // 是否启用自动生成 ESLint 规则文件。
              // 1. 初次配置时设为 true 用于生成 ESLint 配置。启用时,插件会根据自动导入的内容生成一个 ESLint 配置文件,通常用于初次配置或需要更新规则时。
              // 2. 在初次生成配置文件后改回 false。禁用时,避免重复生成 ESLint 配置文件,节省构建资源和时间。
            enabled: true,
            // 指定生成的 ESLint 配置文件的路径。生成的文件将包含自动导入的函数和全局变量定义,确保 ESLint 能正确识别这些内容。
              // 在您的主 ESLint 配置文件 .eslintrc 中,引用生成的 .eslintrc-auto-import.json 文件
            filepath: "./.eslintrc-auto-import.json",
            // 指定生成的 ESLint 配置文件中全局变量的属性值。
              // true: 将全局变量标记为可读写(writable),意味着这些变量可以被重新赋值。
              // false: 将全局变量标记为只读(readonly),意味着这些变量不能被重新赋值。
            globalsPropValue: true,
          },
        }),
        Components({
          resolvers: [],
          // 生成自动导入组件的TS类型声明文件路径
          dts: "types/components.d.ts",
        }),],
      ],
    })
    
  3. 配置tsconfig.json

引入类型声明文件: json { "include": [ // ... "types/*.d.ts" ], }

引入打包分析插件 rollup-plugin-visualizer

安装

1
pnpm add -D rollup-plugin-visualizer

配置vite.config.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
  // ...
  plugins: [
    // ...
    visualizer({
      open: true,          // 构建后自动打开报告
      brotliSize: true,    // 显示 Brotli 压缩后的文件大小
      filename: 'build_report.html' // 报告文件的名称
    })
     ]
})

visualizer 插件的这三个配置项用于控制生成的可视化报告的行为和内容。以下是每个配置项的详细解释:

  1. open: true

    • 作用:构建完成后,自动打开生成的可视化报告文件。
    • 解释:如果设置为 true,当构建完成后,会自动在默认浏览器中打开生成的报告文件,方便开发者立即查看。
  2. brotliSize: true

    • 作用:在生成的报告中显示 Brotli 压缩后的文件大小。
    • 解释:Brotli 是一种高效的压缩算法,比 gzip 更加高效。如果设置为 true,在报告中会额外显示每个文件经过 Brotli 压缩后的大小,帮助开发者了解应用在不同压缩算法下的大小表现。
  3. filename: 'report.html'

    • 作用:指定生成的可视化报告文件的名称。
    • 解释:设置生成的 HTML 报告文件的名称和路径。默认情况下,报告文件会生成在项目的根目录下。你可以通过设置这个选项来更改文件的名称和保存路径。

通过这些配置,你可以在每次构建后自动生成并打开一个包含详细文件大小信息(包括 Brotli 压缩大小)的可视化报告文件 report.html

预览

执行构建后,会自动打开浏览器显示可视化报告

1
pnpm run build

整合ElementPlus

安装

安装依赖:

1
pnpm install element-plus

配置ElementPlus组件按需导入

 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
// vite.config.ts
import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  // ...
  plugins: [
    // ...
    AutoImport({
      resolvers: [
        // ...
        // 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式)
        ElementPlusResolver()
        // ...
      ]
    }),
    Components({
      resolvers: [
        // ...
        // 自动导入 Element Plus 组件
        ElementPlusResolver()
        // ...
      ]
    }),
  ],
})

对比ElementPlus按需导入和完整引入的打包大小: ElementPlus按需导入和完整引入的打包大小对比

配置 Icon 自动导入

安装

1
pnpm add -D unplugin-icons

配置vite.config.ts

保存以下配置后,会自动安装相关依赖,如@iconify-json/ep

 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
 import { defineConfig } from 'vite'
 import AutoImport from 'unplugin-auto-import/vite'
 import Components from 'unplugin-vue-components/vite'
 import Icons from "unplugin-icons/vite";
 import IconsResolver from "unplugin-icons/resolver";

  export default defineConfig({
    // ...
    plugins: [
      // ...
      AutoImport({
        resolvers: [
          // ...
          // 自动导入图标组件
          IconsResolver({}),
          // ...
        ]
      }),
      Components({
        resolvers: [
          // ...
          // 自动注册图标组件
          IconsResolver({
            // 前缀
            prefix: "i",
            enabledCollections: ["ep"],
          }),
          // ...
        ]
      }),
      Icons({
        // 自动安装图标库
        autoInstall: true,
      }),
    ],
  })

使用示例:

  • 命名规范
    根据 unplugin-icons 插件的文档,默认情况下,图标组件使用 i-{collection}-{icon} 这种命名规范,其中i为默认前缀,支持自定义.

    1
    2
    3
    4
    
    // 以 i 开头,即你配置的前缀
    <IEpMenu />
    
    <i-ep-Menu />
    

参考资源

整合Pinia

安装Pinia

1
pnpm add pinia

配置

  1. 新建src/store文件夹

  2. 新建src/store/index.ts文件

    1
    2
    3
    4
    5
    
    // src/store/index.ts
    import { createPinia } from 'pinia'
    
    const pinia = createPinia()
    export default pinia
    
  3. 配置src/main.ts

    1
    2
    3
    4
    
    // ...
    import pinia from './store'
    app.use(pinia)
    app.mount('#app')
    
  4. 配置自动导入

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    export default defineConfig({
    // ...
    plugins: [
        // ...
        AutoImport({
          imports: [
            // ...
            "pinia"
            // ...
          ]
        }),
      ],
    })
    

新建一个store

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// src/store/userStore.ts
import { defineStore } from 'pinia'

// 你可以任意命名 `defineStore()` 的返回值,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。
// (比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useUserStore = defineStore('adminUser', {
    state: () => ({ count: 0 }),
    getters: {
      double: (state) => state.count * 2,
    },
    actions: {
      increment() {
        this.count++
      },
    },
})

使用示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// src/components/HelloWorld.vue
<script setup lang="ts">
import { useUserStore } from '@/store/userStore';
defineProps<{ msg: string }>()

const userStore = useUserStore()
const { count } = storeToRefs(userStore)

</script>

<template>
  <h1>{{ msg }}</h1>

  <div class="card">
    <button type="button" @click="userStore.increment()">useStore count is {{ count }}</button>
  </div>
</template>

整合Vue-Router

安装

1
pnpm add vue-router@4

配置

  1. 新建src/router 文件夹

  2. 新建src/router/index.ts文件

    1
    2
    3
    4
    5
    
    // src/router/index.ts
    import { createPinia } from 'pinia'
    
    const pinia = createPinia()
    export default pinia
    
  3. 配置src/main.ts

    1
    2
    3
    4
    
    // ...
    import router from '@/router'
    app.use(router)
    app.mount('#app')
    
  4. 配置自动导入

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    export default defineConfig({
    // ...
    plugins: [
        // ...
        AutoImport({
          imports: [
            // ...
            "vue-router"
            // ...
          ]
        }),
      ],
    })
    

安装SCSS

1
pnpm add -D sass

环境变量

配置文件

项目根目录新建 .env.development.env.production

配置反向代理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// ...
export default defineConfig({
  // ...
  server: {
    proxy: {
      "/fp": {
        // 这里填写本地环境后端地址
        target: "http://127.0.0.1:18091",
        changeOrigin: true,
        rewrite: path => path.replace(/^\/fp/, "")
      }
    }
  },
  // ...

配置Mockjs

安装依赖

1
2
3
pnpm add -D mockjs
# 使用 vite-plugin-mock 插件来简化 Mock.js 的集成
pnpm add -D vite-plugin-mock

配置

1
2
// vite.config.ts
import { viteMockServe } from 'vite-plugin-mock';

ps: 更多关于Mockjs的信息参考:Mockjs指南

整合Axios

1
pnpm add axios

创建实例

配置请求拦截器

配置响应拦截器

配置的优先级

配置将会按优先级进行合并。它的顺序是:在lib/defaults.js中找到的库默认值,然后是实例的 defaults 属性,最后是请求的 config 参数。后面的优先级要高于前面的。

Q & A

接口请求载荷多了一堆无用的内容?

问题详情

image

原因

使用了Vue的响应式对象作为请求数据。而Vue的响应式对象会携带一些内部属性,这些属性会在发送请求时一并发送出去。 image

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计