日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

 

從零開始配置 TypeScript 項(xiàng)目的教程(二)

 

作者:子弈

轉(zhuǎn)發(fā)鏈接:https://juejin.im/post/6856410900577026061

目錄

從零開始配置 TypeScript 項(xiàng)目的教程(一)

從零開始配置 TypeScript 項(xiàng)目的教程(二) 本篇

Jest 確保構(gòu)建

單獨(dú)通過執(zhí)行Unpm run test 命令進(jìn)行單元測(cè)試,這里演示執(zhí)行構(gòu)建命令時(shí)的單元測(cè)試(需要保證構(gòu)建之前所有的單元測(cè)試用例都能通過)。如果測(cè)試失敗,那么應(yīng)該防止繼續(xù)構(gòu)建,例如進(jìn)行失敗的構(gòu)建行為:

PS C:CodeGitalgorithms> npm run build

> [email protected] build C:CodeGitalgorithms
> npm run lint-strict && npm run jest && rimraf dist types && gulp


> [email protected] lint-strict C:CodeGitalgorithms
> eslint src --max-warnings 0


> [email protected] jest C:CodeGitalgorithms
> jest --coverage

 PASS  dist/test/greet.spec.js
 FAIL  test/greet.spec.ts
  ● src/greet.ts › name param test

    expect(received).toBe(expected) // Object.is equality

    Expected: "Hello from world 1"
    Received: "Hello from world"

      3 | describe("src/greet.ts", () => {
      4 |   it("name param test", () => {
    > 5 |     expect(greet("world")).toBe("Hello from world 1");
        |                            ^
      6 |   });
      7 | });
      8 |

      at Object.<anonymous> (test/greet.spec.ts:5:28)

----------|---------|----------|---------|---------|-------------------
| File       | % Stmts   | % Branch   | % Funcs   | % Lines   | Uncovered Line #s   |
| ---------- | --------- | ---------- | --------- | --------- | ------------------- |
| All files  | 100       | 100        | 100       | 100       |
| greet.ts   | 100       | 100        | 100       | 100       |
| ---------- | --------- | ---------- | --------- | --------- | ------------------- |
Test Suites: 1 failed, 1 passed, 2 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total
Time:        3.45 s
Ran all test suites.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] jest: `jest --coverage`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] jest script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:Users子弈AppDataRoamingnpm-cache_logs2020-07-12T13_42_11_628Z-debug.log
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] build: `npm run lint-strict && npm run jest && rimraf dist types && gulp`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:Users子弈AppDataRoamingnpm-cache_logs2020-07-12T13_42_11_673Z-debug.log

需要注意由于是并行(&&)執(zhí)行腳本,因此執(zhí)行構(gòu)建命令時(shí)(npm run build)會(huì)先執(zhí)行 ESLint 校驗(yàn),如果 ESLint 校驗(yàn)失敗那么退出構(gòu)建,否則繼續(xù)進(jìn)行 Jest 單元測(cè)試。如果單元測(cè)試失敗那么退出構(gòu)建,只有當(dāng)兩者都通過時(shí)才會(huì)進(jìn)行源碼構(gòu)建。

Jest 確保代碼上傳

除了預(yù)防不負(fù)責(zé)任的代碼構(gòu)建以外,還需要預(yù)防不負(fù)責(zé)任的代碼提交。配合 lint-staged 可以防止未跑通單元測(cè)試的代碼進(jìn)行遠(yuǎn)程提交:

"scripts": {
  "lint": "eslint src --max-warnings 0",
  "test": "jest --bail --coverage",
},
"lint-staged": {
  "*.ts": [
    "npm run lint",
    "npm run test"
  ]
}

此時(shí)如果單元測(cè)試有誤,都會(huì)停止代碼提交:

husky > pre-commit (node v12.13.1)
[STARTED] Preparing...
[SUCCESS] Preparing...
[STARTED] Running tasks...
[STARTED] Running tasks for *.ts
[STARTED] npm run lint
[SUCCESS] npm run lint
[STARTED] npm run jest
[FAILED] npm run jest [FAILED]
[FAILED] npm run jest [FAILED]
[SUCCESS] Running tasks...
[STARTED] Applying modifications...
[SKIPPED] Skipped because of errors from tasks.
[STARTED] Reverting to original state because of errors...
[SUCCESS] Reverting to original state because of errors...
[STARTED] Cleaning up...
[SUCCESS] Cleaning up...

× npm run jest:
FAIL test/greet.spec.ts
  src/greet.ts
    × name param test (4 ms)

  ● src/greet.ts › name param test

    expect(received).toBe(expected) // Object.is equality

    Expected: "Hello from world 1"
    Received: "Hello from world"

      3 | describe("src/greet.ts", () => {
      4 |   it("name param test", () => {
    > 5 |     expect(greet("world")).toBe("Hello from world 1");
        |                            ^
      6 |   });
      7 | });
      8 |

      at Object.<anonymous> (test/greet.spec.ts:5:28)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        1.339 s, estimated 3 s
Ran all test suites related to files matching /C:\Code\Git\algorithms\src\index.ts|C:\Code\Git\algorithms\test\greet.spec.ts/i.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] jest: `jest --bail --findRelatedTests --coverage "C:/Code/Git/algorithms/src/index.ts" "C:/Code/Git/algorithms/test/greet.spec.ts"`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] jest script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:Users子弈AppDataRoamingnpm-cache_logs2020-07-12T14_33_51_183Z-debug.log

> [email protected] jest C:CodeGitalgorithms
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] jest script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:Users子弈AppDataRoamingnpm-cache_logs2020-07-12T14_33_51_183Z-debug.log

