Skip to content

vue3.0+TypeScript+Pina+Vite+ElementPlus项目搭建

Vue2.0 马上停止维护了,Vue3.0 是新项目首选项。从零开始搭建一个Vue3全家桶的项目框架,希望对大家有帮助。

参考资料

插件相关文档

项目搭建技术相关文档

规范文档

功能清单

基础

  • [x] 初始化 项目框架
  • [x] 整理 环境信息

工程化插件

  • [x] 整合 Sass
  • [x] 整合 Autoprefixer
  • [x] 整合 Pinia
  • [x] Pinia 持久化
  • [x] 整合 Zipson
  • [x] 整合 TailWind Css
  • [x] 整合 Element Plus
  • [x] 整合 Vue Router
  • [ ] 整合 Axios
  • [ ] 整合 MockJS
  • [x] 配置别名
  • [ ] polyfill
  • [x] husky
  • [x] lint-staged
  • [x] commitlint/cli
  • [x] Commitizen
  • [x] CZ-git
  • [ ] browserslist
  • [x] 配置 Prettler
  • [x] 配置 Eslint
  • [x] 配置 Stylellint css检查
  • [x] 配置 EditorConfig
  • [ ] 配置 tsconfig
  • [x] 自动导入
  • [ ] 环境变量
  • [ ] 跨域处理
  • [ ] 动态路由
  • [ ] 按钮权限
  • [ ] 国际化
  • [ ] 暗黑模式
  • [x] svg图标库
  • [ ] 阿里图标库
  • [ ] 打包配置
    • [ ] 分包
    • [ ] 生成.gz文件
    • [ ] js和css文件夹分离
  • [ ] 静态资源处理
  • [ ] 组件自动化注册

框架赋能

  • [ ] 组件封装
    • [ ] wangEditor 富文本
    • [ ] Echarts 图表
    • [ ] 图标选择器
  • [ ] 规范配置
    • [ ] 代码统一规范
    • [ ] git提交统一规范

业务功能

  • [ ] 权限控制
  • [ ] 按钮权限
  • [ ] iframe页面容器
  • [ ] 页面弹窗
  • [ ] 菜单
  • [ ] 面包屑
  • [ ] tab标签页
  • [ ] 换肤、主题
  • [ ] 自动表单
  • [ ] 自动表格
  • [ ] 登陆页

文档库 vite press

  • [ ] 项目搭建
  • [ ] 组件文档

基础环境与工具

使用pnpm代替npm

pnpm - 速度快、节省磁盘空间的软件包管理器

shell
npm install -g pnpm

image-20231217065300915

NodeJS

Vite 需要 Node.js 版本 18+,20+。

image-20231217074317971

初始化项目

使用官网命令初始化项目

shell
pnpm create vite

image-20231217091641033

更新依赖

image-20231217091847035

运行项目

image-20231217092056786

界面 http://localhost:5173/

image-20231217092229120

项目结构

和vue2没有太大的区别

image-20231217100643294

增加工程化插件

使用sass

其实现在我们的项目已经完全可以运行起来了,但是项目中有一个 style.css 显然不是我们想要的,vite可以原生的支持 scss 文件,我们一般都是使用 scss 写样式。

增加 common.scss 文件,并在 main.ts 中引入

image-20231217123239413

但是此时我们 npm run dev 运行起来的项目提示错误

image-20231217123322320

这是因为 sass 模块是使用 scss 文件必备的工具,所以我们需要安装 sass,注意:

  • 要继续使用 pnpm 命令安装

  • 增加 -D 参数,在开发环境中才使用 sass,因为生产环境,scss 文件会被编译成 css 文件,这个插件是用不到的

shell
pnpm install sass -D

创建 variables.scss 变量文件,添加变量 $bg-color 定义,注意规范变量以 $ 开头

scss
// src/styles/variables.scss
$md-bg-color:#242424;

Vite 配置导入 SCSS 全局变量文件

typescript
// vite.config.ts
css: {
    // CSS 预处理器
    preprocessorOptions: {
        //define global scss variable
        scss: {
            javascriptEnabled: true,
            additionalData: `@use "@/styles/variables.scss" as *;`
        }
    }
}

style 标签使用SCSS全局变量

vue
<!-- src/components/HelloWorld.vue -->
<template>
  <div class="box" />
</template>

<style lang="scss" scoped>
.box {
  width: 100px;
  height: 100px;
  background-color: $md-bg-color;
}
</style>

上面导入的 SCSS 全局变量在 TypeScript 不生效的,需要创建一个以 .module.scss 结尾的文件

scss
// src/styles/variables.module.scss

// 导出 variables.scss 文件的变量
:export{
    mdBgColor:$md-bg-color
}

TypeScript 使用 SCSS 全局变量

scss
<!-- src/components/HelloWorld.vue -->
<script setup lang="ts">
  import variables from "@/styles/variables.module.scss";
  console.log(variables.bgColor)  
</script>

<template>
  <div style="width:100px;height:100px" :style="{ 'background-color': variables.mdBgColor }" />
</template>

现在我们可以安心的使用 scss 了,别忘了把代码提交一下,我们在开发过程中最好尽量保证每个提交都是一个简单的功能。

使用 autoprefixer

autoprefixer 是 postcss 的功能插件,给 web 项目自动增加 css 前缀,兼容各种浏览器。

安装 autoprefixer

shell
pnpm install autoprefixer -D

在配置文件 vite.config.ts 中配置

image-20231217202655024

配置源码
typescript
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import autoprefixer from 'autoprefixer';

// https://vitejs.dev/config/
export default defineConfig({
    css: {
        postcss: {
            plugins: [
                autoprefixer({// 自动添加前缀 的浏览器
                    overrideBrowserslist: [
                        'cover 99.5%',
                        'Chrome > 31',
                        'ie >=8',
                        'last 2 versions', // 所有主流浏览器最近2个版本
                    ],
                }),
            ],
        },
    },
    plugins: [vue()],
});

打包效果

Autoprefixer 未生效时,运行ppm run build 打包出来的代码没有加上自动前缀。

image-20231217203200080

配置生生效后打包出来的效果

image-20231217203643111

使用Pinia

Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态。 如果您熟悉 Composition API,您可能会认为您已经可以通过一个简单的 export const state = reactive({}). 这对于单页应用程序来说是正确的,但如果它是服务器端呈现的,会使您的应用程序暴露于安全漏洞。 但即使在小型单页应用程序中,您也可以从使用 Pinia 中获得很多好处:

  • dev-tools 支持
    • 跟踪动作、突变的时间线
    • Store 出现在使用它们的组件中
    • time travel 和 更容易的调试
  • 热模块更换
    • 在不重新加载页面的情况下修改您的 Store
    • 在开发时保持任何现有状态
  • 插件:使用插件扩展 Pinia 功能
  • 为 JS 用户提供适当的 TypeScript 支持或 autocompletion
  • 服务器端渲染支持

安装pinia

bash
pnpm install pinia

创建入口

在项目的 src/stores 目录下,新建一个 index.ts 文件作为Pinia的入口文件,并创建根Store

typescript
import { createPinia } from 'pinia';

