Vue2.x项目工程环境搭建思路

900 阅读5分钟

前言

去年年底,公司的前端团队确立了代码规范。然而离开了实际项目环境加上普遍紧张的开发周期,规范终究是纸上谈兵。鉴于此,一位同事去开发脚手架。我也花了几天时间搭建了一套 Vue2.x 的工程环境尝试给予一些支持,虽然后面看来对他的实际帮助不大。

由于公司目前使用 Vue3 的项目几乎没有,所以选择搭建 Vue2 的模板。我希望这个模板能强制代码规范、提交规范;保证代码的可靠性的同时提升开发效率;并且为未来可能升级 Vue3 提供一些便利。

本文将主要描述我在搭建环境与封装的整体思路,不会详细描述搭建细节,需要具体实现的小伙伴可以查看文末的参考。但是我不建议照搬这些操作,由于版本的差异会导致踩奇奇怪怪的坑,最可靠的方法的仍是直接去查阅官方文档。

技术栈

  • 编程语言:TypeScript 4.x + Javascript
  • 构建工具: Vue Cli 5
  • 前端框架: Vue2.x + Vue/CompositionApi
  • 路由工具:Vue Router 3.x
  • 状态管理:Pinia 2.x
  • CSS 预编译:Less
  • HTTP 工具:Axios
  • Git Hook 工具:Husky + LintStaged
  • 代码规范:Eslint + Prettier + EditorConfig
  • 提交规范:Commitizen + Commitlint
  • 单元测试:Jest
  • 依赖检测工具:Depcheck

架构搭建

使用 Vue Cli 快速初始化项目雏形

使用 Vue Cli 快速搭建基础模板,选择 vue-ts ,其他的例如 Eslint 等由于要修改配置与封装,我更倾向于自己看文档手动添加。

修改 vue.config.js 文件

const path = require('path');
const { defineConfig } = require('@vue/cli-service');
​
module.exports = defineConfig({
  transpileDependencies: true,
  chainWebpack: (config) => {
    // 配置别名后需要重启项目
    config.resolve.alias.set('@', path.resolve('src'));
  },
  publicPath: './',
});

由于我对 webpack 缺乏系统的认知,所以只是做了最简单的配置。publicPath解决的是打包后资源文件路径错误的问题,chainWebpack中设置了路径别名。之前我看网上的方法增加了一个webpack.config.js将别名配置在里面,我认为这样不好,vue.config.jswebpack.config.js本就在功能上有很大的重复,完全没有必要如此。

规范目录结构

├── public/
└── src/
    ├── assets/                    // 静态资源目录
    ├── api/                       // 接口方法目录
    ├── components/                // 公共组件目录
    ├── router/                    // 路由配置目录
    ├── store/                     // 状态管理目录
    ├── style/                     // 通用 CSS 目录
    ├── utils/                     // 工具函数目录
    ├── views/                     // 页面组件目录
    ├── App.vue
    ├── main.ts
    ├── shims-vue.d.ts
├── tests/                         // 单元测试目录
// 配置文件放在 src 同级目录下

额外说下publicassets的区别:打包后public文件夹中的文件会原封不动地放到dist文件夹中,assets文件夹中的文件会被合并到一个文件中。图片、字体等静态资源我会放在assets,第三方程序则是放在public

Vue Router

在路由文件中,登录页、首页、Layout组件我会直接引入,其他的页面则使用按需加载。

状态管理工具 Pinia

简单介绍,Pinia 是新一代 Vuex ,3月份尤大直播时说过不会有 Vuex5 了。相较于 vuex4,使用更加简单。但是我碰到了个坑,在我有一个同事的电脑上项目中的 Pinia 会报错,但是其他人的则没有问题,初步猜测跟版本号有关系。

HTTP 工具 Axios

纠结了一下午如何去封装,看了许多封装的思路,最后还是选择直接从vue-element-admin中抄了一份。

// utils/request
import axios from 'axios';
​
// create an axios instance
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_URL, // url = base url + request url
  // withCredentials: true, // send cookies when cross-domain requests
  timeout: 5000, // request timeout
});
​
// request interceptor
service.interceptors.request.use(
  // do something before request is sent
  (config) => config,
  (error) => {
    // do something with request error
    console.log(error); // for debug
    return Promise.reject(error);
  },
);
​
// response interceptor
service.interceptors.response.use(
  /**
   * If you want to get http information such as headers or status
   * Please return  response => response
   */
​
  /**
   * Determine the request status by custom code
   * Here is just an example
   * You can also judge the status by HTTP Status Code
   */
  (response) => {
    const { data } = response;
​
    return data;
  },
  (error) => {
    console.log(`err${error}`); // for debug
​
    return Promise.reject(error);
  },
);
​
export default service;