> [email protected] jest C:CodeGitalgorithms
> jest --bail --findRelatedTests --coverage "C:/Code/Git/algorithms/src/index.ts" "C:/Code/Git/algorithms/test/greet.spec.ts"

----------|---------|----------|---------|---------|-------------------
| File       | % Stmts   | % Branch   | % Funcs   | % Lines   | Uncovered Line #s   |
| ---------- | --------- | ---------- | --------- | --------- | ------------------- |
| All files  | 0         | 0          | 0         | 0         |
| ---------- | --------- | ---------- | --------- | --------- | ------------------- |
husky > pre-commit hook failed (add --no-verify to bypass)
git exited with error code 1

溫馨提示:想要了解更多關(guān)于 Jest 的生態(tài)可以查看 awesome-jest[113]

Jest 對(duì)于 ESLint 支持

src 目錄下的源碼通過配置 @typescript-eslint/eslint-plugin 可進(jìn)行推薦規(guī)則的 ESLint 校驗(yàn),為了使得 test 目錄下的測(cè)試代碼能夠進(jìn)行復(fù)合 Jest 推薦規(guī)則的 ESLint 校驗(yàn),可以通過配置 eslint-plugin-jest[114] 進(jìn)行支持(ts-jest 項(xiàng)目就是采用了該插件進(jìn)行 ESLint 校驗(yàn),具體可查看配置文件 `ts-jest/.eslintrc.js`[115]),這里仍然采用推薦的規(guī)則配置:

module.exports = {
  root: true,
  parser: "@typescript-eslint/parser",
  plugins: ["@typescript-eslint"],
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    // 新增推薦的 ESLint 校驗(yàn)規(guī)則
    // 所有規(guī)則集查看:https://github.com/jest-community/eslint-plugin-jest#rules(recommended 標(biāo)識(shí)表明是推薦規(guī)則)
    "plugin:jest/recommended",
  ],
};

為了驗(yàn)證推薦規(guī)則是否生效,這里可以找一個(gè) `no-identical-title`[116] 規(guī)則進(jìn)行驗(yàn)證:

import greet from "@/greet";
describe("src/greet.ts", () => {
  it("name param test", () => {
    expect(greet("world")).toBe("Hello from world 1");
  });
});

// 這里輸入了重復(fù)的 title
describe("src/greet.ts", () => {
  it("name param test", () => {
    expect(greet("world")).toBe("Hello from world 1");
  });
});

需要注意修改 package.json 中的 ESLint 校驗(yàn)范圍:

"scripts": {
  // 這里對(duì) src 和 test 目錄進(jìn)行 ESLint 校驗(yàn)
  "lint": "eslint src test --max-warnings 0",
},

執(zhí)行 npm run lint 進(jìn)行單元測(cè)試的格式校驗(yàn):

PS C:CodeGitalgorithms> npm run lint

> [email protected] lint C:CodeGitalgorithms
> eslint src test --max-warnings 0


C:CodeGitalgorithmstestgreet.spec.ts
  9:10  error  Describe block title is used multiple times in the same describe block  jest/no-identical-title

? 1 problem (1 error, 0 warnings)

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] lint: `eslint src test --max-warnings 0`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] lint script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:Users子弈AppDataRoamingnpm-cache_logs2020-07-13T02_25_12_043Z-debug.log

此時(shí)會(huì)發(fā)現(xiàn) ESLint 拋出了相應(yīng)的錯(cuò)誤信息。需要注意采用此 ESLint 校驗(yàn)之后也會(huì)在 VS Code 中實(shí)時(shí)生成錯(cuò)誤提示(相應(yīng)的代碼下會(huì)有紅色波浪線,鼠標(biāo)移入后會(huì)產(chǎn)生 Tooltip 提示該錯(cuò)誤的相應(yīng)規(guī)則信息,除此之外當(dāng)前工程目錄下對(duì)應(yīng)的文件名也會(huì)變成紅色),此后的 Git 提交以及 Build 構(gòu)建都會(huì)失敗

溫馨提示:如果你希望 Jest 測(cè)試的代碼需要一些格式規(guī)范,那么可以查看 eslint-plugin-jest-formatting[117] 插件。

Npm Script Hook

當(dāng)你查看前端開源項(xiàng)目時(shí)第一時(shí)間可能會(huì)找 package.json 中的 main、bin 以及files 等字段信息,除此之外如果還想深入了解項(xiàng)目的結(jié)構(gòu),可能還會(huì)查看scripts 腳本字段信息用于了解項(xiàng)目的開發(fā)、構(gòu)建、測(cè)試以及安裝等流程。npm 的腳本功能非常強(qiáng)大,你可以利用腳本制作項(xiàng)目需要的任何流程工具。本文不會(huì)過多介紹 npm 腳本的功能,只是講解一下其中用到的 鉤子[118] 功能。

目前在本項(xiàng)目中使用的一些腳本命令如下(就目前而言腳本相對(duì)較少,定義還蠻清晰的):

"lint": "eslint src test --max-warnings 0",
"test": "jest --bail --coverage",
"build": "npm run lint && npm run prettier && npm run test && rimraf dist types && gulp",
"changelog": "rimraf CHANGELOG.md && conventional-changelog -p angular -i CHANGELOG.md -s"

重點(diǎn)看下 build 腳本命令,會(huì)發(fā)現(xiàn)這個(gè)腳本命令包含了大量的繼發(fā)執(zhí)行腳本,但真正和 build 相關(guān)的只有 rimraf dist types && gulp 這兩個(gè)腳本。這里通過 npm 的腳本鉤子 pre 和 post 將腳本的功能區(qū)分開,從而使腳本的語義更加清晰(當(dāng)然腳本越來越多的時(shí)候也可能容易增加開發(fā)者的認(rèn)知負(fù)擔(dān))。npm 除了指定一些特殊的腳本鉤子以外(例如prepublish、postpublish、preinstall、postinstall等),還可以對(duì)任意腳本增加 pre 和 post 鉤子,這里通過自定義鉤子將并發(fā)執(zhí)行的腳本進(jìn)行簡(jiǎn)化:

"lint": "eslint src test --max-warnings 0",
"test": "jest --bail --coverage",
"prebuild": "npm run lint && npm run prettier && npm run test",
"build": "rimraf dist types && gulp",
"changelog": "rimraf CHANGELOG.md && conventional-changelog -p angular -i CHANGELOG.md -s"

此時(shí)如果執(zhí)行 npm run build 命令時(shí)事實(shí)上類似于執(zhí)行了以下命令:

npm run prebuild && npm run build

之后設(shè)計(jì)的腳本如果繼發(fā)執(zhí)行繁多,那么都會(huì)采用 npm scripts hook 進(jìn)行設(shè)計(jì)。

溫馨提示:大家可能會(huì)奇怪什么地方需要類似于 preinstall 或 preuninstall 這樣的鉤子,例如查看 husky - package.json[119],husky 在安裝的時(shí)候因?yàn)橐踩?Git Hook 腳本從而帶來了一些副作用(此時(shí)當(dāng)然可以通過 preinstall觸發(fā) Git Hook 腳本植入的邏輯)。如果不想使用 husky,那么卸載后需要清除植入的腳本從而不妨礙原有的 Git Hook 功能。當(dāng)然如果想要了解更多關(guān)于 npm 腳本的信息,可以查看 npm-scripts[120] 或 npm scripts 使用指南[121]

Vuepress

Vuepress 背景

一般組件庫或工具庫都需要設(shè)計(jì)一個(gè)演示文檔(提供良好的開發(fā)體驗(yàn))。一般的工具庫可以采用 tsdoc[122]jsdoc[123] 或 esdoc[124] 等工具進(jìn)行 API 文檔的自動(dòng)生成,但往往需要符合一些注釋規(guī)范,這些注釋規(guī)范在某種程度上可能會(huì)帶來開發(fā)負(fù)擔(dān),當(dāng)然也可以交給 VS Code 的插件進(jìn)行一鍵生成,例如 Document This For jsdoc[125] 或 TSDoc Comment[126]

組件庫 Element UI 采用 vue-markdown-loader[127](Convert Markdown file to Vue Component using markdown-it) 進(jìn)行組件的 Demo 演示設(shè)計(jì),但是配置相對(duì)復(fù)雜。更簡(jiǎn)單的方式是配合 Vuepress[128] 進(jìn)行設(shè)計(jì),它的功能非常強(qiáng)大,但前提是熟悉 Vue,因?yàn)榭梢栽?Markdown 中使用 Vue 語法。當(dāng)然如果是 React 組件庫的 Demo 演示,則可以采用 dumi[129] 生成組件 Demo 演示文檔(不知道沒有更加好用的類 Vuepress 的 React 組件文檔生成器, 更多和 React 文檔相關(guān)也可以了解 react-markdown[130]react-static[131] 等)。

由于之前采用過 Vuepress 設(shè)計(jì) Vue 組件庫的 Demo 演示文檔,因此這里仍然沿用它來設(shè)計(jì)工具庫包的 API 文檔(如果你想自動(dòng)生成 API 文檔,也可以額外配合 tsdoc 工具)。采用 Vuepress 設(shè)計(jì)文檔的主要特點(diǎn)如下:

  • 可以在 Markdown 中直接使用 Vue(還可以自定義 Vue 文檔視圖組件)
  • 內(nèi)置了很多 Markdown 拓展
  • 可以使用 Webpack 進(jìn)行構(gòu)建定制化配置
  • 默認(rèn)主題支持搜索能力
  • 可以安裝 Vuepress 插件(后續(xù)需要支持的 Latex[132] 排版就可以利用現(xiàn)有的插件能力生成)
  • 默認(rèn)響應(yīng)式

Vuepress 配置

先按照官方的 快速上手[133] 文檔進(jìn)行依賴安裝和 npm scripts 腳本設(shè)置:

"scripts": {
  "docs:dev": "vuepress dev docs",
  "docs:build": "vuepress build docs"
}

按照 Vuepress 官網(wǎng)約定優(yōu)于配置的原則進(jìn)行演示文檔的目錄結(jié)構(gòu)[134]設(shè)計(jì),官方的文檔可能一下子難以理解,可以先設(shè)計(jì)一個(gè)最簡(jiǎn)單的目錄:

.
├── docs
│   ├── .vuepress
│   │   └── config.js       # 配置文件
│   └── README.md           # 文檔首頁
└── package.json

根據(jù)默認(rèn)主題 / 首頁[135]在 docs/README.md 進(jìn)行首頁設(shè)計(jì):

---
home: true
# heroImage: /hero.png
heroText: algorithms-utils
tagline: 算法與 TypeScript 實(shí)現(xiàn)
actionText: 開始學(xué)習(xí)
actionLink: /guide/
features:
  - title: 精簡(jiǎn)理論
    details: 精簡(jiǎn)《算法導(dǎo)論》的內(nèi)容,幫助自己更容易學(xué)習(xí)算法理論知識(shí)。
  - title: 習(xí)題練習(xí)
    details: 解答《算法導(dǎo)論》的習(xí)題,幫助自己更好的實(shí)踐算法理論知識(shí)。
  - title: 面題精選
    details: 搜集常見的面試題目,提升自己的算法編程能力以及面試通過率。
