Vue中的项目搭建规范

257 阅读5分钟

一、 使用脚手架创建Vue3项目的相关选项

image-20220228104744308.png

二、代码规范

1.1 集成 .editorconfig 配置

EditorConfig有助于为不同IDE编辑器上处理同一项目的多个开发人员维护一致的编码风格。

# https://editorconfig.org

root = true

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

# 仅有 md 文件适用以下规则
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

vscode需要安装下面的插件以使EditorConfig生效。

image-20220228110728493.png

1.2 使用prettier工具

Prettier是一款强大的代码格式化工具,支持JavaScript、TypeScript、CSS、SCSS、Less、JSX、Angular、Vue、GraphQL、JSON、Markdown等语言,基本上前端用到的文件格式它都可以搞定,是当下最流行的代码格式化工具。

1、安装prettier

npm install prettier -D

2、配置.prettierrc文件:

  • tabWidth:tab缩进大小,默认为2
  • useTabs:使用tab缩进,默认false(空格)
  • semi:使用分号, 默认true
  • singleQuote:使用单引号, 默认false(在jsx中配置无效, 默认都是双引号)
  • printWidth:每一行代码长度,常用为80、100、120
  • TrailingCooma:行尾逗号,默认none,可选 none|es5|all,es5 包括es5中的数组、对象,all 包括函数对象等所有可选
  • arrowParens:箭头函数参数括号,默认avoid 可选 avoid| always,avoid 能省略括号的时候就省略 例如x => x, always 总是有括号
{
    "tabWidth": 2,
    "useTabs": false,
    "semi": true,
    "singleQuote": false,
    "printWidth": 80,
    "TrailingCooma": "none",
    "arrowParens": "always"
}

3、配置.prettierignore忽略文件

