你在尋找Vue3移動端專案框架嘛?請看這裡

孤舟蓑翁發表於2022-01-04

現在web開發變得更加美妙高效,在於開發工具設計得更好了,豐富性與易用性,都有所提高。豐富性帶來了一個幸福的煩惱,就是針對實際應用場景,如何選擇工具 ?

1. Vue Cli和Vite之間的選擇

Vite的開發環境體驗好,基於瀏覽器原生ES6 Modules提供的功能,不對ES高版本語法進行轉譯,省略掉耗時的打包流程, 可是考慮到:

1) 專案要用到真機除錯功能,開發環境下除錯程式碼時不能使用ES高版本的語法,用著不順暢。

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專案的腳手架。

安裝最新版本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發現檔案中有許多紅色的告警波浪線,安裝Vetur擴充套件,絕大多數語法報錯消失。

3 配置vue檔案儲存時自動格式化

程式碼美化功能,是一個重要的影響開發體驗的指標。書寫潦草的程式碼,按下Ctrl+S儲存之後,瞬間變成整潔有序, 這種視覺感受,讓人神清氣爽。

在專案下新建.vscode/settings.json,內容如下:

{
  // ...
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "[vue]": {
    "editor.defaultFormatter": "octref.vetur"
  },
  "[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:輸出型別


 

參考文獻

 

 

 

 

相關文章