footer: MIT Licensed | Copyright © 2020-present 子弈
---

根據(jù)配置[136] 對(duì) docs/.vuepress/config.js 文件進(jìn)行基本配置:

const packageJson = require("../../package.json");

module.exports = {
  // 配置網(wǎng)站標(biāo)題
  title: packageJson.name,
  // 配置網(wǎng)站描述
  description: packageJson.description,
  // 配置基本路徑
  base: "/algorithms/",
  // 配置基本端口
  port: "8080",
};

此時(shí)通過 npm run docs:dev 進(jìn)行開發(fā)態(tài)文檔預(yù)覽:

PS C:CodeGitalgorithms> npm run docs:dev

> [email protected] docs:dev C:CodeGitalgorithms
> vuepress dev docs

wait Extracting site metadata...
tip Apply theme @vuepress/theme-default ...
tip Apply plugin container (i.e. "vuepress-plugin-container") ...
tip Apply plugin @vuepress/register-components (i.e. "@vuepress/plugin-register-components") ...
tip Apply plugin @vuepress/active-header-links (i.e. "@vuepress/plugin-active-header-links") ...
tip Apply plugin @vuepress/search (i.e. "@vuepress/plugin-search") ...
tip Apply plugin @vuepress/nprogress (i.e. "@vuepress/plugin-nprogress") ...

√ Client
  Compiled successfully in 5.31s

i ?wds?: Project is running at http://0.0.0.0:8080/
i ?wds?: webpack output is served from /algorithms-utils/
i ?wds?: Content not from webpack is served from C:CodeGitalgorithmsdocs.vuepresspublic
i ?wds?: 404s will fallback to /index.html
success [23:13:14] Build 10b15a finished in 5311 ms!
> VuePress dev server listening at http://localhost:8080/algorithms-utils/

效果如下:

從零開始配置 TypeScript 項(xiàng)目的教程(二)

 

當(dāng)然除了以上設(shè)計(jì)的首頁,在本項(xiàng)目中還會(huì)設(shè)計(jì)導(dǎo)航欄[137]側(cè)邊欄[138]、使用插件[139]使用組件[140]等。這里重點(diǎn)講解一下 Webpack 構(gòu)建[141] 配置。

為了在 Markdown 文檔中可以使用 src 目錄的 TypeScript 代碼,這里對(duì) .vuepress/config.js 文件進(jìn)行配置處理:

const packageJson = require("../../package.json");
const sidebar = require("./config/sidebar.js");
const nav = require("./config/nav.js");
const path = require("path");

module.exports = {
  title: packageJson.name,
  description: packageJson.description,
  base: "/algorithms/",
  port: "8080",

  themeConfig: {
    nav,
    sidebar,
  },

  plugins: [
    "vuepress-plugin-cat",
    [
      "mathjax",
      {
        target: "svg",
        macros: {
          "*": "\times",
        },
      },
    ],
    // 增加 Markdown 文檔對(duì)于 TypeScript 語法的支持
    [
      "vuepress-plugin-typescript",
      {
        tsLoaderOptions: {
          // ts-loader 的所有配置項(xiàng)
        },
      },
    ],
  ],

  chainWebpack: (config) => {
    config.resolve.alias.set("image", path.resolve(__dirname, "public"));

    // 在文檔中模擬庫包的引入方式
    // 例如發(fā)布了 algorithms-utils 庫包之后,
    // import greet from 'algorithms-utils/greet.ts' 在 Vuepress 演示文檔中等同于
    // import greet from '~/src/greet.ts',
    // 其中 ~ 在這里只是表示項(xiàng)目根目錄
    config.resolve.alias.set(
      "algorithms-utils",
      path.resolve(__dirname, "../../src")
    );
  },
};

溫馨提示:這里的 Webpack 配置采用了 webpack-chain[142] 鏈?zhǔn)讲僮鳎绻胍捎?Webpack 對(duì)象的配置方式則可以查看 Vuepress - 構(gòu)建流程 - configurewebpack[143]

此時(shí)可以在 Vuepress 的 Markdown 文檔中進(jìn)行 TypeScript 引入的演示文檔設(shè)計(jì):

# Test vuepress

::: danger 測(cè)試 Vuepress
引入 greet.ts 并進(jìn)行調(diào)用測(cè)試。
:::

<template>
  <collapse title="查看答案">{{msg}}</collapse>
</template>

<template>
  <div>{{msg}}</div>
</template>

<script lang="ts">
  import greet from 'algorithms-utils/greet'
  const msg = greet('ziyi')
  export default {
    data() {
      return {
         msg
      }
    },
  }
</script>

啟動(dòng) Vuepress 查看演示文檔:

從零開始配置 TypeScript 項(xiàng)目的教程(二)

 

可以發(fā)現(xiàn)在 Markdown 中引入的 src/greet.ts 代碼生效了,最終通過 npm run docs:build 可以生成演示文檔的靜態(tài)資源進(jìn)行部署和訪問。

溫馨提示:更多本項(xiàng)目的 Vuepress 配置信息可查看 Commit 信息,除此之外如果還想知道更多 Vuepress 的生態(tài),例如有哪些有趣插件或主題,可查看 awesome-vuepress[144] 或 Vuepress 社區(qū)[145]

文檔工具和規(guī)范

通常在書寫文檔的時(shí)候很多同學(xué)都不注重文檔的潔癖,其實(shí)書寫文檔和書寫代碼一樣需要一些格式規(guī)范。markdownlint[146] 是類似于 ESLint 的 Markdown 格式校驗(yàn)工具,通過它可以更好的規(guī)范我們書寫的文檔。當(dāng)然 Markdown 的格式校驗(yàn)不需要像 ESLint 或者 Prettier 校驗(yàn)?zāi)菢舆M(jìn)行強(qiáng)約束,簡(jiǎn)單的能夠做到提示和 Save Auto Fix 即可。

