Skip to content

这是一个基于Vite4.X + Vue3.X + TypeScript + Naive UI + Pinia + VueRouter + Unocss + Alova + Eslint + Prettier + husky + lint-staged + commitlint + commitizen + cz-customizable+ conventional-changelog构建的标准的vue项目模板。整个项目包依赖采用pnpm进行依赖管理,Node版本为16.17.0, 同时也集成了项目中常用的插件,包含组件自动导入API

License

AnyFork/vite-standard-template

Repository files navigation

vite-standard-template

一款简洁,符合通用标准的,基于Vite构建的Vue3项目模板

vite version vue version node compatibility

一 项目简介

对于多人协作开发来说,项目开发规范标准,非常重要,不仅能够统一标准,规范代码书写风格,还能便于后期维护和运营。

一直以来,每次采用vite搭建Vue3项目时,都苦于配置Eslint代码校验规范,网上的水贴层出不穷,你抄我的,我粘贴你的,没有几个是有用的,不仅容易误导他人,还浪费大量的时间排查问题。最近花了一些时间,查阅了很多资料,自己也通过反复验证,决定自己搭建一个简单的模板,集成好日常代码开发规范和提交规范,把常用 的Vite插件都配置好,在以后创建项目时直接使用,避免重复造轮子,同时也供他人参考。

这是一个基于Vite4.X + Vue3.X + TypeScript + Naive UI + Pinia + VueRouter + Unocss + Alova + Eslint + Prettier + husky + lint-staged + commitlint + commitizen + cz-customizable+ conventional-changelog构建的标准的vue项目模板。整个项目包依赖采用pnpm进行依赖管理,Node版本为16.17.0, 同时也集成了项目中常用的插件,包含组件自动导入API

二 项目创建

1 采用pnpm创建项目

pnpm create vite

2 依赖安装

pnpm install

3 项目运行

pnpm dev

三 设置依赖

项目创建完后,我们开始增加一些日常开发过程中经常使用的vite插件和项目工具包,方便我们能够快速开发项目。

1 Vite插件

1 unplugin-auto-import

官网:https://github.com/antfu/unplugin-auto-import

unplugin-auto-import是为 Vite、Webpack、Rollupesbuild 按需自动导入API。例如:ref,reactive等API无需额外导入,就可以全局使用。

1.1 依赖安装
pnpm install unplugin-auto-import -D
1.2 插件配置

vite.config.ts中进行插件配置,如下:

export default defineConfig(){
    plugins:[
      ...
       //自动导入Composition API,https://github.com/antfu/unplugin-auto-import
      AutoImport({
        dts: "src/types/auto-import.d.ts",
        imports: [
          "vue"
        ],
      }),
      ...
    ]
}

上面已经配置了vue框架自动导入API

2 unplugin-vue-components

官网:https://github.com/antfu/unplugin-vue-components

unplugin-vue-components是一款组件自动导入Vite插件,可以自定义需要自动导入的组件目录,无需使用时手动导入。

2.1 依赖安装
pnpm install unplugin-vue-components -D
2.2 插件配置

vite.config.ts中进行插件配置,如下:

export default defineConfig(){
    plugins:[
      ...
        //自动导入组件,https://github.com/antfu/unplugin-vue-components
        Components({
          dts: "src/types/components.d.ts",
          dirs: ['src/components'],
          resolvers: [],
        }),
      ...
    ]
}

3 unplugin-vue-setup-extend-plus

官网:https://github.com/chenxch/unplugin-vue-setup-extend-plus

Vue3组件自定义命名插件,可以在<script setup lang=ts name="Good"></script>标签中,通过设置name属性为组件命名

3.1 依赖安装
pnpm install unplugin-vue-setup-extend-plus -D
3.2 插件配置

vite.config.ts中进行插件配置,如下:

export default defineConfig(){
    plugins:[
      ...
      //官网:https://github.com/chenxch/unplugin-vue-setup-extend-plus
      vueSetupExtend({
        //禁止组件属性自动导出
        enableAutoExpose: false,
      }),
      ...
    ]
}

配置完毕后,我们可以为组件进行命名。例如:SvgIcon组件:

<script setup lang="ts" name="SvgIcon">
</script>

4 vite-plugin-html

官网地址:https://github.com/vbenjs/vite-plugin-html/blob/main/README.zh_CN.md

vite-plugin-html插件可以在html页面中使用ejs语法,动态注入数据。

4.1 依赖安装
pnpm install vite-plugin-html -D
4.2 插件配置

1 在vite.config.ts中进行插件配置,如下:

export default defineConfig(){
    plugins:[
      ...
      //在html中创建ejs标签,官网地址:https://github.com/vbenjs/vite-plugin-html/blob/main/README.zh_CN.md
      createHtmlPlugin({
        // 是否压缩 html
        minify: true,
        /**
         * 需要注入 index.html ejs 模版的数据
         */
        inject: {
          data: {
            title: env.VITE_SYSTEM_TITLE,
            description: env.VITE_SYSTEM_DESC
          }
        }
      }),
      ...
    ]
}

inject中的data就是要注入的变量参数,env为环境变量参数。可以通过一下代码获取到:

import { loadEnv } from 'vite'
const env = loadEnv(mode, process.cwd())

2 修改index.html文件,将vite-plugin-html插件注入的数据,通过ejs语法写入index.html,如下:

<!doctype html>
<html lang="zh-CN">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta name="description" content="<%=description%>" />
        <link rel="icon" type="image/svg+xml" href="/logo.svg" />
        <title><%= title %></title>
    </head>

    <body>
        <div id="app">
            <div id="loading"></div>
        </div>
        <script type="module" src="/src/main.ts"></script>
    </body>
</html>

5 vite-plugin-svg-icons

官网:https://github.com/vbenjs/vite-plugin-svg-icons/blob/main/README.zh_CN.md

vite-plugin-svg-icons是一款用于生成svg 雪碧图的插件,能够将本地指定文件目录下的Svg生成一张雪碧图,在项目运行时就生成所有图标,只需操作一次dom,内置缓存,仅当文件被修改时才会重新生成。通过Svg名称便可以加载对应的Svg图标。

5.1 依赖安装
pnpm install vite-plugin-svg-icons -D
5.2 插件配置

1 在vite.config.ts中进行插件配置,如下:

import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import path from 'path'
export default defineConfig(){
    plugins:[
      ...
      createSvgIconsPlugin({
       //// 指定需要缓存的svg图标文件夹
       iconDirs: [path.resolve(process.cwd(), 'src/assets/svg')],
       // 指定symbolId格式
       symbolId: 'icon-local-[dir]-[name]`,
       // 自定义插入位置,@default: body-last
       inject: 'body-last',
       //自定义domId,默认:__svg__icons__dom__
       customDomId: '__SVG_ICON_LOCAL__'
      })
      ...
    ]
}

2 在main.js中增加以下代码

import 'virtual:svg-icons-register'

3 将需要的svg图标放入与iconDirs设置的路径中,项目中为src/assets/svg

<svg aria-hidden="true" style="width: 14px; height: 14px">
	<use :href="`#icon-local-${menu.icon}`" />
</svg>

menu.icon是路径里面的svg图片名称。这个是简单用法,项目中已封装成了组件SvgIcon,请前往自行查看。

4 如果使用Typescript,你可以在tsconfig.json内添加:

// tsconfig.json
{
  "compilerOptions": {
    "types": ["vite-plugin-svg-icons/client"]
  }
}

6 @iconify/vue

官网:https://iconify.design/docs/icon-components/vue/

iconify是功能最丰富的图标框架。可以与任何图标库一起使用的统一图标框架。开箱即用的功能包括80多个图标集和超过70,000个图标。官方为了便于使用iconify网站上的图标,提供了@iconify/vue组件,供大家使用SVG framework,支持在线和离线2种方式使用。离线方式需要下载对应图标集合json数据,然后先从本地资源中加载,如果没有找到,通过API从线上下载资源,并进行浏览器缓存。