const store = createPinia();
export default store;

导入store

在 main.ts 中引入使用即可

typescript
import store from './stores'
app.use(store)

在引入store之后,可以在 src/stores 目录下新建一个 modules 文件夹用于定义Sore。这样我们的pinia就可以投入使用了。

创建一个Store

我们在开发中需要根据模块划分状态,这样才方便我们统一管理不同模块下的状态。比如我们开发中会设计到用户模块,因此我们可以在 src/stores/modules 目录下新建一个 user.ts 文件用于用户状态管理,将通过下列代码创建user Store

typescript
import { defineStore } from 'pinia';
import { reactive, toRefs } from 'vue';

interface IUserState {
    name: string;
}

export const useUserStore = defineStore('user', () => {
    // states
    const state = reactive<IUserState>({
        name: 'old name',
    });

    // getters
    const myName = (): string => `getters ${state.name}`;

    // actions
    const changeName = (name: string): void => {
        state.name = name;
    };

    return {
        ...toRefs(state),
        myName,
        changeName,
    };
});

使用Store

应为Store是一个函数,所以在组件中需要调用后才能使用

vue
<script lang="ts" setup>
import { useUserStore } from './stores/modules/user';

const userStore = useUserStore();
</script>

<template>
  <div>
      {{ userStore.name }} <br>
      {{ userStore.myName() }} <br>
      <button @click="userStore.changeName('new Name')">changeName</button> <br>
</template>

界面

image-20231217235118403

点击按钮后

image-20231217235148061

Pinia状态持久化

用过vuex的都知道,有时我们会遇到需要将一些状态进行持久化处理,避免刷新浏览器的时候出现数据丢失。当然,你可以选择手动持久化,即手动的将需要持久化存储的数据添加到localStorage或sessionStore中,在定义state的时候默认从这两个Storage中拿。对于小部分数据来说可行,但如果需要大量持久化状态,这种方式就不是很推荐了。要一个个找不说,代码也会大量重复。

因此,我们这时就要借助插件来完成这个操作了。

安装插件

shell
npm i pinia-plugin-persistedstate

导入插件

在 src/stores/index.ts 中引入插件

typescript
import { createPinia } from 'pinia';
// 引入持久化插件
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const store = createPinia();
// 将插件提供给store实例
store.use(piniaPluginPersistedstate)
export default store;

使用 Store :

useUserStore : src/stores/modules/user.ts

typescript
import { defineStore } from 'pinia';
import { reactive, toRefs } from 'vue';

interface IUserState {
    name: string;
}

export const useUserStore = defineStore('userStore', () => {
    // states
    const state = reactive<IUserState>({
        name: 'old name',
    });

    // getters
    const myName = (): string => `getters ${state.name}`;

    // actions
    const changeName = (name: string): void => {
        state.name = name;
    };

    return {
        ...toRefs(state),
        myName,
        changeName,
    };
}, {
    // 注意defineStore的第三个参数可以传入插件配置
    persist: true,
});

点击按钮修改store里的状态,发现已经被默认存储在了localStorage中。

image-20231218002944924

配置部分数据持久化

那么现在又出现了一个问题:并不是所有的状态都需要持久化呀,只持久化部分数据该怎么办呢?现在就需要对插件进行一个配置了。

在 src/utils 目录下新建 persist.ts 文件,修改存储配置:

typescript
import type { PersistedStateOptions } from 'pinia-plugin-persistedstate';

/**
 * @description pinia持久化参数配置
 * @param {String} key 存储到持久化的 name
 * @param {Array} paths 需要持久化的 state name
 * @return persist
 * */
const piniaPersistConfig = (key: string, paths?: string[]) => {
    const persist: PersistedStateOptions = {
        key,
        storage: window.localStorage,
        // storage: window.sessionStorage,
        paths,
    };
    return persist;
};
export default piniaPersistConfig;

观察代码可知,我们不仅可以存储部分状态,并且可以选择存储方式~

因此修改store中的持久化配置:

typescript
import { defineStore } from 'pinia';
import { reactive, toRefs } from 'vue';
import piniaPersistConfig from '../../utils/persist';

interface IUserState {
    name: string;
    lastName: string;
}

export const useUserStore = defineStore('userStore', () => {
    // states
    const state = reactive<IUserState>({
        name: 'old name',
        lastName: 'last name',
    });

    // getters
    const myName = (): string => `getters ${state.name}`;

    // actions
    const changeName = (name: string): void => {
        state.name = name;
    };

    return {
        ...toRefs(state),
        myName,
        changeName,
    };
}, {
    // 注意defineStore的第三个参数可以传入插件配置
    // persist: true,
    persist: piniaPersistConfig('userStore', ['lastName']),
});

image-20231218004709230

从图片中我们可以看到,只有'lastName'被持久化缓存了下来。

Pinia全局配置前缀

可以全局添加前缀,在 src/stores/index.ts

typescript
import { createPinia } from 'pinia';
// 引入持久化插件
import { createPersistedState } from 'pinia-plugin-persistedstate';


const pinia = createPinia();
// 将插件提供给store实例
pinia.use(createPersistedState({
    key: id => `__md_persisted__${id}`,
}));
export default pinia;

image-20231219010216581

Zipson 压缩数据

每个浏览器对localStorage 是有限制大小的,你不压缩,有时候你触发了某个bug,你都不知道怎么回事。

安装插件

shell
pnpm install --save zipson

导入插件

typescript
import { parse, stringify } from 'zipson';

配置 persist

src/utils/persist.ts

typescript
import type { PersistedStateOptions,StorageLike } from 'pinia-plugin-persistedstate';
import { parse, stringify } from 'zipson';

/**
 * @description pinia持久化参数配置
 * @param {String} key 存储到持久化的 name
 * @param {Array} paths 需要化的 state name持久
 * @param storage 存储的位置 locationStorage sessionStorage
 * @return persist
 * */
const piniaPersistConfig = (key: string, paths: string[], storage: StorageLike = localStorage) => {
    const persist: PersistedStateOptions = {
        key,
        paths,
        storage,
        serializer: {
            deserialize: parse,
            serialize: stringify,
        }
    };
    return persist;
};
export default piniaPersistConfig;

image-20231219011103696

安装 Tailwind CSS

Tailwind CSS 的工作原理是扫描所有 HTML 文件、JavaScript 组件和任何其他类名称模板,生成相应的样式,然后将它们写入静态 CSS 文件。

它快速、灵活且可靠 — 具有零运行时间。

安装插件

通过 npm 安装 tailwindcss 及其对等依赖项,并创建你的 tailwind.config.js 文件。

shell
pnpm install -D tailwindcss postcss autoprefixer

将 Tailwind 添加到你的 PostCSS 配置中

shell
npx tailwindcss init -p

运行上面指令生成 postcss.config.js 、tailwindcss.config.js 文件

配置 postcss.config.js

typescript
export default {
    plugins: {
        tailwindcss: {},
        autoprefixer: {},
    },
};

配置 tailwindcss.config.js