通過安裝 Vs Code 插件 markdownlint[147] 并進(jìn)行 Save Auto Fix 配置(在插件中明確列出了哪些規(guī)則是可以被 Fix 的)。安裝完成后查看剛剛進(jìn)行的測(cè)試文件:

從零開始配置 TypeScript 項(xiàng)目的教程(二)

 

此時(shí)會(huì)發(fā)現(xiàn)插件生效了,但是在 Markdown 中插入 html 是必須的一個(gè)能力(Vuepress 支持的能力就是在 Markdown 中使用 Vue),因此可以通過 .markdownlintrc 文件將相應(yīng)的規(guī)則屏蔽掉。

溫馨提示:如果你希望在代碼提交之前或文檔構(gòu)建之前能夠進(jìn)行 Markdown 格式校驗(yàn),則可以嘗試它的命令行接口 markdownlint-cli[148]。除此之外,如果對(duì)文檔的設(shè)計(jì)沒有想法或者不清楚如何書寫好的技術(shù)文檔,可以查看 技術(shù)文章的寫作技巧分享[149],一定能讓你有所收獲。

Github Actions

CI / CD 背景

前提提示:個(gè)人對(duì)于 CI / CD 可能相對(duì)不夠熟悉,只是簡(jiǎn)單的玩過 Travis、Gitlab CI / CD 以及 Jenkins。

關(guān)于 CI / CD 的背景這里就不再過多介紹,有興趣的同學(xué)可以看看以下一些好文:

  • Introduction to CI/CD with GitLab(中文版)[150]
  • GitHub Actions 入門教程[151]
  • Github Actions 官方文檔[152]
  • 當(dāng)我有服務(wù)器時(shí)我做了什么 · 個(gè)人服務(wù)器運(yùn)維指南[153](這個(gè)系列有點(diǎn)佩服啊)

在 Introduction to CI/CD with GitLab(中文版)[154] 中你可以清晰的了解到 CI 和 CD 的職責(zé)功能:

從零開始配置 TypeScript 項(xiàng)目的教程(二)

 

通過以下圖可以更清晰的發(fā)現(xiàn) Gitlab 在每個(gè)階段可用的功能:

從零開始配置 TypeScript 項(xiàng)目的教程(二)

 

由于本項(xiàng)目依賴 Github,因此沒法使用 Gitlab 默認(rèn)集成的能力。之前的 Github 項(xiàng)目采用了 Travis 進(jìn)行項(xiàng)目的 CI / CD 集成,現(xiàn)在因?yàn)橛辛烁奖愕?Github Actions,因此決定采用 Github 自帶的 Actions 進(jìn)行 CI / CD 能力集成(大家如果想更多了解這些 CI / CD 的差異請(qǐng)自行 google 哈)。Github Actions 所帶來的好處在于:

  • 可復(fù)用的 Actions(以前你需要寫復(fù)雜的腳本,現(xiàn)在可以復(fù)用別人寫好的腳本,可以簡(jiǎn)單理解為 CI 腳本插件化)
  • 支持更多的 webhook[155],這些當(dāng)然是 Github 生態(tài)特有的競(jìng)爭(zhēng)力

當(dāng)然也會(huì)產(chǎn)生一些限制[156],這些限制主要是和執(zhí)行時(shí)間以及次數(shù)相關(guān)。需要注意類似于 Jenkins 等支持本地連接運(yùn)行,Github Actions 也支持連接到本地機(jī)器運(yùn)行 workflow,因此部分限制可能不受本地運(yùn)行的限制。

溫馨提示:本項(xiàng)目中使用到的 CI / CD 功能相對(duì)簡(jiǎn)單,如果想了解更多通用的 Actions,可查看 官方 Actions[157] 和 awesome-actions[158]。最近在使用 Jenkins 做前端的自動(dòng)化構(gòu)建優(yōu)化,后續(xù)可能會(huì)出一篇簡(jiǎn)單的教程文章(當(dāng)然會(huì)跟普通講解的用法會(huì)有所不同嘍)。

Github Actions 配置

本項(xiàng)目的配置可能會(huì)包含以下三個(gè)方面:

  • 自動(dòng)更新靜態(tài)資源流程
  • 發(fā)布庫包流程
  • 提交 Pull Request 流程

這里主要講解自動(dòng)更新靜態(tài)資源流程,大致需要分為以下幾個(gè)步驟(以下都是在 Github 服務(wù)器上進(jìn)行操作,你可以理解為新的服務(wù)環(huán)境):

  • 拉取當(dāng)前 Github 倉庫代碼并切換到相應(yīng)的分支
  • 安裝 Node 和 Npm 環(huán)境
  • 安裝項(xiàng)目的依賴
  • 構(gòu)建庫包和演示文檔的靜態(tài)資源
  • 發(fā)布演示文檔的靜態(tài)資源

通過查看 官方 Actions[159] 和 awesome-actions[160],找到所需的 Actions:

  • Checkout[161]: 從 Github 拉取倉庫代碼到 Github 服務(wù)器的 $GITHUB_WORKSPACE目錄下
  • cache[162]: 緩存 npm
  • setup-node[163]: 安裝 Node 和 Npm 環(huán)境
  • actions-gh-pages[164]: 在 Github 上發(fā)布靜態(tài)資源

溫馨提示:可用的 Action 很多,這里只是設(shè)置了一個(gè)簡(jiǎn)單的流程。