@iconify/vue是一个功能非常强大的组件,支持图标名称动态渲染和静态渲染,正好弥补vite-plugin-svg-icons功能缺陷。

6.1 依赖安装
pnpm install @iconify/vue -D
6.2 使用例子
import { Icon } from '@iconify/vue';
<Icon icon="mdi-light:home" />
6.3 自定义组件

虽然@iconify/vue也支持本地svg,但逐个配置非常麻烦,所以结合vite-plugin-svg-icons@iconify/vue,我们创建一个自定义组件,使其不仅支持本地静态动态Svg渲染,还支持显示静态动态Svg渲染。下面是自定义组件代码:

<template>
    <template v-if="localIcon">
        <svg aria-hidden="true" width="1em" height="1em" v-bind="bindAttrs">
            <use :xlink:href="symbolId" fill="currentColor" />
        </svg>
    </template>
    <template v-else>
        <Icon v-if="icon" :icon="icon" v-bind="bindAttrs" />
    </template>
</template>

<script setup lang="ts" name="SvgIcon">
import { Icon } from '@iconify/vue'
// eslint-disable-next-line vue/no-setup-props-destructure
const { icon, localIcon } = defineProps<{
    /** iconify线上图标名称 */
    icon?: string
    /** 本地svg的文件名称 */
    localIcon?: string
}>()
// 获取组件传递的属性
const attrs = useAttrs()
// 计算绑定属性
const bindAttrs = computed<{ class: string; style: string }>(() => ({
    class: (attrs.class as string) ?? 'w-24px h-24px',
    style: attrs.style as string
}))
// 计算本地svg动态的symbolId
const symbolId = computed(() => {
    const icon = localIcon ?? 'no-icon'
    return `#icon-local-${icon}`
})
</script>

此时,便可以灵活的渲染本地和线上iconify网站上的Svg图标,使用例子如下:

本地Svg:<SvgIcon localIcon="logo"></SvgIcon>
iconify线上Svg:<SvgIcon icon="healthicons:fhir-logo" class="w-24px h-24px"></SvgIcon>

7 rollup-plugin-visualizer

官网:https://github.com/btd/rollup-plugin-visualizer

rollup-plugin-visualizer是一个用于Rollup构建工具的插件,一款用于项目性能优化,打包体积分析,能够生成可视化的构建报告,帮助开发者更好地了解构建过程中的文件大小、依赖关系等信息的插件。

7.1 依赖安装
pnpm install rollup-plugin-visualizer -D
7.2 插件配置

1 在vite.config.ts中进行插件配置,如下:

// 引入rollup-plugin-visualizer模块
import { visualizer } from "rollup-plugin-visualizer";
export default defineConfig(){
    plugins:[
      ...
      visualizer({
        //注意这里要设置为true,打包时会自动打开分析页面。
        open:true,  
        //分析图生成的文件名
        filename: "stats.html", 
        //收集gzip大小并将其显示
        gzipSize: true, // 
        //收集 brotli 大小并将其显示
        brotliSize: true, 
      })
      ...
    ]
}

2 运行运行命令打包,生成分析图

输入pnpm build打包项目,等待片刻,生成分析视图,视图会自动跳出来,并保存在项目根目录下,文件名就是刚刚参数filename的名字(stats.html)

视图分析中方块越大,表示该文件占用的空间越大,对于网络带宽和访问速度的要求就越高。如果一个网站中包含大量的大文件,那么用户在访问该网站时需要下载更多的数据,这会导致网站加载速度变慢,用户体验变差。

8 vite-plugin-compression

官网:https://github.com/vbenjs/vite-plugin-compression

gzip压缩:当前端资源过大时,服务器请求资源会比较慢。前端可以将资源通过Gzip压缩使文件体积减少大概60%左右,压缩后的文件,通过后端简单处理,浏览器可以将其正常解析出来。如果浏览器的请求头中包含content-encoding: gzip,即证明浏览器支持该属性。

vite中使用vite-plugin-compression插件可以很便捷的对代码进行gzip压缩,减少代码体积,加快浏览器访问速度。压缩的代码放到服务器后,需要后端配置一些东西,浏览器才可以解析。比如可以配置nginx.

8.1 依赖安装
pnpm install vite-plugin-compression -D
8.2 插件配置

vite.config.ts中进行插件配置,如下:

// 引入vite-plugin-compression模块
import viteCompression from 'vite-plugin-compression';
export default defineConfig(){
    plugins:[
      ...
      viteCompression()
      ...
    ]
}

9 完整的vite.config.ts

下面展示整个项目vite.config.ts完整的配置,代码如下

//vite.config.ts
import { type ConfigEnv, defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
// 该包是用于配置vite运行的时候自动检测eslint规范,不符合规范,启动时不会报错,页面刷新时会报错,https://github.com/gxmari007/vite-plugin-eslint
import eslint from 'vite-plugin-eslint'
import { visualizer } from 'rollup-plugin-visualizer'
import ViteCompression from 'vite-plugin-compression'
import Components from 'unplugin-vue-components/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import AutoImport from 'unplugin-auto-import/vite'
import UnoCSS from 'unocss/vite'
import { createHtmlPlugin } from 'vite-plugin-html'
import path from 'path'

// https://vitejs.dev/config/
export default defineConfig(({ mode }: ConfigEnv) => {
    const env = loadEnv(mode, process.cwd())
    return {
        // 配置插件
        plugins: [
            vue(),
            eslint(),
            /** 打包分析插件,官网:https://github.com/btd/rollup-plugin-visualizer */
            visualizer({
                // 注意这里要设置为true,打包时会自动打开分析页面。
                open: true,
                // 分析图生成的文件名
                filename: 'stats.html',
                // 收集gzip大小并将其显示
                gzipSize: true, //
                // 收集 brotli 大小并将其显示
                brotliSize: true
            }),
            /** 打包压缩插件 官网:https://github.com/vbenjs/vite-plugin-compression */
            ViteCompression({ algorithm: 'gzip' }),
            // 自动导入Composition API,https://github.com/antfu/unplugin-auto-import
            AutoImport({
                // dts生成路径
                dts: 'src/types/auto-import.d.ts',
                // 自动本地导入文件目录路径
                dirs: ['src/store/modules'],
                // 设置第三方自动导入的包名
                imports: [
                    'vue',
                    'vue-router',
                    'pinia',
                    '@vueuse/core',
                    {
                        'naive-ui': ['useDialog', 'useMessage', 'useNotification', 'useLoadingBar'],
                        alova: ['useRequest', 'createAlova'],
                        '@/service/alova': ['alova']
                    }
                ]
            }),
            // 自动导入组件,https://github.com/antfu/unplugin-vue-components
            Components({
                dts: 'src/types/components.d.ts',
                dirs: ['src/components'],
                resolvers: [NaiveUiResolver()]
            }),
            // 本地svg动态加载插件 官网地址:https://github.com/vbenjs/vite-plugin-svg-icons
            createSvgIconsPlugin({
                iconDirs: [path.resolve(process.cwd(), 'src/assets/svg')],
                symbolId: 'icon-local-[dir]-[name]',
                inject: 'body-last',
                customDomId: '__SVG_ICON_LOCAL__'
            }),
            // 官网地址:https://unocss.dev/integrations/vite
            UnoCSS(),
            // 在html中创建ejs标签,官网地址:https://github.com/vbenjs/vite-plugin-html/blob/main/README.zh_CN.md
            createHtmlPlugin({
                // 是否压缩 html
                minify: true,
                /**
                 * 需要注入 index.html ejs 模版的数据
                 */
                inject: {
                    data: {
                        title: env.VITE_SYSTEM_TITLE,
                        description: env.VITE_SYSTEM_DESC,
                        keywords: env.VITE_SYSTEM_KEYWORDS
                    }
                }
            })
        ],
        resolve: {
            // 配置别名
            alias: [{ find: '@', replacement: path.resolve(__dirname, 'src') }]
        },
        // 本地运行配置,及跨域反向代理配置
        server: {
            // 服务器端口
            port: 9527,
            // 默认启用并允许任何源
            cors: true,
            // 在服务器启动时自动在浏览器中打开应用程序
            open: true,
            // 反向代理配置,注意rewrite写法,开始没看文档在这里踩了坑
            proxy: {
                // 本地开发环境通过代理实现跨域,生产环境使用nginx转发
                '/api': {
                    // 通过代理接口访问实际地址。这里是实际访问的地址。vue会通过代理服务器来代理请求
                    target: env.VITE_BASIC_API_URL,
                    changeOrigin: true,
                    // 允许websocket代理
                    ws: true,
                    // 将api替换为空
                    rewrite: (path) => path.replace(/^\/api/, '/api')
                }
            }
        }
    }
})

2 Naive-UI框架

官网地址:https://www.naiveui.com/zh-CN/os-theme/docs/installation

naive-ui全量使用TypeScript编写, 无样式文件,组件按需加载,是一个使用起来非常清爽Vue 3组件库, 组件比较完整,主题可调,完全兼容现在主流的浏览器。

1 安装依赖

pnpm install naive-ui -D

2 按需引入

可以使用unplugin-auto-import 插件来自动导入API,使用unplugin-vue-components插件来按需自动加载组件,插件会自动解析模板中的使用到的组件,并导入组件。

1 在vite.config.ts中进行配置,代码如下:

import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    AutoImport({
      imports: [
        'vue',
        {
          'naive-ui': [
            'useDialog',
            'useMessage',
            'useNotification',
            'useLoadingBar'
          ]
        }
      ]
    }),
    Components({
      resolvers: [NaiveUiResolver()]
    })
  ]
})