api则单独抽一个文件夹出来 ,不要全部放在一个文件与注册到原型链中。否则如果有几百个接口会不好维护,并且这样搞原型链也太大了。

另外,我以前会单独封装postget方法,没有必要这样,axios本身就有相应的api了,我的用法如下:

// api/example
import request from '@/utils/request';
​
export function useAxiosGet(param1: unknown, param2: unknown) {
  return request.get('/banner', { params: { param1, param2 } });
}
​
export function useAxiosPost(data: unknown) {
  return request.post('/banner', data);
}

CSS 预编译器 Less

Less/Sass/Stylus 都差不多,sass 会有版本号的问题,所以同事更倾向于使用 less 。

Vue/CompositionApi

composition-api会提高代码的聚合度,在一些场景下非常好用。另外如果有未来升级 Vue3 的打算,也可以使用。

代码规范

EditorConfig

EditorConfig 有助于为不同 IDE 编辑器上处理同一项目的多个开发人员维护一致的编码风格。我主要是需要这个来配置换行符,由于公司使用的都是 windows ,所以换行符设置crlf

ESLint + Prettier

我在配置这两者上花费了相当多的时间。我看到许多项目会二者都全部配上,但在实际体验后说句真心话,我认为单纯的 ESLint 已经够用了。毕竟二者本质上也只是在rules上的些许不同而已,引入它们其实只是想解决团队成员代码风格差异带来的维护困难。 (2022-4-12 修改)单纯用 ESLint 也不是不行,但 prettier 可以为 ESLint 补充一些规则,例如 css 的格式化。我的 ESLint 配置如下:

module.exports = {
  env: {
    browser: true,
    node: true,
    es2021: true,
  },
  extends: [
    'airbnb-base',
    'eslint:recommended',
    'plugin:vue/recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:jest/recommended',
    'plugin:prettier/recommended', // 添加 prettier 插件
  ],
  parser: 'vue-eslint-parser',
  parserOptions: {
    ecmaVersion: 'latest',
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
  },
  plugins: ['vue', '@typescript-eslint'],
  settings: {
    'import/resolver': {
      webpack: {
        // 此处config对应webpack.config.js的路径,我这个路径是vue-cli3默认的路径
        config: 'node_modules/@vue/cli-service/webpack.config.js',
      },
    },
  },
  rules: {
    'import/extensions': [
      'error',
      'ignorePackages',
      {
        js: 'never',
        jsx: 'never',
        ts: 'never',
        tsx: 'never',
      },
    ],
    'no-param-reassign': [
      'error',
      {
        props: true,
        ignorePropertyModificationsFor: [
          'res', // for Express responses
          'item', // for Express responses
          'state', // for vuex state 解决assignment to property of function parameter 'state'
          'config',
          'args', // for vue.config.js change options.title
        ],
      },
    ],
    indent: [
      2,
      2,
      {
        SwitchCase: 1,
      },
    ],
    complexity: [
      'error',
      {
        max: 40,
      },
    ],
    'linebreak-style': 0,
  },
};

可以看到我的配置是采用airbnb风格的规则以及一些官方推荐的风格,尽管我如此配置,但不代表我完全喜欢这些规则。例如在我看来++&&运算符非常简洁却不被推荐;no-param-reassign这条规则影响了我处理接口拿到数据。具体在配置规则时,还是要从团队的角度出发,考虑如何减少维护成本。

Husky + LintStaged

husky —— Git Hook 工具,可以设置在 git 各个阶段(pre-commitcommit-msgpre-push 等)触发我们的命令。 lint-staged —— 在 git 暂存的文件上运行 linters。

在编码时,ESLint 和 Prettier,会对我们写的代码进行实时校验,在一定程度上能有效规范我们写的代码,但可能会有人觉得这些限制很麻烦,选择无视这些提示。

所以,我们还需要做一些限制,让没通过 ESLint 检测和修复的代码禁止提交,从而保证仓库代码都是符合规范的。

为了解决这个问题,我们需要用到 Git Hook,在本地执行 git commit 的时候,就对所提交的代码进行 ESLint 检测和修复(即执行 eslint --fix),如果这些代码没通过 ESLint 规则校验,则禁止提交。

提交规范

git commit的描述信息精准,在后期维护和 Bug 处理时会变得有据可查。我选用的是社区最流行、最知名、最受认可的 Angular 团队提交规范。

Commitizen

初次接触提交规范的同学可能会头晕,不知道怎么写。Commitizen 解决的就是这个问题,项目中通过npx cz命令,跟着提示走就可以完成一次规范的提交。需要自定义配置提交,修改.cz-config.js文件即可。我的配置如下:

module.exports = {
    // type 类型(定义之后,可通过上下键选择)
    types: [
        { value: 'feat', name: 'feat:     新增功能' },
        { value: 'fix', name: 'fix:      修复 bug' },
        { value: 'docs', name: 'docs:     文档变更' },
        { value: 'style', name: 'style:    代码格式(不影响功能,例如空格、分号等格式修正)' },
        { value: 'refactor', name: 'refactor: 代码重构(不包括 bug 修复、功能新增)' },
        { value: 'perf', name: 'perf:     性能优化' },
        { value: 'test', name: 'test:     添加、修改测试用例' },
        { value: 'build', name: 'build:    构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)' },
        { value: 'ci', name: 'ci:       修改 CI 配置、脚本' },
        { value: 'chore', name: 'chore:    对构建过程或辅助工具和库的更改(不影响源文件、测试用例)' },
        { value: 'revert', name: 'revert:   回滚 commit' }
    ],
​
    // scope 类型(定义之后,可通过上下键选择)
    scopes: [
        ['components', '组件相关'],
        ['hooks', 'hook 相关'],
        ['utils', 'utils 相关'],
        ['element-ui', '对 element-ui 的调整'],
        ['styles', '样式相关'],
        ['deps', '项目依赖'],
        ['auth', '对 auth 修改'],
        ['other', '其他修改'],
        // 如果选择 custom,后面会让你再输入一个自定义的 scope。也可以不设置此项,把后面的 allowCustomScopes 设置为 true
        ['custom', '以上都不是?我要自定义']
    ].map(([value, description]) => {
        return {
            value,
            name: `${value.padEnd(30)} (${description})`
        }
    }),
​
    // 是否允许自定义填写 scope,在 scope 选择的时候,会有 empty 和 custom 可以选择。
    // allowCustomScopes: true,
​
    // allowTicketNumber: false,
    // isTicketNumberRequired: false,
    // ticketNumberPrefix: 'TICKET-',
    // ticketNumberRegExp: '\d{1,5}',
​
​
    // 针对每一个 type 去定义对应的 scopes,例如 fix
    /*
    scopeOverrides: {
      fix: [
        { name: 'merge' },
        { name: 'style' },
        { name: 'e2eTest' },
        { name: 'unitTest' }
      ]
    },
    */
​
    // 交互提示信息
    messages: {
        type: '确保本次提交遵循 Angular 规范!\n选择你要提交的类型:',
        scope: '\n选择一个 scope(可选):',
        // 选择 scope: custom 时会出下面的提示
        customScope: '请输入自定义的 scope:',
        subject: '填写简短精炼的变更描述:\n',
        body:
            '填写更加详细的变更描述(可选)。使用 "|" 换行:\n',
        breaking: '列举非兼容性重大的变更(可选):\n',
        footer: '列举出所有变更的 ISSUES CLOSED(可选)。 例如: #31, #34:\n',
        confirmCommit: '确认提交?'
    },
​
    // 设置只有 type 选择了 feat 或 fix,才询问 breaking message
    allowBreakingChanges: ['feat', 'fix'],
​
    // 跳过要询问的步骤
    skipQuestions: ['body', 'footer'],
​
    // subject 限制长度
    subjectLimit: 100,
    breaklineChar: '|', // 支持 body 和 footer
    // footerPrefix : 'ISSUES CLOSED:'
    // askForBreakingChangeFirst : true,
}

CommitLint

尽管加入了 Commitizen 降低了规范提交的难度,但总是架不住有人我行我素。CommitLint 可以限制只让符合 Angular 规范的 commit 通过。

单元测试

选择了目前流行的 Jest 作为测试框架。目前还没有实践,打算之后补上。不得不说,看了网上非常多的文章讲单测只是单纯讲 api ,我觉得意义不大,就算学会了那些也只是有术无道。单测真正的难点在于到底在于测试点的选择与颗粒度的把握,分享一篇前两天看的文章:掘金 - 《前端单测,为什么不要测 “实现细节”?》。该文的作者主张多关注prosp以及render出来的内容。

遇到的问题

  1. Pinia 在某位同事电脑上报错
  2. 同样在某位同事电脑上热部署相当慢,要10s左右的时间,而我自己基本上是秒更新。

目前猜测这两个问题都是由版本号问题导致的,准备忙完手上的项目就去好好找找原因。

最后

这篇文章早在一个月前就想写了,但是一来最近总是加班(周末又摸鱼打游戏),二是觉得搭好的环境总得上项目实际用一下才行。模板我已经放在仓库 ivestszheng/vue2-typescript-starter,感兴趣的小伙伴可以查看,也希望能给我提出一些改进的建议。

参考

  1. 掘金 - 从 0 开始手把手带你搭建一套规范的 Vue3.x 项目工程环境