在 .github/workflows 下新增 mian.yml 配置文件:

# 以下都是官方文檔的簡(jiǎn)單翻譯
# 當(dāng)前的 yml(.yaml) 文件是一個(gè) workflow,是持續(xù)集成一次運(yùn)行的一個(gè)過程,必須放置在項(xiàng)目的 .github/workflow 目錄下
# 如果不清楚 .yml 文件格式語法,可以查看 https://www.codeproject.com/Articles/1214409/Learn-YAML-in-five-minutes
# 初次編寫難免會(huì)產(chǎn)生格式問題,可以使用 VS Code 插件進(jìn)行格式檢測(cè),https://marketplace.visualstudio.com/items?itemName=OmarTawfik.github-actions-vscode

# 具體各個(gè)配置屬性可查看 https: //docs.github.com/en/actions/reference/workflow-syntax-for-github-actions

# workflow 的執(zhí)行仍然會(huì)受到一些限制,例如
#  - 每個(gè) job 最多執(zhí)行 6 小時(shí)(本地機(jī)器不受限制)
#  - 每個(gè) workflow 最多執(zhí)行 72 小時(shí)
#  - 并發(fā) job 的數(shù)量會(huì)受到限制
#  - 更多查看 https: //docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#usage-limits

# name: 當(dāng)前 workflow 的名稱
name: Algorithms

# on:  指定 workflow 觸發(fā)的 event
#
#      event 有以下幾種類型
#         - webhook
#         - scheduled
#         - manual
on:
  # push: 一個(gè) webhook event,用于提交代碼時(shí)觸發(fā) workflow,也可以是觸發(fā)列表,例如 [push, pull_request]

  #        workflows 觸發(fā)的 event 大部分是基于 webhook 配置,以下列舉幾個(gè)常見的 webhook:
  #           - delete:  刪除一個(gè) branch 或 tag 時(shí)觸發(fā)
  #           - fork / watch:  某人 fork / watch 項(xiàng)目時(shí)觸發(fā)(你問有什么用,發(fā)送郵件通知不香嗎?)
  #           - pull_request:  提交 PR 時(shí)觸發(fā)
  #           - page_build:  提交 Github Pages-enabled 分支代碼時(shí)觸發(fā)
  #           - push:  提交代碼到特定分支時(shí)觸發(fā)
  #           - registry_package:  發(fā)布或跟新 package 時(shí)觸發(fā)
  #           更多 webhook 可查看 https: //docs.github.com/en/actions/reference/events-that-trigger-workflows
  #           從這里可以看出 Git Actions 的一大特點(diǎn)就是 Gihub 官方提供的一系列 webhook
  push:
    # branches: 指定 push 觸發(fā)的特定分支,這里你可以通過列表的形式指定多個(gè)分支
    branches:
      - feat/framework
    #
    # branches 的指定可以是通配符類型,例如以下配置可以匹配 refs/heads/releases/10
    # - 'releases/**'
    #
    # branches 也可以使用反向匹配,例如以下不會(huì)匹配 refs/heads/releases/10
    # - '!releases/**'
    #
    # branches-ignore:  只對(duì) [push, pull_request] 兩個(gè) webhook 起作用,用于指定當(dāng)前 webhook 不觸發(fā)的分支
    # 需要注意在同一個(gè) webhook 中不能和 branches 同時(shí)使用
    #
    # tags:  只對(duì) [push, pull_request] 兩個(gè) webhook 起作用,用于指定當(dāng)前 webhook 觸發(fā)的 tag
    #
    # tags:
    #   - v1             # Push events to v1 tag
    #   - v1.*           # Push events to v1.0, v1.1, and v1.9 tags
    #
    # tags-ignore:  類似于 branches-ignore
    #
    # paths、paths-ignore...
    #
    # 更多關(guān)于特定過濾模式可查看 https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet
    #
    # 其他的 webhook 控制項(xiàng)還包括 types(不是所有的 webhook 都有 types),例如已 issues 為例,可以在 issues 被 open、reopened、closed 等情況下觸發(fā) workflow
    # 更多 webhook 的 types 可查看 https: //docs.github.com/en/actions/reference/events-that-trigger-workflows#webhook-events
    #
    # on:
    #   issues:
    #     types:  [opened, edited, closed]

  # 除此之外如果對(duì)于每個(gè)分支有不同的 webhook 觸發(fā),則可以通過以下形式進(jìn)行多個(gè) webhook 配置
  #
  # push:
  #   branches:
  #     - master
  # pull_request:
  #   branches:
  #     - dev
  #
  # 除了以上所說的 webhook event,還有 scheduled event 和 manual event
  # scheduled event:  用于定時(shí)構(gòu)建,例如最小的時(shí)間間隔是 5 min 構(gòu)建一次
  # 具體可查看 https: //docs.github.com/en/actions/reference/events-that-trigger-workflows#scheduled-events

# env:  指定環(huán)境變量(所有的 job 生效,每一個(gè) job 可以獨(dú)立通過 jobs.<job_id>.env、jobs.<job_id>.steps.env 配置)
# defaults / defaults.run: 所有的 job 生效,每一個(gè) job 可以獨(dú)立通過 jobs.<job_id>.defaults 配置
# deafults
# defaults.run

