wangpenghui522

现在web开发变得更加美妙高效,在于开发工具设计得更好了,丰富性与易用性,都有所提高。丰富性带来了一个幸福的烦恼,就是针对实际应用场景,如何选择工具 ?

1. Vue Cli和Vite之间的选择

Vite的开发环境体验好,基于浏览器原生ES6 Modules提供的功能,不对ES高版本语法进行转译,省略掉耗时的打包流程, 可是考虑到:

1) 项目要用到真机调试功能,开发环境下调试代码时不能使用ES高版本的语法,用着不顺畅。

后面发现可用@vitejs/plugin-legacy解决此问题。

import legacy from \'@vitejs/plugin-legacy\';

2) 受制于历史项目包袱,感受到Vite的一些痛点:

  • Vite最新版2.7.x版本自带的less-loader, 将背景色的rgba属性转换成四位16进制在有些手机上存在兼容性问题。
  • 与某些第三方工具库(比如说Cache-Router)不兼容,编译会报错。
  • Vite的缓存机制,有时候让人有些困惑,代码修改了,重启之后都不生效,要手动删除node_modules下的.vite文件夹才生效。 
  • 给命令行动态添加自定义参数不太方便。
  • Vite不支持装饰器语法,而有的第三方库用到了装饰器语法

3) Vite脚手架默认不集成TypeScript,Vue-Router,Vuex功能,使用起来不太方便

4) Vue Cli作为久经考验的成熟构建工具,稳定坑少,使用者众多,在生态环境和插件数量方面更好。

所以最终选择vue-cli最为Vue项目的脚手架。若是新项目,个人还是比较推荐使用Vite,构建速度确实快。

安装最新版本vue脚手架

npm install -g @vue/cli@next

安装成功后通过查看版本命令,确认是否安装成功

vue -V
@vue/cli 5.0.0-rc.1

2 创建vue3项目

vue create vue3-demo

Vue CLI v5.0.0-rc.1
? Please pick a preset:
  Default ([Vue 2] babel, eslint)
  Default (Vue 3) ([Vue 3] babel, eslint) // 不选择默认的vue3配置,是因为没有vue-router+typescript功能,需要自己引入
> Manually select features // 手动选择特性

Vue CLI v5.0.0-rc.1
? Please pick a preset: Manually select features
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
>(*) Choose Vue version
 (*) Babel               // 添加babel
 (*) TypeScript          // 添加类型约束功能
 ( ) Progressive Web App (PWA) Support
 (*) Router              // 添加路由功能
 (*) Vuex                // 添加状态管理功能
 (*) CSS Pre-processors  // 添加样式预编译功能  
 (*) Linter / Formatter  // 添加代码质量校验提示和格式化功能
 ( ) Unit Testing
 ( ) E2E Testing
Vue CLI v5.0.0-rc.1
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, TS, Router, Vuex, CSS Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with
  2.x
> 3.x    // 选择vue3
Vue CLI v5.0.0-rc.1
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, TS, Router, Vuex, CSS Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 3.x
? Use class-style component syntax? (y/N) N  // 是否使用class组件语法, ,选N  项目中用Composition API

 为了在Vue中使用TypeScript中,许多开发者选择了Class-Style Component 解决方案,时至今日,还有另外一个方案,Composition API 撰写的代码会完美享用类型推导,并且也不用做太多额外的类型标注。这也同样意味着你写出的 JavaScript 代码几乎就是 TypeScript 的代码。即使是非 TypeScript 开发者也会因此得到更好的 IDE 类型支持而获益。

常规风格