2 如果你在使用Volar,那么可以在tsconfig.json中配置compilerOptions.types来指定全局组件类型

// tsconfig.json
{
  "compilerOptions": {
    // ...
    "types": ["naive-ui/volar"]
  }
}

至此naive-ui按需导入成功,此时便可以使用相关组件进行开发,详情参考:naive-ui官网

3 Unocss引擎

官方网站:https://unocss.dev/

UnoCSS - 一个具有高性能且极具灵活性的即时原子化CSS引擎,而非一款框架,因为它并未提供核心工具类,所有功能可以通过预设和内联配置提供,目前内置的presetUno预设涵盖了TailwindcssWindicss的大部分功能,熟悉上面2个原子css框架写法的人,上手很容易,UnoCSS的主要目标是直观性和可定制性。

3.1 依赖安装

pnpm install unocss  -D

3.2 引擎配置

vite.config.ts增加如下配置,相关配置参考:unocss

// vite.config.ts
import UnoCSS from 'unocss/vite'
import { defineConfig } from 'vite'
export default defineConfig({
  plugins: [
    ...
    UnoCSS(),
    ...
  ],
})

3.2 创建uno.config.ts

在项目根目录下面创建uno.config.ts,增加如下配置:

//uno.config.ts
import { defineConfig, presetUno, presetAttributify } from 'unocss'
export default defineConfig({
    //预设配置参考:https://unocss.dev/presets
    presets: [
        //设置默认预设,当自定义其他预设后,默认预设需要额外添加
        presetUno({ dark: "class" }),
        //设置归因预设(Attributify preset),可以使用bg=red等语法
        presetAttributify()
    ],
    //设置shortcuts,只能使用预设的和自定义的规则
    shortcuts: {
        'wh-full': 'w-full h-full',
        'flex-row-center': 'flex justify-center items-center',
        'flex-row-between': 'flex justify-between items-center',
        'flex-row-evenly': 'flex justify-evenly items-center',
        'flex-row-warp': 'flex flex-wrap',
        'flex-row-end': 'flex justify-end items-center',
        'flex-col-center': 'flex flex-col justify-center items-center',
        'flex-x-center': 'flex justify-center',
        'flex-y-center': 'flex items-center',
        'i-flex-center': 'inline-flex justify-center items-center',
        'i-flex-x-center': 'inline-flex justify-center',
        'i-flex-y-center': 'inline-flex items-center'
    },
    //自定义规则
    rules: [],
    //主题配置
    theme: {
        //继承boxShadow
        boxShadow: {
            box: '0 1px 8px 0 rgba(255, 0, 0, 0.1)',
            item: "0 1px 8px 0 rgba(0, 0, 0, 0.1)"
        },
        colors: {
            primary: 'rgb(var(--primary-color))',
            primary_hover: 'var(--primary-color-hover)',
            primary_pressed: 'var(--primary-color-pressed)',
            primary_active: 'var(--primary-color-active)',
            info: 'var(--info-color)',
            info_hover: 'var(--info-color-hover)',
            info_pressed: 'var(--info-color-pressed)',
            info_active: 'var(--info-color-active)',
            success: 'var(--success-color)',
            success_hover: 'var(--success-color-hover)',
            success_pressed: 'var(--success-color-pressed)',
            success_active: 'var(--success-color-active)',
            warning: 'var(--warning-color)',
            warning_hover: 'var(--warning-color-hover)',
            warning_pressed: 'var(--warning-color-pressed)',
            warning_active: 'var(--warning-color-active)',
            error: 'var(--error-color)',
            error_hover: 'var(--error-color-hover)',
            error_pressed: 'var(--error-color-pressed)',
            error_active: 'var(--error-color-active)'
        }
    }
})

上面是我项目中经常使用到的配置。

3.3 配置virtual:uno.css

main.ts中增加如下代码:

// main.ts
import 'virtual:uno.css'

3.4 VScode插件安装

VScode插件市场有Unocss插件,安装以后鼠标放上去可以查看对应classcss属性和值信息,点我安装插件

4 Pinia状态管理

官网:https://pinia.vuejs.org/

PiniaVue的存储库,它允许您跨组件/页面共享状态,与vuex功能类似,不仅兼容Option API写法也兼容Composition API写法,同时也只是插件扩展。

4.1 依赖安装

pnpm install pinia

4.2 配置Pinia实例

1、在vite.config.ts文件的AutoImport自动导入插件中,增加piniaAPI自动导入。

export default defineConfig(({ mode }: ConfigEnv) => {
  return {
    plugins: [
      ...
      //自动导入Composition API,https://github.com/antfu/unplugin-auto-import
      AutoImport({
        dts: "src/types/auto-import.d.ts",
        imports: [
          ...
          + "pinia",
          ...
        ],
      })
      ...
    ]
  }
})

配置了piniaAPI自动导入后,defineStorecreatePinia无需导入,便可以直接使用。

2 在项目src目录下创建store目录,创建index.ts文件,配置pinia实例。

//src/store/index.ts
import type { App } from 'vue';
/**
 * 安装vue状态管理插件pinia
 * @param app 
 */
export function setupStore(app: App) {
    const pinia = createPinia();
    //挂载pinia实例到app
    app.use(pinia);
}

3 在main.ts中注册pinia实例插件。

//main.ts
import { createApp } from 'vue'
import './style.css'
import 'virtual:uno.css'
+ import { setupStore } from './store';
import App from './App.vue'
const setupApp = async () => {
    //创建vue实例
    const app = createApp(App)
    //创建pinia
    + setupStore(app);
    //挂载app
    app.mount('#app');
}
setupApp()

到此为止,pinia实例创建好了,并挂载在了vue实例上。

4 创建src/store/modules目录,将状态管理文件按照模块放在src/store/modules目录中。建议使用Composition API写法进行开发。下面是个简单例子:

//src/store/modules/demo.ts
export const useCounterStore = defineStore('counter', () => {
    const count = ref(0)
    const increment = (): void => {
        count.value++
    }
    return { count, increment }
})

请注意,store是一个用reactive包裹的对象,这意味着不需要在getter之后写.value,但是,就像setup 中的props 一样,我们不能对其进行解构:

export default defineComponent({
  setup() {
    const store = useStore()
    // ❌ 这不起作用,因为它会破坏响应式
    // 这和从 props 解构是一样的
    const { name, doubleCount } = store

    name // "eduardo"
    doubleCount // 2

    return {
      // 一直会是 "eduardo"
      name,
      // 一直会是 2
      doubleCount,
      // 这将是响应式的
      doubleValue: computed(() => store.doubleCount),
      }
  },
})

为了从Store中提取属性同时保持其响应式,您需要使用storeToRefs()。 它将为任何响应式属性创建 refs。 当您仅使用store中的状态但不调用任何操作时,这很有用:

import { storeToRefs } from 'pinia'
export default defineComponent({
  setup() {
    const store = useStore()
    // `name` 和 `doubleCount` 是响应式引用
    // 这也会为插件添加的属性创建引用
    // 但跳过任何 action 或 非响应式(不是 ref/reactive)的属性
    const { name, doubleCount } = storeToRefs(store)

    return {
      name,
      doubleCount
    }
  },
})

5 将模块目录modules下面的文导出到src/store/modules/index.ts文件中,这样我们以后使用的时候,可以直接从@/store/modules直接导入

//src/store/modules/index.ts
export * from './demo'
//src/store/index.ts
export * from './modules'

5 在需要调用的文件中,通过命令导入import { useCounterStore } from '@/store/modules',使用方法如下:

import { useCounterStore } from '@/store/modules'
const counter = useCounterStore()
const { count } = storeToRefs(counter)

注意,不能直接对useCounterStore()进行结构,会影响响应性,需要用storeToRefs进行包装,然后就可以在模板中使用countincrement了。

6 AutoImport设置自动导入

虽然经过上面的配置,我们可以通过import { useCounterStore } from '@/store/modules'在需要的文件中进行导入使用,但每次都要写就显的麻烦,可以通过功能强大的AutoImport插件帮我们自动导入,以后就可以直接使用useCounterStore,就不用导入了。

vite.config.ts文件的AutoImport自动导入插件中,增加src/store/modules目录API的自动导入。

export default defineConfig(({ mode }: ConfigEnv) => {
  return {
    plugins: [
      ...
      //自动导入Composition API,https://github.com/antfu/unplugin-auto-import
      AutoImport({
        dts: "src/types/auto-import.d.ts",
        // 自动本地导入文件目录路径
        + dirs: ['src/store/modules'],
        imports: [
          ...
          "pinia",
          ...
        ],
      })
      ...
    ]
  }
})

到此,便可以愉快的玩耍了:smile:

4.3 状态持久化

pinia支持插件扩展,增强自身功能。类似vuex一样,pinia也有状态持久化插件pinia-plugin-persistedstate,帮助pinia完成状态持久功能,支持localStoragesessionStorage2种持久化方式。

官网: https://prazdevs.github.io/pinia-plugin-persistedstate/guide/

4.3.1 依赖安装
pnpm install pinia-plugin-persistedstate
4.3.2 插件配置

1 在src/store/目录下面创建文件目录plugins, 将插件pinia-plugin-persistedstate配置代码放在src/store/plugin/modules/persistedstate.ts中。

// src/store/plugin/modules/persistedstate.ts
// 参考地址:https://prazdevs.github.io/pinia-plugin-persistedstate/guide/
import { createPersistedState } from 'pinia-plugin-persistedstate'
import { encrypto, decrypto } from '@/utils'
/**
 * pinia 全局持久化配置,会覆盖默认配置,但也会被单个store的persist配置覆盖
 */
export const createPersistedStatePlugins = createPersistedState({
    storage: sessionStorage,
    beforeRestore: (context) => {
        console.log(context)
        return context
    },
    afterRestore: (context) => {
        console.log(context)
        return context
    },
    // 设置序列化,生产加密,开发采用默认不加密
    serializer: {
        serialize: import.meta.env.PROD ? encrypto : JSON.stringify,
        deserialize: import.meta.env.PROD ? decrypto : JSON.parse
    }
})

serializer默认的序列化为JSON.stringify,反序列化为JSON.parse,开发期间为了便于观察数据,采用默认的序列化方式,生成环境采用encryptodecrypto进行加密和解密,增加数据的安全性。更多具体配置,请查看官方文档

2 将配置好的插件安装在pinia实例上面,配置如下:

import type { App } from 'vue'
+ import { createPersistedStatePlugins } from './plugins'
/**
 * 安装vue状态管理插件pinia
 * @param app
 */
export function setupStore(app: App): void {
    // 创建pinia实例
    const pinia = createPinia()
    // 挂载pinia数据持久化插件
    + pinia.use(createPersistedStatePlugins)
    // 挂载pinia实例到app
    app.use(pinia)
}

3 开启持久化设置,在store状态文件增加如下配置,开启持久化配置,默认不开启。以文件src/store/modules/demo.ts举例:

export const useCounterStore = defineStore(
    'counter',
    () => {
        const count = ref(0)
        const increment = (): void => {
            count.value++
        }
        return { count, increment }
    },
    {
       + persist: true
    }
)

到此,pinia状态持久化配置完毕了,持久化状态便会生效。

5 Vue Router路由

Vue Routervue项目提供路由导航功能。

官网:https://router.vuejs.org/zh/

5.1 依赖安装

vue3请安装vue-router@4版本路由。

pnpm install  vue-router@4

5.2 路由配置

1 项目src目录下新建router目录,并进行基础的路由配置,具体路由模块文件根据实际开发自行增加配置。

// src/router/index.ts
import type { App } from 'vue'
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'

// 获取路由模式(history和hash)和项目baseUrl
const { VITE_HASH_ROUTE = 'false', VITE_BASE_URL } = import.meta.env

/**
 * 定义返回模块
 */
export const router = createRouter({
    history: VITE_HASH_ROUTE === 'true' ? createWebHashHistory(VITE_BASE_URL) : createWebHistory(VITE_BASE_URL),
    routes: []
})

/**
 * 路由安装插件,暴露方法在main.ts中进行安装
 * @param app
 */
export async function setupRouter(app: App): Promise<void> {
    app.use(router)
    await router.isReady()
}

2 在main.ts中进行路由安装

// main.ts
import { createApp } from 'vue'
import './style.css'
import 'virtual:uno.css'
+ import { setupRouter } from './router'
import { setupStore } from './store'
import App from './App.vue'
const setupApp = async (): Promise<void> => {
    // 创建vue实例
    const app = createApp(App)
    // 创建pinia
    setupStore(app)
    // 创建vueRouter
    + await setupRouter(app)
    // 挂载app
    app.mount('#app')
}
await setupApp()

3 在vite.config.ts的API自动导入插件AutoImport,增加以下配置:

 // vite.config.ts
 ...
 AutoImport({
    // dts生成路径
    dts: 'src/types/auto-import.d.ts',
    // 自动本地导入文件目录路径
    dirs: ['src/store/modules'],
    // 设置第三方自动导入的包名
    imports: [
        'vue',
        'pinia',
        +'vue-router',
        {
            'naive-ui': ['useDialog', 'useMessage', 'useNotification', 'useLoadingBar']
        }
    ]
}),
...

到此,路由安装配置完毕。

6 Alova数据请求

官网:https://alova.js.org/zh-CN/get-started/overview

alova是一个量级的请求策略库,它针对不同请求场景分别提供了具有针对性的请求策略,来提升应用可用性、流畅性,降低服务端压力,让应用如智者一般具备卓越的策略思维。alova 核心模块提供了各类适配器接口、中间件机制来保证高扩展能力,从而实现更多的请求场景。

6.1 依赖安装

pnpm install alova

6.2 依赖配置

在项目根目录创建service/alova/index.ts文件,代码配置如下:

import GlobalFetch from 'alova/GlobalFetch'
import VueHook from 'alova/vue'

// 1. 创建alova实例
export const alova = createAlova({
    /** base地址 */
    baseURL: '',
    /** 请求超时时间 */
    timeout: 10000,
    // VueHook用于创建ref状态,包括请求状态loading、响应数据data、请求错误对象error等
    statesHook: VueHook,
    // 请求适配器,推荐使用fetch请求适配器
    requestAdapter: GlobalFetch(),
    /** 全局的请求前置钩子 */
    beforeRequest: (config) => {
        console.log(config)
    },
    // 全局的响应拦截器
    responded: async (response) => {
        console.log(response)
        return await response.json()
    }
})

6.3 自动导入配置

vite.config.tsAutoImport增加如下配置:

//vite.config.ts
...
AutoImport({
    // dts生成路径
    dts: 'src/types/auto-import.d.ts',
    // 自动本地导入文件目录路径
    dirs: ['src/store/modules'],
    // 设置第三方自动导入的包名
    imports: [
        'vue',
        'vue-router',
        'pinia',
        '@vueuse/core',
        {
            'naive-ui': ['useDialog', 'useMessage', 'useNotification', 'useLoadingBar'],
            + alova: ['useRequest', 'createAlova'],
            +'@/service/alova': ['alova']
        }
    ]
}),
...

6.4 使用例子

const { loading, data, send: sendRequest } = useRequest(alova.Get('/api/weather/city/101030100'), { immediate: false })
const handlerEvent = async (): Promise<void> => {
    await sendRequest()
}

7 工具包依赖

下面介绍项目中集成的几个工具包。

7.1 @vueuse/core

官网:https://vueuse.org/

@vueuse/core是一个强大的工具包,包含大量的Composition API工具,支持vue2vue3,你想不到的工具都在这里,非常实用。

7.1.1 依赖安装
pnpm install @vueuse/core
7.1.2 配置自动导入

vite.config.ts的API自动导入插件AutoImport,增加以下配置:

 // vite.config.ts
 ...
 AutoImport({
    // dts生成路径
    dts: 'src/types/auto-import.d.ts',
    // 自动本地导入文件目录路径
    dirs: ['src/store/modules'],
    // 设置第三方自动导入的包名
    imports: [
        'vue',
        'vue-router',
        'pinia',
        +'@vueuse/core',
        {
            'naive-ui': ['useDialog', 'useMessage', 'useNotification', 'useLoadingBar']
        }
    ]
}),
...

7.2 crypto-js

官网:https://github.com/brix/crypto-js

crypto-js是一个比较古老的加解密工具,代码仓库都是十年前的,但功能还是很强大。

7.2.1 依赖安装
# 安装crypto-js 依赖
pnpm install crypto-js
# 安装crypto-js类型声明文件
pnpm install @types/crypto-js -D
7.2.2 简单示例

简单的使用例子参考:src/utils/modules/crypto/index.ts,更加详细使用方法参考:官网

7.3 lodash-es

官网:https://www.lodashjs.com/

Lodash是一个一致性、模块化、高性能的JavaScript实用工具库,算是从Underscore分离出来的超集. lodash 为了良好的浏览器兼容性,它使用了旧版es5 的模块语法;而lodash-es则使用了es6 的模块语法,这让vite之类的打包工具可以对其进行tree shake (摇树优化)以删除未使用的代码来优化打包体积。所以在使用lodash库时,推荐通过lodash-es来进行导入操作。lodash-es提供了很多实用的工具,比如:节流,防抖,深拷贝等。

7.3.1 依赖安装
# 安装lodash-es依赖
pnpm install lodash-es
# 安装lodash-es类型声明文件
pnpm install @types/lodash-es -D
7.3.2 简单示例

1 导入方式

/*引入全部*/
import _ from 'lodash-es';
/**按需引入*/
import { defaultsDeep } from 'lodash-es'; 

2 浅拷贝clone

import { clone } from 'lodash-es'; 
const objects = [{ 'a': 1 }, { 'b': 2 }]; 
const shallow = clone(objects);
 // true
console.log(shallow[0] === objects[0]); 

3 深拷贝 cloneDeep cloneDeep(value) 类似clone 但是它会递归拷贝 value。返回拷贝后的值。

import { cloneDeep } from 'lodash-es';
const objects = [{ 'a': 1 }, { 'b': 2 }];
const deep = cloneDeep(objects);
//false
console.log(deep[0] === objects[0]); 

4 防抖 debounce debounce(func, [wait=0], [options=]) 创建一个 debounced(防抖动)函数,该函数会从上一次被调用后,延迟 wait 毫秒后调用 func 方法。 返回新的 debounced(防抖动)函数。

  • func (Function): 要防抖动的函数。
  • [wait=0] (number): 需要延迟的毫秒数。
  • [options=] (Object): 选项对象。
  • [options.leading=false] (boolean): 指定在延迟开始前调用。
  • [options.maxWait] (number): 设置 func 允许被延迟的最大值。
  • [options.trailing=true] (boolean): 指定在延迟结束后调用。
import { debounce } from 'lodash-es';
// 避免窗口在变动时出现昂贵的计算开销。
jQuery(window).on('resize',debounce(calculateLayout, 150));
 
// 当点击时 `sendMail` 随后就被调用。
jQuery(element).on('click',debounce(sendMail, 300, {
  'leading': true,
  'trailing': false
}));
 
// 确保 `batchLog` 调用1次之后,1秒内会被触发。
var debounced = debounce(batchLog, 250, { 'maxWait': 1000 });
var source = new EventSource('/stream');
jQuery(source).on('message', debounced);
 
// 取消一个 trailing 的防抖动调用
jQuery(window).on('popstate', debounced.cancel);

5 节流 throttle throttle(func, [wait=0], [options=]) 创建一个节流函数,在 wait 秒内最多执行 func 一次的函数。 返回节流的函数。

  • func (Function): 要节流的函数。
  • [wait=0] (number): 需要节流的毫秒。
  • [options=] (Object): 选项对象。
  • [options.leading=true] (boolean): 指定调用在节流开始前。
  • [options.trailing=true] (boolean): 指定调用在节流结束后。
// 避免在滚动时过分的更新定位
jQuery(window).on('scroll', throttle(updatePosition, 100));
 
// 点击后就调用 `renewToken`,但5分钟内超过1次。
var throttled = throttle(renewToken, 300000, { 'trailing': false });
jQuery(element).on('click', throttled);
 
// 取消一个 trailing 的节流调用。
jQuery(window).on('popstate', throttled.cancel);

四 代码规范

下面开始集成项目多人协作开发过程中,代码编写规范,格式化规范以及git提交规范,统一团队开发标准,规范代码风格。

1 Eslint

Eslint 中文官网地址:https://zh-hans.eslint.org/docs/latest/use/getting-started
Eslint Github地址:https://github.com/eslint/eslint

ESLint 是一个语法规则和代码风格的检查工具,可以用来保证写出语法正确、风格统一的代码。不管是多人合作还是个人项目,代码规范是很重要的。这样做不仅可以很大程度地避免基本语法错误,也保证了代码的可读性。这所谓工欲善其事,必先利其器。

1.1 VScode启用Eslint

1 在vscode中启用Eslint插件是必须的,它可以在编写代码时自动检测和手动修复。如果没有安装插件,点我安装

2 插件的扩展设置。选择VSCode左下角的“设置”, 打开VSCode 配置文件,添加如下配置:

// 是否开启eslint
"eslint.enable": true,
// code代码保存时,自动eslint修复
"editor.codeActionsOnSave": {
    "source.fixAll.eslint": true,
    "eslint.autoFixOnSave" : true
}

添加后,这样每次保存的时候就可以根据根目录下.eslintrc.cjs你配置的ESLint规则来检查和做一些简单的fix,如果未生效,重启VScode

同样也可以不在VScode编辑器全局进行配置,只在项目中进行配置,参考项目目录.vscodesetting.json的配置。

1.2 Eslint依赖安装

1 Eslint安装方式有2种,一种是直接安装依赖包,另一种就是通过命令进行npm init @eslint/config进行Eslint初始化,建议采用第2种,不容易漏掉相关依赖。执行结果如下:

2 安装完成后,项目目录下会自动生成.eslintrc.cjs文件,注意:文件后缀为cjsjs,文件初始化内容如下:

module.exports = {
    env: {
        browser: true,
        es2021: true,
        node: true
    },
    extends: ['standard-with-typescript', 'plugin:vue/vue3-essential'],
    overrides: [
        {
            env: {
                node: true
            },
            files: ['.eslintrc.{js,cjs}'],
            parserOptions: {
                sourceType: 'script'
            }
        }
    ],
    parserOptions: {
        ecmaVersion: 'latest',
        sourceType: 'module'
    },
    plugins: ['vue'],
    rules: {}
}

3 .eslintrc.cjs文件配置 ESLint Plugin Vue 则是专门为Vue.js项目中的代码提供支持的插件。这个插件可以帮助我们检查Vue.js组件中的代码规范、避免常见的错误以及优化代码性能,参照eslint-plugin-vue官网配置文档,需要对.eslintrc.cjs增加如下配置:

module.exports = {
  ...
  overrides: [
  ],
  + parser: 'vue-eslint-parser',
  parserOptions: {
    + parser: '@typescript-eslint/parser',
    ...
  },
  ...
}

上面增加的解析器用于解析vuetypeScript相关的Eslint校验规则。

至此,项目中的Eslint已经起作用了,如果无效,请查看vscode上是否已安装Eslint插件,如果没有,点击安装

1.3 script检测脚本安装

我们可以通过命令进行Eslint规范检测和修复。执行以下命令在package.json中生成检测命令。

//eslint检测命令
pnpm pkg set scripts.lint="eslint . --ext src/*.{js,ts,vue}"
//eslint修复命令
pnpm pkg set scripts.lint:fix="eslint . --ext src/*.{js,ts,vue} --fix"

完成上面的步骤,我们可以通过pnpm lint检测规则,pnpm lint:fix修复简单的校验错误,但都是手动触发,不太方便,下面介绍一款插件,能够集成Vite中使用,在项目运行核打包时进行Eslint规则校验。

1.4 Vite插件配置

官网:https://github.com/gxmari007/vite-plugin-eslint

vite-plugin-eslint用于配置vite在运行和打包的时候,自动检测eslint规范,如果不符合规范,在项目启动时不会报错,浏览器打开页面或者页面刷新时会报eslint检测错误。

刚开始使用的时候,明明运行没有报错,一度怀疑插件有问题,直到打开浏览器进行访问,页面和控制台才出现Eslint规则校验错误,走了好的弯路。

1 安装插件依赖

pnpm install vite-plugin-eslint -D

2 在vite.config.ts配置插件

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// 该包是用于配置vite运行的时候自动检测eslint规范,不符合规范,启动时不会报错,页面刷新时会报错,https://github.com/gxmari007/vite-plugin-eslint
import eslint from 'vite-plugin-eslint'

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

1.5 常见问题

经过上面的配置后,当我们通过pnpm dev运行项目时,会出现以下几个问题。

错误①:@typescript-eslint/dot-notation

Error: Error while loading rule '@typescript-eslint/dot-notation': You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.

方案:

1 在.eslintrc.cjs增加以下配置

module.exports = {
  ...
  parserOptions: {
    + project: ["./tsconfig.json"],
    ...
  },
  ...
}

2 在.tsconfig.json文件的include选项中增加.eslintrc.cjs配置

module.exports = {
  ...
   + "include": [".eslintrc.cjs"],
  ...
}

错误2:<tsconfigRootDir>/vite.config.ts

error  Parsing error: ESLint was configured to run on `<tsconfigRootDir>/vite.config.ts` using `parserOptions.project`: <tsconfigRootDir>/tsconfig.json
However, that TSConfig does not include this file. Either:

方案:

1 在.tsconfig.json文件的include选项中增加vite.confit.ts配置

module.exports = {
  ...
   + "include": ["vite.confit.ts"],
  ...
}

错误3:@typescript-eslint/triple-slash-reference

C:\Users\Administrator\Desktop\vite-standard-template\src\vite-env.d.ts
  1:1  error  Do not use a triple slash reference for vite/client, use `import` style instead  @typescript-eslint/triple-slash-reference

方案:

1 直接在vite-env.d.ts中忽略///引用规则校验

/* eslint-disable @typescript-eslint/triple-slash-reference */
/// <reference types="vite/client" />

2 或者修改.eslintrc.cjs校验规则

module.exports = {
   ...
    rules: {
        // 关闭下面/// <reference path="..." />, /// <reference types="..." />,  /// <reference lib="..." /> 校验
        '@typescript-eslint/triple-slash-reference': 'off'
    }
    ...
}

错误4:parserOptions.extraFileExtensions

 error  Parsing error: ESLint was configured to run on `<tsconfigRootDir>/src\App.vue` using `parserOptions.project`: <tsconfigRootDir>/tsconfig.json
The extension for the file (`.vue`) is non-standard. You should add `parserOptions.extraFileExtensions` to your config

方案:

1 在.eslintrc.cjs增加以下配置

module.exports = {
  ...
  parserOptions: {
    + extraFileExtensions: ['.vue'],
    ...
  },
  ...
}

到此为止,所有错误处理完毕。如果编辑器依然爆红,重启一下vscode即可。

1.6 配置eslintignore

.eslintignore文件是Eslint校验规则忽略文件,与.gitignore功能类似。项目中创建.eslintignore,填写需要忽略校验的文件

*.sh
node_modules
*.md
*.woff
*.ttf
.vscode
dist
public
.husky

2 Perttier

官网:https://www.prettier.cn/

Perttier是一个功能强大的代码格式化工具,支持多种格式的文件类型,还能保存就格式化,支持需要编程语言。VScode提供了强大的Prettier插件,点我安装,安装完插件,便可以愉快的玩耍了。

2.1 Perttier与Eslint区别

1、Eslint针对的是javascript,他是一个检测工具,包含js语法以及少部分格式问题,在Eslint看来,语法对了就能保证代码正常允许,格式问题属于其次;2、prettier属于格式化工具,它看不惯格式不统一,所以它就把Eslint没干好的事接着干,另外prettier支持包含js在内的多种语言

总结:Eslintprettier这俩兄弟一个保证js代码质量,一个保证代码美观。共同点就是标准化代码规范。

2.2 依赖安装

1 Eslint搭配prettier使用步骤,首先安装插件eslint-config-prettiereslint-plugin-prettier

pnpm install  eslint-plugin-prettier prettier eslint-config-prettier -D

2 其次配置.eslintrc.cjs文件,增加如下配置:

module.exports = {
  ...
  + extends: ["plugin:prettier/recommended"]
  ...
}

如果有其他扩展,则plugin:prettier/recommended放在最后.

3 创建.prettierrc.cjs文件,可以新增规则,对.eslintrc.cjsrules进行覆盖,常用配置如下:

module.exports = {
    // 指定每个缩进级别的空格数<int>,默认2
    tabWidth: 4,
    // 用制表符而不是空格缩进行<bool>,默认false
    useTabs: false,
    //一行的字符数,如果超过会进行换行,默认为80
    printWidth: 300,
    //字符串是否使用单引号,默认为false,使用双引号
    singleQuote: true,
    //避免报错delete (cr)的错
    endOfLine: 'auto',
    // 换行,always:超过printWidth就换行,never:不换行,preserve:按照原样处理
    proseWrap: 'always',
    // 不加分号
    semi: false,
    // 结尾处不加逗号
    trailingComma: 'none',
    // 忽略'>'下落问题
    htmlWhitespaceSensitivity: 'ignore'
}

这里的执行逻辑顺序是:eslint会首先读extends的规则,这个时候遇到了最后配置的plugin:prettier/recommended,而这个插件又会先读本地配置的.prettierrc文件再读取prettier自己内部设置的配置,最后读.eslintrc.cjsrules配置。所以.eslintrc.cjsrules优先级最高,可以覆盖.prettierrc的部分配置。

优先级:本地.eslintrc.cjsrules > 本地.prettierrc>prettier内部配置>extends其他配置>eslintrc内部默认配置。

prettier配置完成后,再通过eslint插件对文件进行格式化,就能够正常格式化了。由此可知,对eslint进行扩展之后,prettier能够对js代码做的事,eslint也能,只要你制定好规则以及对应的处理。

2.3 安装Scripts命令

执行下面命令,在package.json的scripts中生成命令,执行命令可以进行文件格式化

pnpm pkg set scripts.format='prettier --write "./**/*.{html,vue,ts,js,json,md,css}"'

2.4 配置文件保存自动格式

同样的,我们不可能每写一行代码,就运行pnpm format来美化一次代码,我们希望保存代码时,就能够自动格式化代码。于是又需要安装prettier插件。然后再ctrl+shift+p打开vscodesetting.json文件,添加如下配置:

 //prettier可以格式化很多种格式,所以需要在这里对应配置下
 "[html]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[css]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[less]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[vue]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[javascript]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescriptreact]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[jsonc]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescript]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[json]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  //这个设置在ctrl+s的时候,会启用默认的格式化,这里是prettier
  "editor.formatOnSave": true

或者在项目中的.vscode目录中的settings.json中进行如下配置:

 //prettier可以格式化很多种格式,所以需要在这里对应配置下
 "[html]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[css]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[less]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[vue]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[javascript]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescriptreact]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[jsonc]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescript]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[json]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  //这个设置在ctrl+s的时候,会启用默认的格式化,这里是prettier
  "editor.formatOnSave": true

2.5 配置prettierignore

.perttierigonre文件用于忽略那些文件不需要Perttier进行格式化,功能和.gitignore类似。项目根目录创建.perttierignore,配置如下:

dist
node_modules
**/*.svg
**/*.sh

3 husky

Git中也存在一些钩子函数,通常成为git hook,其中较常用的有pre-pushpre-commit,其中pre-commit钩子会在commit前触发,pre-push会在push前触发。注意:所有钩子默认情况下是禁用的。

为了保证不同的协作者,每次提交的git代码都是符合Eslint规范的,避免提交格式不统一或者错误的代码,为此我们可以使用Eslint配合git hook, 在进行git commit的时候验证Eslint规范。如果Eslint验证不通过,则不能提交。

husky就是我们需要的git hook工具。使用husky,我们可以很方便配置git hook脚本,例如: pre-commitpre-pushcommit-msg 等.通过git hook触发Eslint规则校验,规范代码提交。

下面是相关配置:

3.1 安装依赖

pnpm install husky  -D

3.2 husky配置

1 在package.json中添加脚本命令,用于生成.husky目录

pnpm pkg set scripts.prepare="husky install"

2 执行命令pnpm prepare,在根目录创建.husky文件夹,将git hooks钩子交由husky执行,每次执行pnpm install会生成.husky脚本目录,如果目录存在,不会重复生成。

pnpm  prepare

3 添加commit-msg钩子,在执行git commit命令时执行信息校验。

npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

4 为了便于记住命令,可以将步骤3中的命令加入到package.jsonscripts

 "commit-msg": "npx husky add .husky/commit-msg \"npx --no-install commitlint --edit '$1'\""

4 lint-staged

通过git提交代码时,通过husky触发git hook钩子进行eslint或者prettier校验时,往往都是全目录或者指定目录进行代码规范检测,这样就比较消耗时间,影响性能。我们都希望只对提交的代码进行规范检测,避免全目录检测,此时lint-staged刚好能满足我们的需求。

lint-staged只扫描git暂存区的文件而不是全盘扫描,这样每次lint量就比较小,而且是符合我们的需求的。首先需要注意,Lint-staged仅仅是文件过滤器,不会帮你格式化任何东西,只需要在package.json中进行简单配置。

4.1 依赖安装

pnpm install lint-staged -D

4.2 lint-staged配置

1 在package.json中进行如下配置:

{
    "lint-staged": {
        "*.{js,ts,tsx,jsx,json,md}": ["prettier --write", "eslint --fix"],
        "*.vue": ["prettier --parser=vue --write", "eslint --fix"],
        "*.css": ["prettier --write"]
    }
}

2 设置pre-commit为运行lint-staged.在完成上面的配置之后,可以手动通过npx lint-staged, 来检查暂存区里面的文件。当然我们也可以通过git hook的钩子pre-commit来进行自动控制。执行下面命令在husky中进行设置:

npx husky add .husky/pre-commit "npx lint-staged"

3 同时也可以将步骤2种的命令配置在package.jsonscripts中:

pnpm pkg set scripts.pre-commit="npx husky add .husky/pre-commit 'npx lint-staged'"

lint-staged过滤文件采用glob模式,git commit时触发pre-commit钩子,运行lint-staged命令,执行eslint或者prettier命令。在git commit的时候就自动的回去帮我们跑检查脚本,而且还是只针对我们本次提交的代码进行检查。

5 commitlint

前面配置了通过husky执行了pre-commit钩子,在代码git commit前执行npx lint-staged,规范暂存区代码。下面就接着配置代码提交commit-message规范。多人协作开发,在提交代码时经常出现五花八门的commit-message,使得代码提交注释不统一,使得代码reReview很难。

commitlint从名字就能看出是提交时lint,它针对commit-message进行lint.commitlint是一个提交代码时注释规范的工具。与Eslint类似,它定义了一套标准的代码提交注释信息规范。相关配置如下:

5.1 安装依赖

pnpm install @commitlint/cli @commitlint/config-conventional -D
  • @commitlint/cli: commitlint的命令行工具
  • @commitlint/config-conventional: commitlint校验的规则集,比较常用的Conventional CommitsAngular约定

注意: @commitlint/config-conventional在后面会被移除

5.2 commitlint配置

1 配置commitlint规则commitlint.config.cjs,注意文件名称后缀设置为.cjs

module.exports = {
    extends: ['@commitlint/config-conventional']
}

2 此时文件commitlint.config.cjs会爆红,请在tsconfig.json增加如下设置

{
    "include": ["commitlint.config.cjs"]
}

如果爆红未消失,重启vscode

3 commitlint.config.cjs参数相关说明:生成的配置文件是默认的规则,也可以自己定义规则,提交格式则如下:

<type>(<scope>): <subject>

I) type为必填项,用于指定commit的类型

II)scope为非必填项,用于描述改动的范围,可以是文件的名称,最好包含路径III)subject是必填项,这次提交的日志信息,提交日志必须有意义。

一般情况下,commitlint.config.cjs中插件@commitlint/config-conventional默认的规则就够用了。当然,如果需要自定义限制这些规则,不启用默认的规则,可以把配置写的更详细

module.exports = {
    extends: ['@commitlint/config-conventional'],
    rules: {
        'type-enum': [2, 'always', ['upd', 'feat', 'fix', 'refactor', 'docs', 'chore', 'style', 'revert']],
        'type-case': [0],
        'type-empty': [0],
        'scope-empty': [0],
        'scope-case': [0],
        'subject-full-stop': [0, 'never'],
        'subject-case': [0, 'never'],
        'header-max-length': [0, 'always', 72]
    }
}

rule配置说明: rule由name和配置数组组成,如:‘name:[0, ‘always’, 72]’,数组中第一位为level,可选0,1,2,0为disable,1为warning,2为error,第二位为应用与否,可选always|never,第三位该rule的值。

4 配置husky,让husky触发git hook钩子pre-commit。相关设置已在步骤3.2husky配置第三步,第四步配置过了,请跳转查看。

6 commitizen

上面通过commitlint规范了代码提交时commit-message的标准,但操作起来非常不顺手,交互感不够友好,很别扭。别着急,下面介绍的commitizen工具就能优化我们的交互体验。

Commitizen是一个用于撰写Git提交信息的工具。它可以帮助开发人员遵循一个规范,以便更容易地阅读和维护Git仓库历史记录。Commitizen采用了一个交互式的命令行界面,引导你逐步填写必要的数据,从而生成符合规范的Git提交信息。相关配置如下:

6.1依赖安装

pnpm install commitizen cz-conventional-changelog -D

6.2 commitizen配置

1 在package.json 中添加commit指令, 执行git-cz指令

pnpm pkg set scripts.commit="git add . && git-cz"

2 在项目目录里,运行下面的命令,使其支持VueCommit message格式,自动初始化命令行的选项信息

commitizen init cz-conventional-changelog --save --save-exact

3 在package.json中添加配置:

 ...
 "config": {
        "commitizen": {
            "path": "node_modules/cz-conventional-changelog"
        }
  },
  ...

以后,凡是用到git commit命令,一律改为使用git cz。这时,就会出现选项,用来生成符合格式的Commit message。通过执行命令pnpm commit测试如下:

7 cz-customizable

上面通过commitizen可以进行交互式操作,通过引导完成提交信息的填写,但是是英文的,也不够灵活。

cz-customizable是一个Commitizen的插件,它允许你使用自定义的Git提交规范。通过为项目添加一个配置文件,你可以指定你自己的提交格式,并在使用Commitizen时使用该格式。你可以轻松地定义自己的提交类型、作用域和描述等信息.

7.1 依赖安装

pnpm install  cz-customizable commitlint-config-cz --D
  • cz-customizable可自定义的Commitizen插件(或独立实用程序),可帮助实现一致的提交消息

  • commitlint-config-cz用于配置cz-customizable提交模板和共享规则给commitlint校验

7.2 cz-customizable配置

1 在package.json文件中,添加以下字段:

"config": {
  "commitizen": {
    - "path": "node_modules/cz-conventional-changelog",
    + "path": "./node_modules/cz-customizable"
  }
}

更改commitizen的path为./node_modules/cz-customizable,移除node_modules/cz-conventional-changelog,此时也可以移除node_modules/cz-conventional-changelog相关依赖。

2 然后,在项目根目录下添加一个.cz-config.cjs文件,注意:文件后缀为:cjs,并定义你的提交类型、作用域和描述等信息。例如:

module.exports = {
    types: [
        {
            value: ':sparkles: feat',
            name: '✨ feat:     新功能'
        },
        {
            value: ':bug: fix',
            name: '🐛 fix:      修复bug'
        },
        {
            value: ':package: build',
            name: '📦️ build:    打包'
        },
        {
            value: ':zap: perf',
            name: '⚡️ perf:     性能优化'
        },
        {
            value: ':tada: release',
            name: '🎉 release:  发布正式版'
        },
        {
            value: ':lipstick: style',
            name: '💄 style:    代码的样式美化'
        },
        {
            value: ':recycle: refactor',
            name: '♻️  refactor: 重构'
        },
        {
            value: ':pencil2: docs',
            name: '✏️  docs:     文档变更'
        },
        {
            value: ':white_check_mark: test',
            name: '✅ test:     测试'
        },
        {
            value: ':rewind: revert',
            name: '⏪️ revert:   回退'
        },
        {
            value: ':rocket: chore',
            name: '🚀 chore:    构建/工程依赖/工具'
        },
        {
            value: ':construction_worker: ci',
            name: '👷 ci:       CI related changes'
        }
    ],
    scopes: [{ name: 'components' }, { name: 'assets' }, { name: 'router' }, { name: 'utils' }, { name: 'views' }, { name: 'types' }],
    messages: {
        type: '请选择提交类型(必填)',
        customScope: '请输入文件修改范围(可选)',
        subject: '请简要描述提交(必填)',
        body: '请输入详细描述(可选)',
        breaking: '列出任何BREAKING CHANGES(可选)',
        footer: '请输入要关闭的issue(可选)',
        confirmCommit: '确定提交此说明吗?'
    },
    allowCustomScopes: true,
    subjectLimit: 100
}

3 在package.json中增加如下配置:用于指定cz-config.cjs文件位置

 "config": {
        "cz-customizable": {
            "config": ".cz-config.cjs"
        }
  },

cz-customizable 会首先在项目根目录下寻找: .cz-config.js.config/cz-config.js,如果找不到,会去主目录寻找。我们也可以在package.json中手动去指定配置文件的路径

7.3 git-commit-emoji安装

1 git-commit-emoji是一款支持在commit-message中输入emoji的插件,丰富提交信息

pnpm install commitlint-config-git-commit-emoji -D

2 更新commitlint.config.cjs移除extends中原来的@commitlint/config-conventional,加入'git-commit-emoji', 'cz'

module.exports = {
    extends: ['git-commit-emoji', 'cz']
}

最后,你可以使用以下命令来代替git commit:

pnpm commit

这将启动Commitizen的交互式命令行界面,并引导你逐步填写必要的数据。此时你看到的便是定制话的commit-message交互式界面,还是带表情中文的。

8 conventional-changelog

通过插conventional-changelog可以轻松的将commit-message转化为changelog

8.1 依赖安装

pnpm install conventional-changelog conventional-changelog-cli -D

8.2 conventional-changelog配置

1 将changelog脚本添加到您的package.json

pnpm pkg set scripts.changelog="conventional-changelog -p cz-config.cjs -i CHANGELOG.md -s -r 0"

参数说明

-p 指定风格*
-i CHANGELOG.md 指定输出的文件名称
-s 输出到infile,这样就不需要指定与outfile相同的文件
-r 从最新的版本生成多少个版本。如果为0,则整个更改日志将被重新生成,输出文件将被覆盖。默认值:1
-n ./changelog-option.js 指定自定义配置

2 运行命令生成最新CHANGELOG

pnpm changelog

至此,更新日志生成完毕。

9 VsCode插件

上面都是通过命令行窗口进行代码提交信息交互,相对来说还不够智能。下面介绍一款VScode插件git-commit-plugin点我下载安装。该插件会在VScodegit插件的左上角生成一个小图标,点击按照交互式操作进行提交信息填写。

五 项目下载

1 git clone 项目源码到本地

git clone https://github.com/AnyFork/vite-standard-template.git

2 安装项目依赖

pnpm install

3 项目运行

pnpm dev

六 参考文档

https://blog.csdn.net/Jackson_Wen/article/details/127921063

https://zhuanlan.zhihu.com/p/270662897

https://codeleading.com/article/66646301239/

About

这是一个基于Vite4.X + Vue3.X + TypeScript + Naive UI + Pinia + VueRouter + Unocss + Alova + Eslint + Prettier + husky + lint-staged + commitlint + commitizen + cz-customizable+ conventional-changelog构建的标准的vue项目模板。整个项目包依赖采用pnpm进行依赖管理,Node版本为16.17.0, 同时也集成了项目中常用的插件,包含组件自动导入API

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published