# jobs: 一個(gè) workflow 由一個(gè)或多個(gè) job 組成
jobs:
  # job id: 是 job 的唯一標(biāo)識(shí),可以通過 _ 進(jìn)行連接,例如:  my_first_job,例如這里的 build 就是一個(gè) job id
  build_and_deploy:
    # name: 在 Github 中顯示的 job 名稱
    name: Build And Deploy
    #
    # needs: 用于繼發(fā)執(zhí)行 job,例如當(dāng)前 job build 必須在 job1 和 job2 都執(zhí)行成功的基礎(chǔ)上執(zhí)行
    # needs: [job1, job2]
    #
    # runs-on: job 運(yùn)行的環(huán)境配置,包括:
    #   - windows-latest
    #   - windows-2019
    #   - ubuntu-20.04
    #   - ubuntu-latest
    #   - ubuntu-18.04
    #   - ubuntu-16.04
    #   - macos-latest
    #   - macos-10.15
    #   - self-hosted(本地機(jī)器,具體可查看 https: //docs.github.com/en/actions/hosting-your-own-runners/using-self-hosted-runners-in-a-workflow)
    runs-on: ubuntu-latest
    #
    # outputs:  用于輸出信息
    #
    # env:  用于設(shè)置環(huán)境變量
    #
    # defaults:  當(dāng)前所有 step 的默認(rèn)配置
    #
    # defaults.run

    # if: 滿足條件執(zhí)行當(dāng)前 job

    # steps:  一個(gè) job 由多個(gè) step 組成,step 可以
    #   - 執(zhí)行一系列 tasks
    #   - 執(zhí)行命令
    #   - 執(zhí)行 action
    #   - 執(zhí)行公共的 repository
    #   - 在 Docker registry 中的 action
    steps:
      #
      # id: 類似于 job id
      #
      # if:  類似于 job if
      #
      # name:  當(dāng)前 step 的名字
      - name: Checkout
        #
        # uses: 用于執(zhí)行 action
        #
        #       action: 可以重復(fù)使用的單元代碼
        #          - 為了 workflow 的安全和穩(wěn)定建議指定 action 的發(fā)布版本或 commit SHA
        #          - 使用指定 action 的 major 版本,這樣可以允許你接收 fixs 以及 安全補(bǔ)丁并同時(shí)保持兼容性
        #          - 盡量不建議使用 master 版本,因?yàn)?nbsp;master 很有可能會(huì)被發(fā)布新的 major 版本從而破壞了 action 的兼容性
        #          - action 可能是 JAVAScript 文件或 Docker 容器,如果是 Docker 容器,那么 runs-on 必須指定 linux 環(huán)境
        #
        #         指定固定 commit SHA
        #         uses:  actions/setup-node@74bc508
        #         指定一個(gè) major 發(fā)布版本
        #         uses:  actions/setup-node@v1
        #         指定一個(gè) minor 發(fā)布版本
        #         uses:  actions/[email protected]
        #         指定一個(gè)分支
        #         uses:  actions/setup-node@master
        #         指定一個(gè) Github 倉庫子目錄的特定分支、ref 或 SHA
        #         uses:  actions/aws/ec2@master
        #         指定當(dāng)前倉庫所在 workflows 的目錄地址
        #         uses:  ./.github/actions/my-action
        #         指定在 Dock Hub 發(fā)布的 Docker 鏡像地址
        #         uses:  docker: //alpine: 3.8
        #         A Docker image in a public registry
        #         uses:  docker: //gcr.io/cloud-builders/gradle

        # checkout action 主要用于向 github 倉庫拉取源代碼(需要注意 workflow 是運(yùn)行在服務(wù)器上,因此需要向當(dāng)前 github 拉取倉庫源代碼)
        # 它的功能包括但不限于
        #   - Fetch all history for all tags and branches
        #   - Checkout a different branch
        #   - Checkout HEAD^
        #   - Checkout multiple repos (side by side)
        #   - Checkout multiple repos (nested)
        #   - Checkout multiple repos (private)
        #   - Checkout pull request HEAD commit instead of merge commit
        #   - Checkout pull request on closed event
        #   - Push a commit using the built-in token

        # checkout action:  https: //github.com/actions/checkout
        uses: actions/checkout@v2
        # with: action 提供的輸入?yún)?shù)
        with:
          # 指定 checkout 的分支、tag 或 SHA
          # 更多 checkout action 的配置可查看 https: //github.com/actions/checkout#usage
          ref: feat/ci
        # args: 用于 Docker 容器的 CMD 指令參數(shù)
        # entrypoint: Docker 容器 action(覆蓋 Dockerfile 的 ENTRYPOINT) 和 JavaScript action 都可以使用
      #
      # run: 使用當(dāng)前的操作系統(tǒng)的默認(rèn)的 non-login shell 執(zhí)行命令行程序
      # 運(yùn)行單個(gè)腳本
      # run: npm install
      # 運(yùn)行多個(gè)腳本
      # run: |
      #   npm ci
      #   npm run build
      #
      # working-directory: 用于指定當(dāng)前腳本運(yùn)行的目錄
      # working-directory: ./temp
      #
      # shell: 可以指定 shell 類型進(jìn)行執(zhí)行,例如 bash、pwsh、Python、sh、cmd、powershell
      # shell: bash
      #
      # env: 除了可以設(shè)置 workflow 以及 job 的 env,也可以設(shè)置 step 的 env(可以理解為作用域不同,局部作用域的優(yōu)先級(jí)更高)
      #
      # comtinue-on-error: 默認(rèn)當(dāng)前 step 失敗則會(huì)阻止當(dāng)前 job 繼續(xù)執(zhí)行,設(shè)置 true 時(shí)當(dāng)前 step 失敗則可以跳過當(dāng)前 job 的執(zhí)行

      - name: Cache
        # cache action: https://github.com/actions/cache
        # cache 在這里主要用于緩存 npm,提升構(gòu)建速率
        uses: actions/cache@v2
        # npm 緩存的路徑可查看 https://docs.npmjs.com/cli/cache#cache
        # 由于這里 runs-on 是 ubuntu-latest,因此配置 ~/.npm
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-
      # github-script action: https://github.com/actions/github-script
      # 在 workflow 中使用 Script 語法調(diào)用 Github API 或引用 workflow context

      # setup-node action: https://github.com/actions/setup-node
      # 配置 Node 執(zhí)行環(huán)境(當(dāng)前構(gòu)建的服務(wù)器默認(rèn)沒有 Node 環(huán)境,可以通過 Action 安裝 Node)
      # 需要注意安裝 Node 的同時(shí)會(huì)捆綁安裝 npm,如果想了解為什么會(huì)捆綁,可以 Google 一下有趣的故事哦
      # 因此使用了該 action 后就可以使用 npm 的腳本在服務(wù)器進(jìn)行執(zhí)行啦
      # 這里也可以嘗試 v2-beta 版本哦
      - name: Set Node
        uses: actions/setup-node@v1
        with:
          # 也可以通過 strategy.matrix.node 進(jìn)行靈活配置
          # 這里本地使用 node 的 12 版本構(gòu)建,因此這里就進(jìn)行版本固定啦
          node-version: "12"

      - run: npm install
      - run: npm run build
      - run: npm run docs:build

      - name: Deploy
        # 用于發(fā)布靜態(tài)站點(diǎn)資源
        # actions-gh-pages action: https://github.com/peaceiris/actions-gh-pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          # GTIHUB_TOKEN:https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token
          # Github 會(huì)在 workflow 中自動(dòng)生成 GIHUBT_TOKEN,用于認(rèn)證 workflow 的運(yùn)行
          github_token: ${{ secrets.GITHUB_TOKEN }}
          # 靜態(tài)資源目錄設(shè)置
          publish_dir: ./docs/.vuepress/dist
          # 默認(rèn)發(fā)布到 gh-pages 分支上,可以指定特定的發(fā)布分支
          publish_branch: gh-pages1 # default: gh-pages
          full_commit_message: ${{ github.event.head_commit.message }}
    #
    # timeout-minutes: 一個(gè) job 執(zhí)行的最大時(shí)間,默認(rèn)是 6h,如果超過時(shí)間則取消執(zhí)行
    #
    # strategy.matrix: 例如指定當(dāng)前 job 的 node 版本列表、操作系統(tǒng)類型列表等
    # strategy.fail-fast
    # strategy.max-parallel
    # continue-on-error:  一旦當(dāng)前 job 執(zhí)行失敗,那么 workflow 停止執(zhí)行。設(shè)置為 true 可以跳過當(dāng)前 job 執(zhí)行
    # container: Docker 容器配置,包括 image、env、ports、volumes、options 等配置
    #
    # services: 使用 Docker 容器 Action 或者 服務(wù) Action 必須使用 Linux 環(huán)境運(yùn)行