export default {
    data(){
        return {
            selectOptions: [\'A1\', \'A2\'],
            results: [],
            // ...
        }
    }
}

class 组件风格

import { Vue, Component } from \'vue-property-decorator\'
@Component
export default class Game extends Vue {
    // 定义data
  private selectOptions = [\'A1\', \'A2\']
  private results: string[] = []
    ...
}
? Use class-style component syntax? Yes

? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes  // 是否使用babel工具自动为转换后的 TypeScript 代码注入 polyfiills,此处选择 Y 

? Use history mode for router? (Requires proper server setup for index fallback in production) Yes   // 使用history网址路径风格,hash路径有点丑陋

? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Less    // sass在国内安装经常出错,所以选less

? Pick a linter / formatter config: Prettier  // 校验配置选择 eslint+prettier组合,沿袭项目使用习惯

? Pick additional lint features: Lint on save // 保存代码时校验代码质量, 提交代码时才去校验代码质量有些滞后

? Where do you prefer placing config for Babel, ESLint, etc. In dedicated config files // 将Babel,ESlint等配置文件从package.json中独立出来,因为json文件不能写注释

? Save this as a preset for future projects? Y // 将此配置保存起来,用于将来的项目 ,下次用vue-cli生成项目时,可以看到之前保存的配置项

启动服务

cd vue3-demo && yarn serve

在VSCode中打开app.vue发现文件中有许多红色的告警波浪线,安装Volar扩展,,绝大多数语法报错消失。顺便说一下为什么要使用Volar扩展,Vetur对<script setup>里面定义的响应式变量支持度不够好。

<script setup>
// ...
// 仅用于template,未在script中被使用,会报 count is declared but its value is never read.Vetur(6133)
const count = ref(1)
//...
</script>

3 配置vue文件保存时自动格式化

代码美化功能,是一个重要的影响开发体验的指标。书写潦草的代码,按下Ctrl+S保存之后,瞬间变成整洁有序, 这种视觉感受,让人神清气爽。

在项目下新建.vscode/settings.json,内容如下: 

{
  // ...
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "[vue]": {
    "editor.defaultFormatter": "johnsoncodehk.volar"
  },
  "[json]": {
    "editor.defaultFormatter": "vscode.json-language-features"
  },
  // ...
}

修改项目下的.prettierrc文件为.prettierrc.js, 内容如下:

module.exports = {
  // 1.一行代码的最大字符数,默认是80(printWidth: <int>)
  printWidth: 120,
  // 2.tab宽度为2空格(tabWidth: <int>)
  tabWidth: 2,
  // 3.是否使用tab来缩进,我们使用空格(useTabs: <bool>)
  useTabs: false,
  // 4.结尾是否添加分号
  semi: true,
  // 5.使用单引号(singleQuote: <bool>)
  singleQuote: true,
  // 6.object对象中key值是否加引号(quoteProps: "<as-needed|consistent|preserve>")as-needed只有在需求要的情况下加引号,consistent是有一个需要引号就统一加,preserve是保留用户输入的引号
  quoteProps: \'as-needed\',
  // 7.在jsx文件中的引号需要单独设置(jsxSingleQuote: <bool>)
  jsxSingleQuote: false,
  // 8.尾部逗号设置,es5是尾部逗号兼容es5,none就是没有尾部逗号,all是指所有可能的情况,需要node8和es2017以上的环境。(trailingComma: "<es5|none|all>")
  trailingComma: \'all\',
  // 9.object对象里面的key和value值和括号间的空格(bracketSpacing: <bool>)
  bracketSpacing: true,// 10.箭头函数单个参数的情况是否省略括号,默认always是总是带括号(arrowParens: "<always|avoid>")
  arrowParens: \'always\',
  // 11.range是format执行的范围,可以选执行一个文件的一部分,默认的设置是整个文件(rangeStart: <int>  rangeEnd: <int>)
  rangeStart: 0,
  rangeEnd: Infinity,
  // 12. requirePragma: <bool>,格式化有特定开头编译指示的文件 比如下面两种
  /**
   * @prettier
   */
  // or
  /**
   * @format
   */
  requirePragma: false,
  // 13.insertPragma: <bool> 自动插入pragma到已经完成的format的文件开头
  insertPragma: false,
  // 14. proseWrap: "<always|never|preserve>" 文章换行,默认情况下会对你的markdown文件换行 进行format会控制在printwidth以内
  proseWrap: \'always\',
  // 15. htmlWhitespaceSensitivity: "<css|strict|ignore>" html中的空格敏感性
  // html文档片段 1<b>2</b>3 原本显示为123, 不设置忽略空格的话格式化后会变成 1<b> 2 </b>3 显示为1 2 3
  htmlWhitespaceSensitivity: \'ignore\',
  // 16. vue script和style标签中是否缩进,开启可能会破坏编辑器的代码折叠
  vueIndentScriptAndStyle: false,
  // 17. endOfLine: "<lf|crlf|cr|auto>" 行尾换行符,默认是lf,
  endOfLine: \'lf\',
  // 18. 控制被引号包裹的代码是否进行格式化, 默认是auto,
  embeddedLanguageFormatting: \'off\'
}

4 配置移动端UI库

做移动端开发,虽然定制性比较强。绝大多数UI库组件都用不到,但像Toast,Modal,Picker,Form,PullRefresh等组件几乎是必用,所以需要引入移动端UI库。

vue3移动端UI库的选择:

antd-mobile-vue-next 移入项目之后,编译报错
Vux UI风格是绿色系,与现有项目使用的蓝色系风格UI不符,  所以没用
Vant 是业界主流的移动端组件库之一,UI色系风格与历史项目相符,支持vue3,组件功能优于Vux,已Toast为例,Vant提供了网络加载的Toast, Vux未提供。Vant总共提供了69个(不含组合api)涵盖基础,表单,反馈,展示,导航,业务六大类组件。

 

 yarn add vant@3

按需引入

按需加载需要借助babel-plugin-import, 这样就可以只引入需要的组件,以减小项目体积

yarn add babel-plugin-import -D

 对babel.config.js进行配置

module.exports = {
  presets: ["@vue/cli-plugin-babel/preset"],
  plugins: [
    [
      "import",
      {
        libraryName: "vant",
        libraryDirectory: "es",
        style: true,
      },
      "vant",
    ],
  ],
};

 main.js中引入vant的样式

import { createApp } from "vue";
import App from "./App.vue";
import "vant/lib/index.css";
createApp(App).mount("#app");

在App.vue中引入组件

<template>
  <div>
    <Button type="primary">主要按钮</Button>
    <img alt="Vue logo" src="./assets/logo.png" />
    <HelloWorld msg="Welcome to Your Vue.js App" />
  </div>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";
import { Button } from "vant";
export default {
  name: "App",
  components: {
    HelloWorld,
    Button,
  },
};
</script>

 5 配置开发环境请求代理

请求代理是解决本地开发请求跨域的最佳方式之一,对前后端代码都没有侵入性。  在vue.config.js中添加代理转发配置:

const { defineConfig } = require("@vue/cli-service");
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    port: 9000,
    host: "localhost",
    https: false,
    open: "/",
    proxy: {
      "/api": {
        target: "http://192.168.xx.xx:50000",
        secure: false,
        changeOrigin: true,
        logLevel: "debug",
      },
    },
  },
  // ....
});