/dist/*
.local/dist
.output.js
/node_modules/**

**/*.svg
**/*.sh

/public/*

vscode需要安装prettier插件

image-20220228113901392.png

1.3 使用ESLint检测

使用脚手架创建Vue项目时,如果选择ESLint,Vue会默认帮助我们配置需要的ESLint环境,并且解决eslint和prettier的冲突。

1.4 git Husky和eslint

通过Husky,可以在执行git commit命令的时候对其进行校验,如果不符合eslint规范,那么会自动通过规范进行修复。

husky自动配置命令:

npx husky-init && npm install

1.5 git commit规范

1.5.1 使用Commitizen 编写规范的 commit message

1.安装Commitizen

npm install commitizen -D

2.安装cz-conventional-changelog,并且初始化cz-conventional-changelog:

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

这个时候我们提交代码需要使用 npx cz

  • 第一步是选择type,本次更新的类型
Type作用
feat新增特性 (feature)
fix修复 Bug(bug fix)
docs修改文档 (documentation)
style代码格式修改(white-space, formatting, missing semi colons, etc)
refactor代码重构(refactor)
perf改善性能(A code change that improves performance)
test测试(when adding missing tests)
build变更项目构建或外部依赖(例如 scopes: webpack、gulp、npm 等)
ci更改持续集成软件的配置文件和 package 中的 scripts 命令,例如 scopes: Travis, Circle 等
chore变更构建流程或辅助工具(比如更改测试环境)
revert代码回退
  • 第二步选择本次修改的范围(作用域)

image-20210723150147510

  • 第三步选择提交的信息

image-20210723150204780

  • 第四步提交详细的描述信息

image-20210723150223287

  • 第五步是否是一次重大的更改

image-20210723150322122

  • 第六步是否影响某个open issue

image-20210723150407822

我们也可以在scripts中构建一个命令来执行 cz

image-20210723150526211

提交时运行

npm run commit

1.5.2 用commitlint进行代码提交验证

git commit 按照规范的格式进行验证,不符合规范时拒绝提交。

  • 我们可以通过commitlint来限制提交;

1.安装 @commitlint/config-conventional 和 @commitlint/cli

npm i @commitlint/config-conventional @commitlint/cli -D

2.在根目录创建commitlint.config.js文件,配置commitlint

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

3.使用husky生成commit-msg文件,验证提交信息:

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

二. 第三方库集成

2.1. vue.config.js配置

vue.config.js有三种配置方式:

  • 方式一:直接通过CLI提供给我们的选项来配置:
    • 比如publicPath:配置应用程序部署的子目录(默认是 /,相当于部署在 https://www.my-app.com/);
    • 比如outputDir:修改输出的文件夹;
  • 方式二:通过configureWebpack修改webpack的配置:
    • 可以是一个对象,直接会被合并;
    • 可以是一个函数,会接收一个config,可以通过config来修改配置;
  • 方式三:通过chainWebpack修改webpack的配置:
    • 是一个函数,会接收一个基于 webpack-chain 的config对象,可以对配置进行修改;
const { defineConfig } = require("@vue/cli-service");
const path = require('path')

module.exports = defineConfig({
  outputDir: './build',
  // configureWebpack: {
  //   resolve: {
  //     alias: {
  //       views: '@/views'
  //     }
  //   }
  // }
  // configureWebpack: (config) => {
  //   config.resolve.alias = {
  //     '@': path.resolve(__dirname, 'src'),
  //     views: '@/views'
  //   }
  // },
  chainWebpack: (config) => {
    config.resolve.alias.set('@', path.resolve(__dirname, 'src')).set('views', '@/views')
  }
});

2.2. 使用vue-router最新版

安装vue-router的最新版本:

npm install vue-router@next

2.3. 使用vuex最新版

安装vuex最新版本:

npm install vuex@next

2.4. element-plus集成

Element Plus,一套为开发者、设计师和产品经理准备的基于 Vue 3.0 的桌面端组件库:

  • 它的使用方式和很多其他的组件库是一样的,所以学会element-plus,其他类似于ant-design-vue、NaiveUI、VantUI(移动端)都是差不多的;

安装element-plus

npm install element-plus

2.4.1. 全局引入

一种引入element-plus的方式是全局引入,代表的含义是所有的组件和插件都会被自动注册:

import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'

import router from './router'
import store from './store'

createApp(App).use(router).use(store).use(ElementPlus).mount('#app')

2.4.2. 局部引入

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

npm install -D unplugin-vue-components unplugin-auto-import

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

Vite
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default {
  plugins: [
    // ...
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
}
Webpack
// webpack.config.js
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')

module.exports = {
  // ...
  plugins: [
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
}

想了解更多打包 (RollupVue CLI) 和配置工具,请参考 unplugin-vue-components 和 unplugin-auto-import

2.5. axios集成

安装axios:

npm install axios

封装axios:

import axios, { AxiosResponse, AxiosInstance, AxiosRequestConfig } from "axios";
import { ElLoading } from "element-plus";

interface InterceptorHooks {
  requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig;
  requestInterceptorCatch?: (error: any) => any;

  responseInterceptor?: (response: AxiosResponse) => AxiosResponse;
  responseInterceptorCatch?: (error: any) => any;
}

interface RequestConfig extends AxiosRequestConfig {
  showLoading?: boolean;
  interceptorHooks?: InterceptorHooks;
}

interface Data<T> {
  data: T;
  returnCode: string;
  success: boolean;
}

class AxiosRequest {
  config: AxiosRequestConfig;
  interceptorHooks?: InterceptorHooks;
  showLoading: boolean;
  loading?: any;
  instance: AxiosInstance;

  constructor(options: RequestConfig) {
    this.config = options;
    this.interceptorHooks = options.interceptorHooks;
    this.showLoading = options.showLoading ?? true;
    this.instance = axios.create(options);

    this.setupInterceptor();
  }

  setupInterceptor(): void {
    this.instance.interceptors.request.use(
      this.interceptorHooks?.requestInterceptor,
      this.interceptorHooks?.requestInterceptorCatch
    );
    this.instance.interceptors.response.use(
      this.interceptorHooks?.responseInterceptor,
      this.interceptorHooks?.requestInterceptorCatch
    );

    this.instance.interceptors.request.use((config) => {
      if (this.showLoading) {
        this.loading = ElLoading.service({
          lock: true,
          text: "Loading",
          spinner: "el-icon-loading",
          background: "rgba(0, 0, 0, 0.7)",
        });
      }
      return config;
    });

    this.instance.interceptors.response.use(
      (res) => {
        this.loading?.close();
        return res;
      },
      (err) => {
        this.loading?.close();
        return err;
      }
    );
  }

  request<T = any>(config: RequestConfig): Promise<T> {
    if (!config.showLoading) {
      this.showLoading = false;
    }
    return new Promise((resolve, reject) => {
      this.instance
        .request<any, Data<T>>(config)
        .then((res) => {
          resolve(res.data);
          this.showLoading = true;
        })
        .catch((err) => {
          reject(err);
          this.showLoading = true;
        });
    });
  }

  get<T = any>(config: RequestConfig): Promise<T> {
    return this.request({ ...config, method: "GET" });
  }

  post<T = any>(config: RequestConfig): Promise<T> {
    return this.request({ ...config, method: "POST" });
  }

  delete<T = any>(config: RequestConfig): Promise<T> {
    return this.request({ ...config, method: "DELETE" });
  }

  patch<T = any>(config: RequestConfig): Promise<T> {
    return this.request({ ...config, method: "PATCH" });
  }
}

export default AxiosRequest;