typescript
/** @type {import('tailwindcss').Config} */
export default {
  content: ["index.html","./src/**/*.{vue,html,js,jsx,ts,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
}

配置 tailwind.css

src/assets/style/tailwind.css

scss
@tailwind base;
@tailwind components;
@tailwind utilities

导入css

src/main.ts

image-20231219144505969

使用 tailwind

code
vue
<script lang="ts" setup>
</script>

<template>
<div class="flex p-6 font-mono">
  <div
      class="flex-none w-48 mb-10 relative z-10 before:absolute before:top-1 before:left-1 before:w-full before:h-full before:bg-teal-400">
    <img alt="" class="absolute z-10 inset-0 w-full h-full object-cover rounded-lg" loading="lazy"
         src="./assets/vue.svg" />
  </div>
  <form class="flex-auto pl-6">
    <div
        class="relative flex flex-wrap items-baseline pb-6 before:bg-black before:absolute before:-top-6 before:bottom-0 before:-left-60 before:-right-6">
      <h1 class="relative w-full flex-none mb-2 text-2xl font-semibold text-white">
        Retro Shoe
      </h1>
      <div class="relative text-lg text-white">
        $89.00
      </div>
      <div class="relative uppercase text-teal-400 ml-3">
        In stock
      </div>
    </div>
    <div class="flex items-baseline my-6">
      <div class="space-x-3 flex text-sm font-medium">
        <label>
          <input checked class="sr-only peer" name="size" type="radio" value="xs" />
          <div
              class="relative w-10 h-10 flex items-center justify-center text-black peer-checked:bg-black peer-checked:text-white before:absolute before:z-[-1] before:top-0.5 before:left-0.5 before:w-full before:h-full peer-checked:before:bg-teal-400">
            XS
          </div>
        </label>
        <label>
          <input class="sr-only peer" name="size" type="radio" value="s" />
          <div
              class="relative w-10 h-10 flex items-center justify-center text-black peer-checked:bg-black peer-checked:text-white before:absolute before:z-[-1] before:top-0.5 before:left-0.5 before:w-full before:h-full peer-checked:before:bg-teal-400">
            S
          </div>
        </label>
        <label>
          <input class="sr-only peer" name="size" type="radio" value="m" />
          <div
              class="relative w-10 h-10 flex items-center justify-center text-black peer-checked:bg-black peer-checked:text-white before:absolute before:z-[-1] before:top-0.5 before:left-0.5 before:w-full before:h-full peer-checked:before:bg-teal-400">
            M
          </div>
        </label>
        <label>
          <input class="sr-only peer" name="size" type="radio" value="l" />
          <div
              class="relative w-10 h-10 flex items-center justify-center text-black peer-checked:bg-black peer-checked:text-white before:absolute before:z-[-1] before:top-0.5 before:left-0.5 before:w-full before:h-full peer-checked:before:bg-teal-400">
            L
          </div>
        </label>
        <label>
          <input class="sr-only peer" name="size" type="radio" value="xl" />
          <div
              class="relative w-10 h-10 flex items-center justify-center text-black peer-checked:bg-black peer-checked:text-white before:absolute before:z-[-1] before:top-0.5 before:left-0.5 before:w-full before:h-full peer-checked:before:bg-teal-400">
            XL
          </div>
        </label>
      </div>
    </div>
    <div class="flex space-x-2 mb-4 text-sm font-medium">
      <div class="flex space-x-4">
        <button class="px-6 h-12 uppercase font-semibold tracking-wider border-2 border-black bg-teal-400 text-black"
                type="submit">
          Buy now
        </button>
        <button class="px-6 h-12 uppercase font-semibold tracking-wider border border-slate-200 text-slate-900"
                type="button">
          Add to bag
        </button>
      </div>
      <button aria-label="Like" class="flex-none flex items-center justify-center w-12 h-12 text-black" type="button">
        <svg aria-hidden="true" fill="currentColor" height="20" width="20">
          <path clip-rule="evenodd"
                d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z"
                fill-rule="evenodd" />
        </svg>
      </button>
    </div>
    <p class="text-xs leading-6 text-slate-500">
      Free shipping on all continental US orders.
    </p>
  </form>
</div>
</template>

image-20231219144700471

运行成功了 看到上面界面。

异常情况:

Tailwind Css不生效

按照上述步骤操作后没有见到效果

image-20231219145500970

下面我解决这个问题

找到vite.config.js,我们配置的css模块和文件postcss里内容冲突,我们将这里css配置删掉。修改postcss.config.js

image-20231219145624500

postcss.config.js

typescript
export default {
    plugins: {
        tailwindcss: {},
        autoprefixer: {
            overrideBrowserslist: [
                'cover 99.5%',
                'Chrome > 31',
                'ie >=8',
                'last 2 versions', // 所有主流浏览器最近2个版本
            ],
        },
    },
};

​ 然后重新运行项目就能看到效果了

配置IDEA中自动提示

如果 idea 不提示 tailwindcss 的标签,那么可以尝试将安装postcss的版本降低到

shell
pnpm install @tailwindcss/postcss7-compat@2.1.4 --save # "@tailwindcss/postcss7-compat": "2.1.4",
pnpm install tailwindcss@latest # "tailwindcss": "^3.3.7",

此外,插件市场记得安装下面的三个插件,这样提示效果会更加全面:

image-20231219164023112

image-20231219164815126

配置VSCode中自动提示

参考资料:分享4个不可或缺的 VSCode 插件,让 Tailwind CSS开发更简单

  • Tailwind CSS IntelliSense
  • Tailwind Fold
  • Tailwind Documentation
  • Tailwind Config Viewer

使用 Element Plus

安装插件

bash
pnpm install element-plus # "element-plus": "^2.4.4",

导入组件库

建议使用按需导入

方案一:完整引入

如果你对打包后的文件大小不是很在乎,那么使用完整导入会更方便。 src/main.ts

typescript
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'

const app = createApp(App)

app.use(ElementPlus)
app.mount('#app')
方案二:按需导入

您需要使用额外的插件来导入要使用的组件。官方推荐用这种方式

首先你需要安装unplugin-vue-components 和 unplugin-auto-import这两款插件

shell
pnpm install -D unplugin-vue-components unplugin-auto-import # "unplugin-vue-components": "^0.26.0","unplugin-auto-import": "^0.17.2",

然后把下列代码插入到你的 Vite 的配置文件中

vite.config.ts

typescript
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: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
})

使用组件

src/App.vue

![image-20231219210037636](./images/vue3.0+TypeScript+Pina+Vite+ElementPlus项目搭建/image-20231219210037636.png

使用 Vue Router

参考资料

安装插件

bash
pnpm install vue-router@4 # "vue-router": "4",

创建页面

在 src 路径下新建 pages 文件夹

新建 account.vue
src/pages/account.vue
vue
<template>
    <div @click="toLogin">toLogin</div>
    <router-view></router-view>
</template>
<script lang="ts" setup>

import { useRoute, useRouter } from 'vue-router';

const router = useRouter();

function toLogin() {
    router.push({
        name: 'login',
    });
}
</script>

<style scoped>

</style>
新建login.vue
src/pages/login.vue
vue
<template>
    <div @click="toAccount">toAccount</div>
    <router-view></router-view>
</template>
<script lang="ts" setup>

import { useRoute, useRouter } from 'vue-router';

const router = useRouter();

function toAccount() {
    router.push({
        name: 'account',
    });
}
</script>

新建router配置

在 src 路径下新建 router/index.ts

src/router/index.ts
typescript
/**
 * @FileName index.ts.ts
 * @Author Mad Dragon <395548460@qq.com>
 * @Version V 0.0.1
 * @Date 2023/12/25 22:12
 * @Title
 * @Desc
 **/
import { createRouter, createWebHistory } from 'vue-router';

export const routes = [
    {
        path: '/',
        redirect: '/login',
    },
    {
        name: 'login',
        path: '/login',
        component: () => import('../pages/login.vue'),
    },
    {
        name: 'account',
        path: '/account',
        component: () => import('../pages/account.vue'),
    },

];

const router = createRouter({
    scrollBehavior: () => ({left: 0, top: 0}),
    history: createWebHistory(),
    routes,
});


router.beforeEach((to, from, next) => {
    next();
});


export default router;

引用router

在main.ts

src/main.ts
vue
import { createApp } from 'vue';
import './assets/style/tailwind.scss';
import './style.css';
import './assets/style/test.scss';
import App from './App.vue';
import store from './stores';
import router from './router';

const app = createApp(App);
app.use(store);
app.use(router);
app.mount('#app');

设置router view

删掉之前没啥用的代码

src/App.vue
vue
<script lang="ts" setup>
</script>

<template>
    <router-view></router-view>
</template>

<style lang="scss">
</style>

结果

点击可以切换路由

image-20231225223401900

image-20231225223424666

路径别名配置

相对路径别名配置,使用 @ 代替 src

安装 @types/node

bash
pnpm i @types/node -D # "@types/node": "^20.10.5",

TypeScirpt 编译器配置

tsconfig.json
typescript
{
    "compilerOptions": {
        "target": "ES2020",
        "useDefineForClassFields": true,
        "module": "ESNext",
        "lib": [
            "ES2020",
            "DOM",
            "DOM.Iterable"
        ],
        "skipLibCheck": true,
        /* Bundler mode */
        "moduleResolution": "bundler",
        "allowImportingTsExtensions": true,
        "resolveJsonModule": true,
        "isolatedModules": true,
        "noEmit": true,
        "jsx": "preserve",
        /* Linting */
        "strict": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "noFallthroughCasesInSwitch": true,
        // 解析非相对模块的基地址,默认是当前目录
        "baseUrl": "./",
        "paths": {
            // 映射路径 相对于 baseUrl
            "@/*": [
                "src/*",
            ],
            "@a/*": [
                "src/assets/*",
            ],
            "@css/*": [
                "src/assets/style/*",
            ],
            "@r/*": [
                "src/router/*",
            ],
            "@p/*": [
                "src/pages/*",
            ],
            "@s/*": [
                "src/stores/*",
            ],
            "@u/*": [
                "src/utils/*",
            ],
            "@@/*": [
                "src/components/*",
            ],
        }
    },
    "include": [
        "src/**/*.ts",
        "src/**/*.tsx",
        "src/**/*.vue"
    ],
    "references": [
        {
            "path": "./tsconfig.node.json"
        }
    ]
}

Vite 配置

在 vite.config.ts 中设置
type
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
import { resolve } from 'path';

// https://vitejs.dev/config/
export default defineConfig({
    plugins: [
        vue(),
        AutoImport({
            resolvers: [ElementPlusResolver()],
        }),
        Components({
            resolvers: [ElementPlusResolver()],
        }),
    ],
    resolve: {
        // alias: {
        //     '@': resolve(__dirname, 'src'),
        // },
        alias: [
            {
                find: '@',
                replacement: resolve(__dirname, 'src'),
            },
            {
                find: '@a',
                replacement: resolve(__dirname, 'src/assets'),
            },
            {
                find: '@css',
                replacement: resolve(__dirname, 'src/assets/style'),
            },
            {
                find: '@r',
                replacement: resolve(__dirname, 'src/router'),
            },
            {
                find: '@p',
                replacement: resolve(__dirname, 'src/pages'),
            },
            {
                find: '@s',
                replacement: resolve(__dirname, 'src/stores'),
            },
            {
                find: '@u',
                replacement: resolve(__dirname, 'src/utils'),
            },
            {
                find: '@@',
                replacement: resolve(__dirname, 'src/components'),
            },
        ],
    },
});

路径别名使用

src/main.ts
typescript
import { createApp } from 'vue';
// import './assets/style/tailwind.scss';
// import './style.css';
// import './assets/style/test.scss';
// import App from './App.vue';
// import store from './stores';
// import router from './router';
import '@css/tailwind.scss';
import '@css/test.scss';
import '@/style.css';
import App from '@/App.vue';
import store from '@/stores';
import router from '@/router';

const app = createApp(App);
app.use(store);
app.use(router);
app.mount('#app');
src/App.vue
typescript
<script lang="ts" setup>
 // import HelloWorld from './components/HelloWorld.vue';
 import HelloWorld from '@@/HelloWorld.vue';
</script>

<template>
    <router-view></router-view>
    <HelloWorld></HelloWorld>
</template>

<style lang="scss">
</style>

代码规范

ESLint+Prettier+Stylelint+EditorConfig 约束和统一前端代码规范 ESLint 检测 JS/TS 代码、Prettier 格式化代码、Stylelint 检测 CSS/SCSS 代码和配置 EditorConfig 来全方位约束和统一前端代码规范。

ESLint 代码检测

ESLint 可组装的JavaScript和JSX检查工具,目标是保证代码的一致性和避免错误。

安装 ESLint 插件

VSCode 插件市场搜索 ESLint 插件并安装

image-20240108103340898

安装ESlint依赖
bash
pnpm install -D eslint # "eslint": "^8.56.0",
ESLint 配置

执行命令完成 ESLint 配置初始化

bash
npx eslint --init

image-20240108150658616

根目录自动生成的 .eslintrc.cjs 配置内容如下:

typescript
module.exports = {
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:vue/vue3-essential"
    ],
    "overrides": [
        {
            "env": {
                "node": true
            },
            "files": [
                ".eslintrc.{js,cjs}"
            ],
            "parserOptions": {
                "sourceType": "script"
            }
        }
    ],
    "parserOptions": {
        "ecmaVersion": "latest",
        "parser": "@typescript-eslint/parser",
        "sourceType": "module"
    },
    "plugins": [
        "@typescript-eslint",
        "vue"
    ],
    "rules": {
    }
}
ESLint 解析器配置

在默认配置基础上需要修改解析器为 vue-eslint-parser ,不然在检测执行中出现 error Parsing error: '>' expected 的解析错误,修改 .eslintrc.cjs 如下:

image-20240108170158580

Package.json 新增依赖
json
{
   "devDependencies": {
        "eslint": "^8.56.0", // EsLint的核心代码
        "eslint-plugin-vue": "^9.19.2" // 为Vue使用Eslint的插件
        "@typescript-eslint/parser": "^6.18.0", // ESLint的解析器,用于解析typescript,从而检查和规范Typescript代码
        "@typescript-eslint/eslint-plugin": "^6.18.0", // 这是一个ESLint插件,包含了各类定义好的检测Typescript代码的规项目下新建 .eslintrc.cjs,配置 eslint 校验规则
    }
}
配置 eslint 校验规则

.eslintrc.cjs

typescript
module.exports = {
    env: {
        browser: true,
        es2021: true,
    },
    extends: [
        'eslint:recommended',
        'plugin:@typescript-eslint/recommended',
        'plugin:vue/vue3-essential',
    ],
    overrides: [
        {
            env: {
                node: true,
            },
            files: ['.eslintrc.{js,cjs}'],
            parserOptions: {
                sourceType: 'script',
            },
        },
        {
            files: ['*.vue'],
            rules: {
                // 这里写覆盖vue文件的规则
                'no-unused-vars': [0],
            },
        },
    ],
    parser: 'vue-eslint-parser',
    parserOptions: {
        ecmaVersion: 'latest',
        parser: '@typescript-eslint/parser',
        sourceType: 'module',
    },
    plugins: ['@typescript-eslint', 'vue'],
    rules: {
        // JavaScript/ESLint 推荐的规则
        'no-console': 'warn', // 不允许使用 console.log 等
        'no-unused-vars': 'warn', // 不允许存在未使用的变量
        'no-undef': 'error', // 不允许使用未定义的变量
        'no-debugger': 'warn', // 不允许使用debugger
        // 禁止使用alert
        'no-alert': 'warn',
        // 禁用var,用let和const代替
        'no-var': 'warn',
        // 允许每个作用域有多个变量声明,但需要将连续的变量声明组合成一个声明
        'one-var': ['error', 'consecutive'],
        // 要求使用 === 和 !==
        eqeqeq: 'error',
        // 强制使用骆驼拼写法命名约定
        camelcase: 'warn',
        // 强制使用一致的缩进
        indent: ['error', 4],

        // 强制结尾分号
        semi: ['warn', 'always'],
        // 强制字符串单引号
        quotes: ['error', 'single'],
        // 当最后一个元素或属性与结束 ] 或 } 在不同的行时需要尾随逗号,当最后一个元素或属性与结束 ] 或 } 在同一行时不允许尾随逗号
        'comma-dangle': ['error', 'always-multiline'],

        // Vue/ESLint 推荐的规则
        // HTML 缩进为 2 个空格
        'vue/html-indent': ['error', 4],
        // 属性名使用连字符形式
        'vue/attribute-hyphenation': 'error',
        // 关闭自闭合标签要求,根据个人或团队喜好配置
        'vue/html-self-closing': 'warn',
        'vue/max-attributes-per-line': [
            'error',
            {
                // 单行最多 6 个属性
                singleline: 6,
                // 每行最多一个属性
                multiline: {
                    max: 1,
                },
            },
        ],
        // 不允许使用 v-html 指令
        'vue/no-v-html': 'error',
        // 不允许存在未使用的组件
        'vue/no-unused-components': 'error',
        // TypeScript/ESLint 推荐的规则
        // 不允许存在未使用的 TypeScript 变量
        '@typescript-eslint/no-unused-vars': 'error',
        // 不允许不显式指定导出函数的返回类型
        '@typescript-eslint/explicit-module-boundary-types': 'warn',
        // 不允许使用 any 类型
        '@typescript-eslint/no-explicit-any': 'warn',
        // 组件命名规范校验
        'vue/multi-word-component-names': 'off',
    },
};
ESLint 忽略配置(.eslintignore)

根目录新建 .eslintignore 文件,添加忽略文件, ESLint 校验会忽略这些文件,配置如下:

dist
node_modules
public
.husky
.vscode
.idea
*.sh
*.md

src/assets

.eslintrc.cjs
.prettierrc.cjs
.stylelintrc.cjs
ESLint 检测指令

package.json 添加 eslint 检测指令:

json
"scripts": {
        "lint:eslint": "eslint \"src/**/*.{vue,js,jsx,ts}\" --fix"
    },

执行命令进行ESLint检测:

bash
pnpm run lint:eslint

image-20240108194849243

ESLint 保存自动检测
vscode

打开 File → Preferences → Settings 搜索 Editor: Code Actions On Save 选择 Workspace标签设置工作区,点击 Edit in settings.json

image-20240109101502008

json
{
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true // 开启eslint检测
    }
}
webStorm

image-20240109101423325

Prettier 代码格式化

Prettier一个“有态度”的代码格式化工具

安装 Prettier 依赖
bash
pnpm install -D prettier eslint-config-prettier eslint-plugin-prettier
  • prettier:prettier插件的核心代码
  • eslint-config-prettier:解决ESLint中的样式规范和prettier中样式规范的冲突,以prettier的样式规范为准,使ESLint中的样式规范自动失效
  • Aslant-plugin-prettier:将prettier作为ESLint规范来使用
安装 Prettier 插件
VSCode

VSCode 插件市场搜索 Prettier - Code formatter 插件安装

image-20240117144344955

webStorm

image-20240117150040237

配置 Prettier 规则

项目根目录下新建 .prettierrc.cjs,配置 prettier 规则

javascript
/**
 * @FileName .prettierrc.cjs
 * @Author Mad Dragon <395548460@qq.com>
 * @Version V 0.0.1
 * @Date 2024/1/17 14:56
 * @Title Prettierrc
 * @Desc
 *  Prettier 官网 https://www.prettier.cn/docs/options.html
 **/
module.exports = {
    // (x)=>{},单个参数箭头函数是否显示小括号。(always:始终显示;avoid:省略括号。默认:always)
    arrowParens: 'avoid',
    // 开始标签的右尖括号是否跟随在最后一行属性末尾,默认false
    bracketSameLine: false,
    // 对象字面量的括号之间打印空格 (true - Example: { foo: bar } ; false - Example: {foo:bar})
    bracketSpacing: false,
    // 是否格式化一些文件中被嵌入的代码片段的风格(auto|off;默认auto)
    embeddedLanguageFormatting: 'auto',
    // 指定 HTML 文件的空格敏感度 (css|strict|ignore;默认css)
    htmlWhitespaceSensitivity: 'css',
    // 当文件已经被 Prettier 格式化之后,是否会在文件顶部插入一个特殊的 @format 标记,默认false
    insertPragma: false,
    // 在 JSX 中使用单引号替代双引号,默认false
    jsxSingleQuote: false,
    // 每行最多字符数量,超出换行(默认80)
    printWidth: 120,
    // 超出打印宽度 (always | never | preserve )
    proseWrap: 'preserve',
    // 对象属性是否使用引号(as-needed | consistent | preserve;默认as-needed:对象的属性需要加引号才添加;)
    quoteProps: 'as-needed',
    // 是否只格式化在文件顶部包含特定注释(@prettier| @format)的文件,默认false
    requirePragma: false,
    // 结尾添加分号
    semi: true,
    // 使用单引号 (true:单引号;false:双引号)
    singleQuote: true,
    // 缩进空格数,默认2个空格
    tabWidth: 4,
    // 元素末尾是否加逗号,默认es5: ES5中的 objects, arrays 等会添加逗号,TypeScript 中的 type 后不加逗号
    trailingComma: 'es5',
    // 指定缩进方式,空格或tab,默认false,即使用空格
    useTabs: false,
    // vue 文件中是否缩进 <style> 和 <script> 标签,默认 false
    vueIndentScriptAndStyle: false,
    // 在变为默认行为之前,请尝试使用漂亮的新三元格式。
    experimentalTernaries: false,
    // 在HTML、Vue和JSX中每行强制执行一个属性。
    singleAttributePerLine: true,
};
配置 Prettier 忽略配置(.prettierignore)
dist
node_modules
public
.husky
.vscode
.idea
*.sh
*.md

src/assets
Prettier 格式化指令

package.json 添加 prettier 格式化指令:

typescript
"scripts": {
    "lint:prettier": "prettier --write \"**/*.{js,ts,json,css,less,scss,vue,html,md}\""
}
Prettier 格式化&验证
bash
pnpm run lint:prettier

image-20240328213925496

Prettier 保存自动格式化
VSCode

VSCode 的 settings.json 配置:

json
{
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "esbenp.prettier-vscode",
}
WebStorm

image-20240328220353783

image-20240328220414673

Stylelint CSS 检测

Stylelint 一个强大的 CSS linter(检查器),可帮助您避免错误并强制执行约定。官方网站: https://stylelint.io

注意官网明确指出 Stylelint 作为 CSS 代码规范检测而不作为代码格式化工具使用(Prettier 是更好的选择),新版本(15.0.0)为此废弃相关的 rules

安装 Stylelint 依赖
bash
pnpm install -D stylelint stylelint-config-standard stylelint-config-recommended-scss stylelint-config-recommended-vue postcss postcss-html postcss-scss stylelint-config-recess-order stylelint-config-html
依赖说明备注
stylelintStylelint 核心库stylelint官网 stylelint中文官网
stylelint-config-standardStylelint 标准共享配置stylelint-config-standard 文档
stylelint-config-recommended-scss扩展 stylelint-config-recommended 共享配置并为 SCSS 配置其规则stylelint-config-recommended-scss 文档
stylelint-config-recommended-vue扩展 stylelint-config-recommended 共享配置并为 Vue 配置其规则stylelint-config-recommended-vue 文档
stylelint-config-recess-order提供优化样式顺序的配置CSS 书写顺序规范
stylelint-config-html共享 HTML (类似 HTML) 配置,捆绑 postcss-html 并对其进行配置stylelint-config-html 文档
postcss-html解析 HTML (类似 HTML) 的 PostCSS 语法postcss-html 文档
postcss-htmlPostCSS 的 SCSS 解析器postcss-scss 文档,支持 CSS 行类注释
安装 Stylelint 插件
VScode

VScode 插件搜索 Stylelint 并安装

image-20240402201920202

WebStorm

image-20240402202004890

Stylelint 配置

根目录下新建 .stylelintrc.cjs 文件

typescript
module.exports = {
    // 继承推荐规范配置
    extends: [
        'stylelint-config-standard',
        'stylelint-config-recommended-scss',
        'stylelint-config-recommended-vue/scss',
        'stylelint-config-html/vue',
        'stylelint-config-recess-order',
    ],
    // 指定不同文件对应的解析器
    overrides: [
        {
            files: ['**/*.{vue,html}'],
            customSyntax: 'postcss-html',
        },
        {
            files: ['**/*.{css,scss}'],
            customSyntax: 'postcss-scss',
        },
    ],
    // 定义自定义规则
    rules: {
        // 允许 global, export, v-deep等伪类
        'selector-pseudo-class-no-unknown': [
            true,
            {
                ignorePseudoClasses: ['global', 'export', 'v-deep', 'deep'],
            },
        ],
        'order/properties-alphabetical-order': true,
        'block-no-empty': true,
        'scss/at-rule-no-unknown': [
            true,
            {
                ignoreAtRules: ['tailwind'],
            },
        ],
        'property-no-unknown': [
            true,
            {
                ignoreProperties: ['/^md/'],
            },
        ],
    },
};

Stylelint 忽略配置(.stylelintignore)

text
dist
node_modules
public
.husky
.vscode
.idea
*.sh
*.md

src/assets
Style lint 检测指令

Package.json 添加Stylelint 检测指令

json
 "scripts": {
      "lint:stylelint": "stylelint  \"**/*.{css,scss,vue,html}\" --fix"
  }
Stylelint 检测&验证

执行一下命令

bash
pnpm run lint:stylelint

image-20240402203438257

Stylelnit 保存自动检测

Vscode的 settings.json配置

json
{
  "editor.codeActionsOnSave": {
    // 开启 Stylelint 保存自动检测
    "source.fixAll.stylelint": "always"
  },
  // Stylelint 校验文件
  "stylelint.validate": ["css", "scss", "vue", "html"]
}

EditorConfig 编辑器配置

EditorConfig 主要用于统一不同 IDE 编辑器的编码风格。官方网站: https://editorconfig.org/

配置 EditorConfig

根目录创建 .editorconfig 文件,添加配置如下:

# http://editorconfig.org
root = true

# 表示所有文件适用
[*]
charset = utf-8 # 设置文件字符集为 utf-8
end_of_line = lf # 控制换行类型(lf | cr | crlf)
indent_style = space # 缩进风格(tab | space)
indent_size = 4 # 缩进大小 4
insert_final_newline = true # 始终在文件末尾插入一个新行
trim_trailing_whitespace = true # 删除行尾空格 = 是

# 表示仅 md 文件适用以下规则
[*.md]
max_line_length = off # 关闭最大行长度限制
trim_trailing_whitespace = false # 关闭末尾空格修剪
安装 EditorConfig 插件

VSCode 搜索 EditorConfig for VS Code 插件并安装

image-20240404102744062

配置 Git 提交规范

使用 Husky + Lint-staged + Commitlint —— commitizen + cz-git 来配置 Git提交代码规范。

核心内容是配置 Husky 的 pre-commitcommit-msg 两个钩子:

  • pre-commit : Husky + Lint-staged 整合实现 Git 提交前代码规范检测/格式化(前提: ESLint + prettier + Stylelint 代码统一规范配置好)
  • commit-msg : Husky + Commit + Commitizen + cz-git 整合实现生成规范化且高度自定义的 Git commit message。

image-20240409210811614

使用 Husky

Husky是Git钩子工具,可以设置在git各个阶段(pre-commit、commit-msg等)触发。

官方网站:https://typicode.github.io/husky

安装 Husky 依赖
bash
npx husky-init && pnpm install

自动生成.husky目录和指令:

image-20240409211801957

使用 Lint-staged

lint-staged 是一个在 git add 到 暂存区的文件运行 linters(ESLint / Prettier / Stylelint)的工具,避免在git commit提交的时候在整个项目执行。

官方网站:https://github.com/okonet/lint-staged

安装 Lint-staged 依赖
bash
pnpm install -D lint-staged
配置 Lint-staged

package.json 中添加不同文件在git提交执行的lint检测配置