6 配置路径别名

项目统一使用路径别名,好处是在代码中不用写../../../之类让人看着有些云里雾里的文件引用路径,此外用短路径替代长路径,书写也更方便。而且编辑器对于别名路径也有提示,可以跳转。

需要注意的是,如果使用了TypeScript,除了要在vue.config.js中配置路径别名之外,还需要在tsconfig.json配置路径别名,否则虽然打包编译不报错,但是代码编辑器却会提示引用路径有错误。

vue.config.js路径别名配置

const path = require(\'path\');

module.exports = {
    configureWebpack: {
        resolve: {
            alias: {
                \'@\': path.join(__dirname, \'src/\')
            }
        }
    }
}

 tsconfig.json路径别名配置

{
  "compilerOptions": {
    // ...
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
    }
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    // 包含自定义声明文件
    "typings/**/*.d.ts",
  ],
  // ...
}

7 设置自定义环境变量

开发调试的时候,需要动态在命令行添加动态参数,用以执行开发环境的调试逻辑,将自定义的命令行参数传到业务文件的方法是:

在package.json中传入自定义环境变量

{
  // ...
  "scripts": {
    "start:wx": "ytt && vue-cli-service serve --mode local --wx true",
  // ...
  },
}

在vue.config.js中设置自定义环境变量