溫馨提示:這里不再敘述具體的配置過程,更多可查看配置文件中貼出的鏈接信息。

上傳 CI 的配置文件后,Github 就會(huì)進(jìn)行自動(dòng)構(gòu)建,具體如下:

從零開始配置 TypeScript 項(xiàng)目的教程(二)

 

正在構(gòu)建或者構(gòu)建完成后可查看每個(gè)構(gòu)建的信息,如果初次構(gòu)建失敗則可以通過構(gòu)建信息找出失敗原因,并重新修改構(gòu)建配置嘗試再次構(gòu)建。除此之外,每次構(gòu)建失敗 Github 都會(huì)通過郵件的形式進(jìn)行通知:

從零開始配置 TypeScript 項(xiàng)目的教程(二)

 

如果構(gòu)建成功,則每次你推送新的代碼后,Github 服務(wù)會(huì)進(jìn)行一系列流程并自動(dòng)更新靜態(tài)資源站點(diǎn)。

總結(jié)

希望大家看完這篇文檔之后如果想使用其中某些工具能夠養(yǎng)成以下一些習(xí)慣:

  • 通篇閱讀工具的文檔,了解相同功能的不同工具的差異點(diǎn)

通篇閱讀工具對(duì)應(yīng)的官方 Github README 文檔以及官方站點(diǎn)文檔,了解該工具設(shè)計(jì)的核心哲學(xué)、核心功能、解決什么核心問題。前端的工具百花齊放,同樣的功能可能可以采用多種不同的工具實(shí)現(xiàn)。如果想要在項(xiàng)目中使用適當(dāng)?shù)墓ぞ撸偷弥肋@些工具的差異。完整的閱讀相應(yīng)的官方文檔,有助于你理解各自的核心功能和差異。

  • 在調(diào)研了各個(gè)工具的差異之后,選擇認(rèn)為合適的工具進(jìn)行實(shí)踐

在實(shí)踐的過程中你會(huì)對(duì)該工具的使用越來越熟悉。此時(shí)如果遇到一些問題或者想要實(shí)現(xiàn)某些功能,在通篇閱讀文檔的基礎(chǔ)上會(huì)變得相對(duì)容易。當(dāng)然如果遇到一些報(bào)錯(cuò)信息無法解決,此時(shí)第一時(shí)間應(yīng)該是搜索當(dāng)前工具所對(duì)應(yīng)的 Github Issues。除此之外,你也可以根據(jù)錯(cuò)誤的堆棧信息追蹤工具的源碼,了解源碼之后可能會(huì)對(duì)錯(cuò)誤信息產(chǎn)生的原因更加清晰。

  • 在完成以上兩步之后,你應(yīng)該總結(jié)工具的使用技巧啦,此時(shí)在此通讀工具文檔可能會(huì)產(chǎn)生不一樣的收獲

 

分享到:
標(biāo)簽:TypeScript
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定