json
    "lint-staged": {
        "*.{js,ts}": [
            "eslint --fix",
            "prettier --write"
        ],
        "*.{cjs,json}": [
            "prettier --write"
        ],
        "*.{vue,html}": [
            "eslint --fix",
            "prettier --write",
            "stylelint --fix"
        ],
        "*.{scss,css}": [
            "stylelint --fix",
            "prettier --write"
        ],
        "*.md": [
            "prettier --write"
        ]
    }
Lint-staged 指令
json
"scripts": {
		"lint:lint-staged": "lint-staged",
}
修改提交前钩子命令

根目录 .husky 目录下 pre-commit 文件中 npm test 修改 npm run lint:lint-staged

sh
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run lint:lint-staged
Git 提交代码检测

1、git add 需要提交的文件

2、运行指令

bash
git commit -m "test husky lint-staged"

image-20240409213203952

使用 Commitlint

Commitlint检查您的提交消息是否符合 Conventional commit format。

安装 Commitlint 依赖
bash
pnpm install -D @commitlint/cli @commitlint/config-conventonal
配置 Commitlint

根目录创建 commitlint.config.cjs 配置文件

typescript
module.exports = {
    // 继承的规则
    extends: ['@commitlint/config-conventional'],
    // @see: https://commitlint.js.org/#/reference-rules
    rules: {
        'subject-case': [0], // subject大小写不做校验

        // 类型枚举,git提交type必须是以下类型
        'type-enum': [
            2,
            'always',
            [
                'feat', // 新增功能
                'fix', // 修复缺陷
                'docs', // 文档变更
                'style', // 代码格式(不影响功能,例如空格、分号等格式修正)
                'refactor', // 代码重构(不包括 bug 修复、功能新增)
                'perf', // 性能优化
                'test', // 添加疏漏测试或已有测试改动
                'build', // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)
                'ci', // 修改 CI 配置、脚本
                'revert', // 回滚 commit
                'chore', // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
            ],
        ],
    },
};
添加提交信息校验钩子

执行下面命令生成 commint-msg 钩子用于git提交信息校验

bash
npx husky add .husky/commit-msg "npx --no -- commitlint --edit \$1"

生成配置如下:

image-20240410091827430

Commitlint 验证

正确的提交格式:<type>(<scope>):<subject>,type 和 subject 默认必填

image-20240410093313856

Commitizen & cz-git

  • Commitizen: 基于Node.js 的 git commit 命令行工具,辅助生成标准化规范化的commmit message。
  • cz-git: 一款工程性更强,轻量级,高度自定义,标准输出格式的 commitizen适配器。
安装 Commitizen 、cz-git 依赖
bash
pnpm install -D commitizen cz-git
配置 cz-git

修改 package.json 指定使用的适配器

typescript
    "config": {
        "commitizen": {
            "path": "node_modules/cz-git"
        }
    }

cz-git 与 commitlint 进行联动给予加盐信息, 所以便携于 commitlint配置文件之中 commitlint.config.cjs

typescript
module.exports = {
    rules: {
       ...
    },
    prompt: {
        // 自定义命令行提问信息
        messages: {
            type: '选择你要提交的类型 :',
            scope: '选择一个提交范围(可选):',
            customScope: '请输入自定义的提交范围 :',
            subject: '填写简短精炼的变更描述 :\n',
            body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
            breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
            footerPrefixesSelect: '选择关联issue前缀(可选):',
            customFooterPrefix: '输入自定义issue前缀 :',
            footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
            generatingByAI: '正在通过 AI 生成你的提交简短描述...',
            generatedSelectByAI: '选择一个 AI 生成的简短描述:',
            confirmCommit: '是否提交或修改commit ?',
        },
        // prettier-ignore
        // 自定义选择类型提示
        types: [
            {value: 'feat', name: '特性:     ✨  新增功能', emoji: ':sparkles:'},
            {value: 'fix', name: '修复:     🐛  修复缺陷', emoji: ':bug:'},
            {value: 'docs', name: '文档:     📝  文档变更', emoji: ':memo:'},
            {value: 'style', name: '格式:     💄  代码格式(不影响功能,例如空格、分号等格式修正)', emoji: ':lipstick:'},
            {value: 'refactor', name: '重构:     ♻️  代码重构(不包括 bug 修复、功能新增)', emoji: ':recycle:'},
            {value: 'perf', name: '性能:     ⚡️  性能优化', emoji: ':zap:'},
            {value: 'test', name: '测试:     ✅  添加疏漏测试或已有测试改动', emoji: ':white_check_mark:'},
            {value: 'build', name: '构建:     📦️  构建流程、外部依赖变更(如升级 npm 包、修改 vite 配置等)', emoji: ':package:'},
            {value: 'ci', name: '集成:     🎡  修改 CI 配置、脚本', emoji: ':ferris_wheel:'},
            {value: 'revert', name: '回退:     ⏪️  回滚 commit', emoji: ':rewind:'},
            {value: 'chore', name: '其他:     🔨  对构建过程或辅助工具和库的更改(不影响源文件、测试用例)', emoji: ':hammer:'},
        ],
        // 是否开启 commit message 带有 Emoji 字符。
        useEmoji: true,
        // 设置 Emoji 字符 的 位于头部位置
        emojiAlign: 'center',
        useAI: false,
        aiNumber: 1,
        // 设置终端交互部件的主题色
        themeColorCode: '',
        // 选择一个提交范围配置
        scopes: ['框架搭建', '开发规范', '需求整理', '工具类封装', '项目重构'],
        allowCustomScopes: true,
        allowEmptyScopes: true,
        // 设置 选择范围 中 为空选项(empty) 和 自定义选项(custom) 的 位置
        customScopesAlign: 'top',
        // 自定义 选择范围 中 自定义选项(custom) 在命令行中显示的 名称
        customScopesAlias: '自定义',
        // 自定义 选择范围 中 为空选项(empty) 在命令行中显示的 名称
        emptyScopesAlias: '跳过',
        upperCaseSubject: false,
        markBreakingChangeMode: false,
        allowBreakingChanges: ['feat', 'fix'],
        breaklineNumber: 100,
        breaklineChar: '|',
        skipQuestions: [],
        issuePrefixes: [{value: 'closed', name: 'closed:   ISSUES has been processed'}],
        // 设置 选择 issue 前缀 中 跳过选项(skip) 和 自定义选项(custom) 的 位置
        customIssuePrefixAlign: 'top',
        // 自定义 选择 issue 前缀 中 跳过选项(skip) 在命令行中显示的 名称
        emptyIssuePrefixAlias: '跳过',
        // 自定义 选择 issue 前缀 中 自定义选项(custom) 在命令行中显示的 名称
        customIssuePrefixAlias: '自定义',
        allowCustomIssuePrefix: true,
        allowEmptyIssuePrefix: true,
        confirmColorize: true,
        maxHeaderLength: Infinity,
        maxSubjectLength: Infinity,
        minSubjectLength: 0,
        scopeOverrides: undefined,
        defaultBody: '',
        defaultIssues: '',
        defaultScope: '',
        defaultSubject: '',
    },
};
添加提交指令