// ...
const webpack = require(\'webpack\');
const { defineConfig } = require(\'@vue/cli-service\');
const { wx, mode } = require(\'minimist\')(process.argv.slice(2));
console.log({ wx, mode });

// console.log(process.env.VITE_API_HOST);
module.exports = defineConfig({
  // ...
  configureWebpack: {
    plugins: [
      // 定义环境变量
      new webpack.DefinePlugin({
        \'process.env.WX_JS_SDK_ENABLED\': wx, // 是否真机调试SDK模式
        \'process.env.CURRENT_ENV\': JSON.stringify(mode),
      }),
    ],
  },
});

在业务文件中接收自定义环境变量

import { createApp } from \'vue\';
import \'vant/lib/index.css\';
import App from \'./App.vue\';
import router from \'./router\';
import store from \'./store\';

console.log(process.env.WX_JS_SDK_ENABLED, process.env.CURRENT_ENV);

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

8 配置stylelint校验规则

stylelint的好处:

  • 可以发现样式书写问题,如无效的十六进制值;重复的选择器;未命名的动画名称;错误的线性渐变语法;后面的属性覆盖前面的属性;前面的选择器优先级更高,覆盖后面的属性;选择器下未设置任何属性等。
  • 能使所有人写的样式风格都一致,按照业内知名公司 (GitHub、Google、Airbnb)的样式规范要求写样式。
  • 保存时自动对样式书写进行优化(如采用样式的简写形式,删除无意义小数点前面的0,调整样式顺序等),修复可以修复的样式错误。

1.安装依赖

yarn  add -D stylelint stylelint-config-recess-order  stylelint-order stylelint-config-standard stylelint-less  stylelint-webpack-plugin postcss-html

2. 创建.stylelintrc.js 样式校验规则

module.exports = {
  plugins: [\'stylelint-less\'],
  extends: [\'stylelint-order\', \'stylelint-config-standard\', \'stylelint-config-recess-order\'],
  rules: {
    indentation: 2,
    \'at-rule-no-unknown\': [true, { ignoreAtRules: [\'mixin\', \'extend\', \'content\', \'include\'] }],
    \'no-empty-source\': null, //  null是关闭规则的意思--less文件内容可以为空
    \'no-descending-specificity\': null, //禁止特异性较低的选择器在特异性较高的选择器之后重写
    \'font-family-no-missing-generic-family-keyword\': null, // 关闭必须设置通用字体的规则
    \'property-no-unknown\': [
      true,
      {
        ignoreProperties: [\'box-flex\'], // 忽略某些未知属性的检测
      },
    ],
    \'selector-pseudo-element-no-unknown\': [
      true,
      {
        ignorePseudoElements: [\'ng-deep\'], // 忽略ng-deep这种合法的伪元素选择器报警
      },
    ],
    \'declaration-colon-newline-after\': null, //一个属性过长的话可以写成多行
    \'media-feature-name-no-unknown\': null, // 关闭禁止未知的媒体功能名
    // 下面的排序规则是stylelint-config-recess-order的css排序规则,
    // 要对某个属性排序进行调整,这个属性之前的样式排序都要配置在自定义属性排序中
    \'order/properties-order\': [
      {
        // Must be first.
        properties: [\'all\'],
      },
      {
        // Position.
        properties: [\'position\', \'top\', \'right\', \'bottom\', \'left\', \'z-index\'],
      },
      {
        // Display mode.
        properties: [\'box-sizing\', \'display\'],
      },
      {
        // Flexible boxes.
        properties: [\'flex\', \'flex-basis\', \'flex-direction\', \'flex-flow\', \'flex-grow\', \'flex-shrink\', \'flex-wrap\'],
      },
      {
        // Grid layout.
        properties: [
          \'grid\',
          \'grid-area\',
          \'grid-template\',
          \'grid-template-areas\',
          \'grid-template-rows\',
          \'grid-template-columns\',
          \'grid-row\',
          \'grid-row-start\',
          \'grid-row-end\',
          \'grid-column\',
          \'grid-column-start\',
          \'grid-column-end\',
          \'grid-auto-rows\',
          \'grid-auto-columns\',
          \'grid-auto-flow\',
          \'grid-gap\',
          \'grid-row-gap\',
          \'grid-column-gap\',
        ],
      },
      {
        // Align.
        properties: [\'align-content\', \'align-items\', \'align-self\'],
      },
      {
        // Justify.
        properties: [\'justify-content\', \'justify-items\', \'justify-self\'],
      },
      {
        // Order.
        properties: [\'order\'],
      },
      {
        // Box model.
        properties: [
          \'float\',
          \'width\',
          \'min-width\',
          \'max-width\',
          \'height\',
          \'line-height\',
          \'min-height\',
          \'max-height\',
          \'padding\',
          \'padding-top\',
          \'padding-right\',
          \'padding-bottom\',
          \'padding-left\',
          \'margin\',
          \'margin-top\',
          \'margin-right\',
          \'margin-bottom\',
          \'margin-left\',
          \'overflow\',
          \'overflow-x\',
          \'overflow-y\',
          \'-webkit-overflow-scrolling\',
          \'-ms-overflow-x\',
          \'-ms-overflow-y\',
          \'-ms-overflow-style\',
          \'clip\',
          \'clear\',
        ],
      },
      {
        // Typography.
        properties: [
          \'font\',
          \'font-family\',
          \'font-size\',
          \'font-style\',
          \'font-weight\',
          \'font-variant\',
          \'font-size-adjust\',
          \'font-stretch\',
          \'font-effect\',
          \'font-emphasize\',
          \'font-emphasize-position\',
          \'font-emphasize-style\',
          \'-webkit-font-smoothing\',
          \'-moz-osx-font-smoothing\',
          \'font-smooth\',
          \'hyphens\',
          \'color\',
          \'text-align\',
          \'text-align-last\',
          \'text-emphasis\',
          \'text-emphasis-color\',
          \'text-emphasis-style\',
          \'text-emphasis-position\',
          \'text-decoration\',
          \'text-indent\',
          \'text-justify\',
          \'text-outline\',
          \'-ms-text-overflow\',
          \'text-overflow\',
          \'text-overflow-ellipsis\',
          \'text-overflow-mode\',
          \'text-shadow\',
          \'text-transform\',
          \'text-wrap\',
          \'-webkit-text-size-adjust\',
          \'-ms-text-size-adjust\',
          \'letter-spacing\',
          \'word-break\',
          \'word-spacing\',
          \'word-wrap\', // Legacy name for `overflow-wrap`
          \'overflow-wrap\',
          \'tab-size\',
          \'white-space\',
          \'vertical-align\',
          \'list-style\',
          \'list-style-position\',
          \'list-style-type\',
          \'list-style-image\',
        ],
      },
      {
        // Accessibility & Interactions.
        properties: [
          \'pointer-events\',
          \'-ms-touch-action\',
          \'touch-action\',
          \'cursor\',
          \'visibility\',
          \'zoom\',
          \'table-layout\',
          \'empty-cells\',
          \'caption-side\',
          \'border-spacing\',
          \'border-collapse\',
          \'content\',
          \'quotes\',
          \'counter-reset\',
          \'counter-increment\',
          \'resize\',
          \'user-select\',
          \'nav-index\',
          \'nav-up\',
          \'nav-right\',
          \'nav-down\',
          \'nav-left\',
        ],
      },
      {
        // Background & Borders.
        properties: [
          \'background\',
          \'background-color\',
          \'background-image\',
          "-ms-filter:\\\'progid:DXImageTransform.Microsoft.gradient",
          \'filter:progid:DXImageTransform.Microsoft.gradient\',
          \'filter:progid:DXImageTransform.Microsoft.AlphaImageLoader\',
          \'filter\',
          \'background-repeat\',
          \'background-attachment\',
          \'background-position\',
          \'background-position-x\',
          \'background-position-y\',
          \'background-clip\',
          \'background-origin\',
          \'background-size\',
          \'background-blend-mode\',
          \'isolation\',
          \'border\',
          \'border-color\',
          \'border-style\',
          \'border-width\',
          \'border-top\',
          \'border-top-color\',
          \'border-top-style\',
          \'border-top-width\',
          \'border-right\',
          \'border-right-color\',
          \'border-right-style\',
          \'border-right-width\',
          \'border-bottom\',
          \'border-bottom-color\',
          \'border-bottom-style\',
          \'border-bottom-width\',
          \'border-left\',
          \'border-left-color\',
          \'border-left-style\',
          \'border-left-width\',
          \'border-radius\',
          \'border-top-left-radius\',
          \'border-top-right-radius\',
          \'border-bottom-right-radius\',
          \'border-bottom-left-radius\',
          \'border-image\',
          \'border-image-source\',
          \'border-image-slice\',
          \'border-image-width\',
          \'border-image-outset\',
          \'border-image-repeat\',
          \'outline\',
          \'outline-width\',
          \'outline-style\',
          \'outline-color\',
          \'outline-offset\',
          \'box-shadow\',
          \'mix-blend-mode\',
          \'filter:progid:DXImageTransform.Microsoft.Alpha(Opacity\',
          "-ms-filter:\\\'progid:DXImageTransform.Microsoft.Alpha",
          \'opacity\',
          \'-ms-interpolation-mode\',
        ],
      },
      {
        // SVG Presentation Attributes.
        properties: [
          \'alignment-baseline\',
          \'baseline-shift\',
          \'dominant-baseline\',
          \'text-anchor\',
          \'word-spacing\',
          \'writing-mode\',

          \'fill\',
          \'fill-opacity\',
          \'fill-rule\',
          \'stroke\',
          \'stroke-dasharray\',
          \'stroke-dashoffset\',
          \'stroke-linecap\',
          \'stroke-linejoin\',
          \'stroke-miterlimit\',
          \'stroke-opacity\',
          \'stroke-width\',

          \'color-interpolation\',
          \'color-interpolation-filters\',
          \'color-profile\',
          \'color-rendering\',
          \'flood-color\',
          \'flood-opacity\',
          \'image-rendering\',
          \'lighting-color\',
          \'marker-start\',
          \'marker-mid\',
          \'marker-end\',
          \'mask\',
          \'shape-rendering\',
          \'stop-color\',
          \'stop-opacity\',
        ],
      },
      {
        // Transitions & Animation.
        properties: [
          \'transition\',
          \'transition-delay\',
          \'transition-timing-function\',
          \'transition-duration\',
          \'transition-property\',
          \'transform\',
          \'transform-origin\',
          \'animation\',
          \'animation-name\',
          \'animation-duration\',
          \'animation-play-state\',
          \'animation-timing-function\',
          \'animation-delay\',
          \'animation-iteration-count\',
          \'animation-direction\',
        ],
      },
    ],
  },
};

编译时自动修复

// ...
const { defineConfig } = require(\'@vue/cli-service\');
const StylelintPlugin = require(\'stylelint-webpack-plugin\');

module.exports = defineConfig({
  // ...
  configureWebpack: {
    plugins: [
      // ...
      new StylelintPlugin({
        files: [\'src/**/*.vue\'],
        // 编译时自动修复
        fix: true,
        // 这样配才能修复vue文件style片段中的less语法
        customSyntax: \'postcss-html\',
      }),
    ],
  },
});

安装VSCode stylelint扩展

在.vscode/settings.json中配置保存代码时自动修复样式错误

{
  "editor.codeActionsOnSave": {
    // ...
    "source.fixAll.stylelint": true
  },
  // 关闭vscode自带的css,less,scss报错提示
  "css.validate": false,
  "less.validate": false,
  "scss.validate": false,
  "stylelint.validate": ["css", "less"]
}

9 配置yapi-to-typescript

typescript面世以来,经历了一个从厌烦到喜欢的过程,厌恶的是要在原来开发的基础上,多做一些繁杂数据类型定义的工作,喜欢的是确实帮助开发者发现了许多语法错误,享受到接口和传参的便利提示, 综合起来,还是利大于弊,所以 越来越多的开发者开始加入ts大家庭。yapi-to-typescript这个工具,可以帮我们省去接口数据类型定义的工作量。让我们更愉快的与ts玩耍。

安装依赖

yarn add -D yapi-to-typescript

创建ytt.config.ts配置文件

import { defineConfig } from \'yapi-to-typescript\';

/**
 * 生成Api接口名称  Interface和ChangeCase数据类型参见node_modules\yapi-to-typescript\lib\esm\index.d.ts定义
 * @param interfaceInfo : Interface
 * @param changeCase:ChangeCase
 * @returns 请求响应接口名称--pascal命名
 */
function genApiInterfaceName(interfaceInfo, changeCase) {
  // 取解析路径dir最尾部的路径作为前缀路径
  const lastPath = interfaceInfo.parsedPath.dir.split(\'/\').pop();
  // 拼接前缀路径+文件名称
  return `${changeCase.pascalCase(lastPath)}${changeCase.pascalCase(interfaceInfo.parsedPath.name)}`;
}

export default defineConfig([
  {
    serverUrl: \'https://yapi.xxx.com\',
    typesOnly: true,
    target: \'typescript\',
    reactHooks: {
      enabled: false,
    },
    prodEnvName: \'项目名称\',
    // 将生成文件路径转化成小驼峰命名方式
    outputFilePath: (interfaceInfo, changeCase) => {
      // 文件夹名称取api-url路径末尾2个
      const filePathArr = interfaceInfo.path.split(\'/\').slice(-2);
      const filePath = filePathArr.map((item) => changeCase.camelCase(item)).join(\'/\');
      return `typings/httpTypes/${filePath}.ts`;
    },
    // 生成ts文件中请求参数interface名称,将下划线命名转换成pascal命名
    getRequestDataTypeName: (interfaceInfo, changeCase) => {
      return `${genApiInterfaceName(interfaceInfo, changeCase)}Request`;
    },
    // 生成ts文件中请求响应数据interface名称,将下划线命名转换成pascal命名
    getResponseDataTypeName: (interfaceInfo, changeCase) => {
      return `${genApiInterfaceName(interfaceInfo, changeCase)}Response`;
    },
    // 响应数据中要生成ts数据类型的键名
    dataKey: \'retdata\',
    projects: [
      {
        // token获取方式: 在yapi-设置-token配置中查看
        token: \'xxx\',
        // 分类id查找方式: 点击接口左侧的分类菜单,查看url地址栏最后面的数字获取
        // 分类id配置特别重要,配置错了无法生成对应的ts数据类型定义文件
        categories: [
          {
            id: [xxx], // 民生立减金API分类id
          },
        ],
      },
    ],
  },
]);

3.在package.json中配置ytt指令

{
  // ...
  "scripts": {
    "start": "ytt && vue-cli-service serve --mode local",
    "build": "ytt && vue-cli-service build --mode local",
    "ytt": "ytt",
  },
}

4.在.gitignore中添加对httpTypes下文件的忽略

# ...
/typings/httpTypes/*
# ...

10 配置commitlint

commit message 是程序员开发的日常高频操作,自然状态下commit message 呈现五花八门的书写风格,不利于阅读和维护,规范的 commit message 有助于团队做 code review, 输出清晰明了的 CHANGELOG, 有利于项目的长期维护。

安装依赖包

yarn add -D husky conventional-changelog-cli @commitlint/{cli,config-conventional}

创建提交校验配置文件commitlint.config.js

/**
 *  git commit最佳实践
 *  1.经常commit,One Thing,One Commit
 *  2.commit之前测试,不要commit一半工作;
 *  3.编写规范的commit message
 */

/**
 * Commit message 包括三个部分:Header,Body 和 Footer
 * <type>(<scope>): <subject>
 * 空一行
 * <body>
 * 空一行
 * <footer>
 */
module.exports = {
  extends: [\'@commitlint/config-conventional\'],
  rules: {
    // Header包括三个字段:type(必需)、scope(可选)和subject(必需)。最多200字
    \'header-max-length\': [2, \'always\', 200],
    // 提交类型<type>枚举
    \'type-enum\': [
      2,
      \'always\',
      [
        \'init\', // 项目初始化
        \'clean\', // 清理过时无用文件
        \'merge\', // 合并代码
        \'style\', //  修改样式文件(包括css/less/sass,图片,字体文件)
        \'format\', // 格式化,不影响代码含义的修改,比如空格、格式缩进、缺失的分号等
        \'build\', // 改变构建流程、或者增加依赖库、工具等 如webpack.config.js,package.json yarn.lock
        \'chore\', // 各种配置文件的修改,  如.gitignore,tsconfig.json,.vscode,.tenone, eslint/stylelint,envConfig
        \'ci\', // 对CI配置文件或脚本进行了修改
        \'docs\', // 修改项目说明文档
        \'feat\', // 新增功能
        \'fix\', // 修复bug
        \'perf\', // 性能优化
        \'refactor\', // 既不是修复bug也不是添加功能的代码重构
        \'revert\', // 版本回退
        \'test\', // 修改测试用例
      ],
    ],
    // 格式-可选值
    // \'lower-case\' 小写 lowercase
    // \'upper-case\' 大写 UPPERCASE
    // \'camel-case\' 小驼峰 camelCase
    // \'kebab-case\' 短横线 kebab-case
    // \'pascal-case\' 大驼峰 PascalCase
    // \'sentence-case\' 首字母大写 Sentence case
    // \'snake-case\' 下划线 snake_case
    // \'start-case\' 所有首字母大写 start-case
    // <type> 不能为空
    \'type-empty\': [2, \'never\'],
    // <type> 格式 小写
    \'type-case\': [2, \'always\', \'lower-case\'],
    // <scope> 关闭改动范围不能为空规则
    \'scope-empty\': [0, \'never\'],
    // <scope> 格式 小写
    \'scope-case\': [2, \'always\', \'lower-case\'],
    // <subject> 不能为空
    \'subject-empty\': [2, \'never\'],
    // <subject> 关闭 以.为结束标志
    \'subject-full-stop\': [0, \'never\', \'.\'],
    // <subject> 格式
    \'subject-case\': [2, \'never\', []],
    // <body> 以空行开头
    \'body-leading-blank\': [1, \'always\'],
    // <footer> 以空行开头
    \'footer-leading-blank\': [1, \'always\'],
  },
};

3. 创建提交校验shell脚本 husky.sh和commit-msg

yarn husky install

在.husky文件夹下创建commit-msg文件

npx husky add .husky/commit-msg

在.husky/commit-msg文件中写入

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# 提交记录检查
yarn commitlint --edit $1
# 代码重复率检测
yarn jscpd
# 格式化检查
yarn format:check
# eslint检查
yarn lint:check

11 配置代码重复率检测工具jscpd

代码的简洁之道有一条铁律是 Don\'t Repeat Yourself,那么如何快速地检测出项目出是否存在着大段的重复代码,靠人工检查显然不可取,这种重复体力活应该交给工具。

检测前端代码重复率的工具有jsinspectjscpdPMD-CPD(PMD\'s Copy/Paste Detector)

  • jsinspect工具支持js和jsx格式的文件,基于抽象语法树,可以检测出结构类似的代码块
  • PMD-CPD工具支持js文件检测,也可以自己开发扩展包来解析指定的语言
  • jscpd工具支持文件格式广泛,如js、jsx、vue、ts、less,java、oc等。其重复率判定依据为一定长度标识符的MD5值是否相同

每个工具各有其优缺点,若只需要检测js或jsx文件,且对检测结果要求较高,可以选择jsinspect或者PMD-CPD工具,若考虑检测工具的通用性,可以选择jscpd工具。

安装:
npm install -g jscpd
用法:
jscpd  src/*
在项目下创建.jscpd.json配置文件
{ 
// 重复率阈值
"threshold": 0.1,
// 报告输出格式
"reporters": [ "html", "console" ], "ignore": [ "dist/**", "node_modules/**" ],
// 文件路径使用相对路径
"absolute": false }
 --min-tokens  -k:代码的最小块大小。小于的代码块min-tokens将被跳过,默认为50;
 --min-lines  -l:最小代码行数,默认为5;
 --max-lines  -x: 最大代码行数,默认为1000;
 --max-size  -z:最大文件大小,单位为kb,默认100;
 --threshold  -t:重复级别的阈值,当项目重复级别大于该阈值时报错退出,默认为空;
 --ignore  -i:忽略的文件类型;
 --reporters  -r:输出类型


 

参考文献

 

 

 

 

分类:

技术点:

相关文章: