自己动手搭一个基于Vite 2.0、typescript、vue3.0的CLI(eslint、stylelint、jest)

478 阅读1分钟

对于前端的代码,各种各样的书写风格都可能出现。
比如这样的:

handelSelect(item) {
  this.chechedType = item
  this.$emit("genSelect", item);
},handleClick(val) { console.log(val)}

对于这种代码我们必须要一种强制的约束才能避免它的出现,从而导致项目中出现看不懂的代码。

vite2.0已经发布有一段时间了,最近准备用在公司的项目上,第一步当然是开始搭建vite2.0的CLI,初步的想法是集成jest、eslint、prettier、stylelint、vue-test-unit。

首先,初始化一个vite2.0的项目:

yarn create @vitejs/app

在template选项中选择vue-ts 这样我们就得到了一个干净的vite+Vue 3.0工程模板。

接下来往里面集成:

首先是editorconfig,这里我使用的编辑器是Vscode。 项目的根目录中新建.editorconfig文件,内容如下:

# http://editorconfig.org

root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
max_line_length = 100
trim_trailing_whitespace = true

[*.md]
max_line_length = 0

然后在vscode的插件中安装eslint并配置:

现在应用商店中找到它:

eslint.png 接下来编辑vscode的setting项,让它能够支持eslint的功能: 这个setting文件位于/Users/你的用户名/Library/Application Support/Code/User/settings.json (MAC)
加入下面的这一段代码:

"eslint.codeAction.showDocumentation": {
  "enable": true  // 在工作区启动并显示eslint
},
"eslint.format.enable": true,  // 启用eslint的功能
"eslint.probe": [   // 启用eslint支持的项
  "javascript",
  "javascriptreact",
  "typescript",
  "typescriptreact",
  "html",
  "vue",
  "markdown"
],
"editor.codeActionsOnSave": {  // 保存代码时自动修复代码的格式错误
  "source.fixAll.eslint": true
},
"eslint.nodeEnv": ""

安装eslint:

由于我要使用到typescript、prettier、vue3.0 所以它们对应的插件也要装上:

yarn add -D eslint-config-prettier eslint eslint-plugin-import eslint-plugin-prettier eslint-plugin-vue prettier @typescript-eslint/eslint-plugin @typescript-eslint/parser

编辑eslintrc.js:

module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: [
    'plugin:vue/vue3-recommended', // 导入vue3的格式建议拓展
    'plugin:@typescript-eslint/recommended', // 使用ts导入ts的格式建议拓展
  ],
  parserOptions: {
    ecmaVersion: 2020,
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
    jsxPragma: 'React', // jsx的解析方式
    ecmaFeatures: {
      jsx: true,
      tsx: true,
    },
  },
  parser: 'vue-eslint-parser',
  plugins: ['vue', '@typescript-eslint', 'prettier'],
  rules: {
    // js/ts
    'eol-last': 'error',  // 禁止文件末尾保留一行空行
    'no-trailing-spaces': 'error', // 禁用行尾空白
    'comma-style': ['error', 'last'],  // 逗号的风格,在末尾补上逗号
    'comma-dangle': ['error', 'always-multiline'],  // 在[].{}中换行的尾部用上逗号
    'quotes': ['error', 'single', { avoidEscape: true, allowTemplateLiterals: true }], // 默认单引号
    'camelcase': ['error', { properties: 'never' }], // 驼峰语法
    'semi': ['error', 'never'], // 禁止使用半分号
    'indent': ['error', 2, { SwitchCase: 1 }],  // 前空隙,2个单位
    'object-curly-spacing': ['error', 'always'], // 强制在{}中使用一致的空格
    'arrow-parens': ['error', 'as-needed'], // 箭头函数需要括号
    '@typescript-eslint/explicit-module-boundary-types': 'off', // ts的约束
    '@typescript-eslint/no-explicit-any': 'off', // ts的约束
    '@typescript-eslint/member-delimiter-style': [  // ts的约束
      'error',
      {
        multiline: {
          delimiter: 'none',
          requireLast: false,
        },
        singleline: {
          delimiter: 'semi',
          requireLast: true,
        },
      },
    ],
    // vue相关约束
    'vue/no-v-html': 'off',
    'vue/singleline-html-element-content-newline': 'off',
    'vue/html-self-closing': [
      'error',
      {
        html: {
          void: 'never',
          normal: 'never',
          component: 'always',
        },
      },
    ],
    'vue/max-attributes-per-line': [
      'error',
      {
        singleline: 3,
        multiline: 1,
      },
    ],
    'vue/require-default-prop': 'off',
    'vue/html-closing-bracket-spacing': 'error',
  },
}

接下来编辑.eslintignore:

*.sh
node_modules
*.md
*.woff
*.ttf
.vscode
.idea
dist
/public
/docs
.husky
.local
/bin
Dockerfile

编辑.prettierrc.js:

module.exports = {
  printWidth: 100,
  tabWidth: 2,
  useTabs: false,   // 使用空格缩进
  semi: true,   // 分号
  vueIndentScriptAndStyle: true,
  singleQuote: true,
  quoteProps: 'as-needed',
  bracketSpacing: true,  //对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
  trailingComma: 'es5',
  jsxBracketSameLine: false,
  jsxSingleQuote: false,  // jsx 属性使用双引号
  arrowParens: 'avoid',  // 箭头函数只有一个参数的时候可以忽略括号
  insertPragma: false,
  requirePragma: false,
  proseWrap: 'preserve',  // 换行方式
  htmlWhitespaceSensitivity: 'strict',
  endOfLine: 'auto',  // 行结束符使用 Unix 格式
  rangeStart: 0
};

至此,我们的js代码已经能在保存的时候自动修复格式的错误了。

接下来我们安装stylelint:

yarn add -D stylelint stylelint-config-prettier stylelint-config-standard
新建stylelint.config.js文件:

module.exports = {
  root: true,
  extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
  rules: {
    // 不要使用已被 autoprefixer 支持的浏览器前缀
    'media-feature-name-no-vendor-prefix': true,
    'at-rule-no-vendor-prefix': true,
    'selector-no-vendor-prefix': true,
    'property-no-vendor-prefix': true,
    'value-no-vendor-prefix': true,
  },
  ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],
};

现在错误格式的CSS也能被指示出来了。

接入jest

在协作开发的过程中,有严格代码规范特别重要,并且能做好前端的单元测试特别重要,鉴于这个原因,我们接下来将在项目中引入jest,在vue-cli在能够勾选完成单元测试的一些配置和引用,这里我将说明一下不同的配置项,不同的包的作用是什么。
首先,我们要导入jest的库:

yarn add @types/jest babel-jest ts-jest jest vue-jest --dev

由于我使用的是Vue 3.0所以在这里需要指定vue-jest的版本为:5.0.0-alpha.5
这里,我模仿elementUI的packages设定来做,使用lerna来做packages的拆分,每个package一个package.json,接下来构建lerna.json:

{
  "packages": [
    "packages/*"
  ],
  "version": "independent",
  "npmClient": "yarn",
  "useWorkspaces": true
}

在根目录的package.json中指定workplace:

{
  ...
  "private": true,
  ...
  "workspaces": [
    "packages/*"
  ],
  ...
}

然后是jest.config.js的配置项:

module.exports = {
  globals: {
    // work around: https://github.com/kulshekhar/ts-jest/issues/748#issuecomment-423528659
    'ts-jest': {
      diagnostics: {
        ignoreCodes: [151001],
      },
    },
  },
  testEnvironment: 'jsdom',  // 测试的环境
  transform: {
    '^.+\\.vue$': 'vue-jest',
    '^.+\\.(t|j)sx?$': [
      'babel-jest',
      {
        presets: [
          [
            '@babel/preset-env',
            {
              targets: {
                node: true,
              },
            },
          ],
          '@babel/preset-typescript',
        ],
        plugins: ['@vue/babel-plugin-jsx'], // 主要是配置jsx运行环境
      },
    ],
  },
  moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
  // u can change this option to a more specific folder for test single component or util when dev
  // for example, ['<rootDir>/packages/input']
  roots: ['<rootDir>'],
}

有了这些我们可以开始正式的编写属于我们的package了: 新建一个package目录,在下面新建src,__test__目录

image.png
其中,__test__目录用于放测试的脚本,这里我写了一个最简单的demo:

index.vue:
<template>
  <div>
    {{ content }}
  </div>
</template>

<script lang='ts'>
import {
  defineComponent,
} from 'vue'

export default defineComponent({
  name: 'MyComponentA',
  props: {
    content: {
      type: String,
      default: '',
    },
  },
})
</script>
myComponentA.spec.ts:
import { mount } from '@vue/test-utils'
import myComponentA from '../src/index.vue'

describe('myComponentA.vue', ( )=> {
  const testText = 'this is a beautiful girl !'
  test('render', () => {
    const wrapper = mount(myComponentA, {
      props: {
        content:testText,
      },
    })
    expect(wrapper.text()).toEqual(testText) // 检查渲染出来的文本是否包含上面的内容 涉及一些jest的API
  })
})

写好上面的内容后,我们在根目录运行jest: image.png 测试通过!
这里是demo的代码
接下来,我还要往里面集成jest进行自动化测试。