package.json 添加 commit指令

typescript
 "scripts": {
     "commit": "git-cz"
 }
cz-git 验证

执行 commit 指令进行代码提交流程,执行前需将改动的文件通过 git add 添加到暂存区 , 根据提示一步步完善 commit msg 信息。commit 之后 git push 推送到远程仓库可以看到提交信息。

bash
pnpm run commit

image-20240410100613477

编辑器插件

VSCode

安装插件 Commit Message Editor

image-20240410133326881

填写信息,并save,然后提交

image-20240410133802745

查看提交记录

image-20240410134350620

WebStorm

安装插件 git commit template

image-20240410135552937

填写信息

image-20240410140002804

提交

image-20240410140033167

查看提交记录

image-20240410140130883

安装自动导入

Element Plus 官方文档中推荐 按需自动导入 的方式,而此需要使用额外的插件 unplugin-auto-importunplugin-vue-components 来导入要使用的组件。所以在整合 Element Plus 之前先了解下自动导入的概念和作用

概念

为了避免在多个页面重复引入API组件,由此而产生的自动导入插件来节省重复代码和提高开发效率。

插件概念自动导入对象
unplugin-auto-import按需自动导入APIre、reactive、watch、computed等API
unplugin-vue-components按需自动导入组件Element Plus 等三方库和指定目录下的自定义组件

看下自动导入插件未使用和使用的区别:

unplugin-auto-import

未使用自动导入

image-20240410215315206

使用自动导入

image-20240410215525479

unplugin-vue-components

未使用自动导入

image-20240410215809155

使用自动导入

image-20240410215825117

安装依赖

bash
pnpm install -D unplugin-auto-import unplugin-vue-components

vite.config.ts - 自动导入配置

新建 /src/types 目录用于存放自动导入函数和组件的TS类型声明文件

typescript
import {defineConfig} from 'vite';
import vue from '@vitejs/plugin-vue';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import {ElementPlusResolver} from 'unplugin-vue-components/resolvers';
import {resolve} from 'path';

// https://vitejs.dev/config/
export default defineConfig({
    plugins: [
        vue(),
        AutoImport({
            // 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
            // 自动导入 pinia 相关函数,如:createPinia, defineStore, mapState 等
            // 自动导入 vue-router 相关函数,如:useLink, useRoute, useRouter 等
            imports: ['vue', 'pinia', 'vue-router'],
            eslintrc: {
                // 是否自动生成 eslint 规则,建议生成之后设置 false
                enabled: true,
                // 指定自动导入函数 eslint 规则的文件
                filepath: './.eslintrc-auto-import.json',
            },
            // 指定自动导入函数TS类型声明文件路径
            dts: resolve(__dirname, 'src/types', 'auto-imports.d.ts'),
            // 自动导入 elementPlus 相关
            resolvers: [ElementPlusResolver()],
        }),
        Components({
            resolvers: [ElementPlusResolver()],
            dts: resolve(__dirname, 'src/types', 'components.d.ts'),
        }),
    ],
});

.eslintrc.cjs - 自动导入函数 eslint规则引入

typescript
"extends": [
    "./.eslintrc-auto-import.json"
],

tsconfig.json - 自动导入TS类型声明文件引入

typescript
{
  "include": ["src/**/*.d.ts"]
}

自动导入效果

运行项目 pnpm dev

image-20240411085807896

安装自动导入 element plus Icon

安装 Icon 依赖

bash
pnpm install -D unplugin-icons

vite.config.ts 配置

typescript
import {defineConfig} from 'vite';
import vue from '@vitejs/plugin-vue';
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';
import {ElementPlusResolver} from 'unplugin-vue-components/resolvers';
import {resolve} from 'path';

// eslint-disable-next-line no-undef
const dirname = __dirname;

// https://vitejs.dev/config/
export default defineConfig({
    plugins: [
        AutoImport({
            // 指定自动导入函数TS类型声明文件路径
            dts: resolve(dirname, 'src/types', 'auto-imports.d.ts'),
             // 是否在 vue 模版中自动导入
            vueTemplate: true,
            // 自动导入 elementPlus 相关
            resolvers: [
                // 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式)
                ElementPlusResolver(),
                // 自动导入图标组件
                IconsResolver({}),
            ],
        }),
        Components({
            resolvers: [
                // 自动导入 Element Plus 组件
                ElementPlusResolver(),
                IconsResolver({
                    // element-plus图标库,其他图标库 https://icon-sets.iconify.design/
                    enabledCollections: ['ep'],
                }),
            ],
            dts: resolve(dirname, 'src/types', 'components.d.ts'),
        }),
        Icons({
            // 自动安装图标库
            autoInstall: true,
        }),
    ],
});

示例代码

vue
<el-button type="success"><i-ep-SuccessFilled />Success</el-button>
<el-button type="info"><i-ep-InfoFilled />Info</el-button>
<el-button type="warning"><i-ep-WarningFilled />Warning</el-button>
<el-button type="danger"><i-ep-WarnTriangleFilled />Danger</el-button>

效果预览

image-20240411184200714

整合 SVG 图标

通过 vite-plugin-sbg-icons 插件整合 Iconfont第三方图标库实现本地图标

安装依赖

bash
pnpm install -D fast-glob vite-plugin-svg-icons

创建目录

创建 src/assets/icons目录,放入葱Iconfont赋值的svg 图标

image-20240411195403691

引入脚本

main.js 引入注册脚本

typescript
import 'virtual:svg-icons-register';

vite.config.ts 配置插件

typescript
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';

export default ({command, mode}: ConfigEnv): UserConfig => {
 return (
     {
         plugins: [
             createSvgIconsPlugin({
                 // 指定需要缓存的图标文件夹
               // eslint-disable-next-line no-undef
                 iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
                 // 指定symbolId格式
                 symbolId: 'icon-[dir]-[name]',
             })
         ]
     }
 )
}

SVG组件封装

创建 src/components/MdSvgIcon/index.vue

vue
<script lang="ts" setup>
const props = defineProps({
        prefix: {
            type: String,
            default: 'icon',
        },
        iconClass: {
            type: String,
            required: false,
        },
        color: {
            type: String,
        },
        size: {
            type: String,
            default: 'iem',
        },
    }),
    symbolId = computed(() => `#${props.prefix}-${props.iconClass}`);
</script>

<template>
    <svg
        :style="`width:${size};height:${size}`"
        aria-hidden="true"
        class="svg-icon"
    >
        <use
            :fill="color"
            :xlink:href="symbolId"
        />
    </svg>
</template>

<style scoped>
.svg-icon {
    display: inline-block;
    width: 1em;
    height: 1em;
    overflow: hidden;
    vertical-align: -0.15em; /* 因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果 */
    outline: none;
    fill: currentcolor; /* 定义元素的颜色,currentColor是一个变量,这个变量的值就表示当前元素的color值,如果当前元素未设置color值,则从父元素继承 */
}
</style>

组件使用

vue
<template>
	<el-button type="primary"><md-svg-icon icon-class="snowflake" />SVG 本地图标</el-button>
</template>

image-20240411200006831