close

前言

作者的個人實踐,想法思路都很好。今日前端早讀課文章由 @hijiangtao 授權分享。

在昨日【圖書】前端工程質量保障體系中也有提到相關實踐,有興趣可以了解看看。

正文從這開始~~

隨着前端的發展,越來越多的工具庫、方法被用在日常研發流程中,這大大提升了業務開發的效率,而隨着各類自動化流程的建設,開發同學也不再需要關注到每一個細節。前段時間項目階段性交付,在推進的過程中也做了不少嘗試,雖然從長期看,這類工作最後可能都該收斂到基礎設施部門或者標準的自動化流程中去,但並不妨礙我通過實踐來落實一些對項目開發的思考和想法。

如果你是一名有經驗的開發者,可以直接跳到文章末尾,「總結」一章有對全文內容的精簡描述。

接下來,我來分享下在項目開發中嘗試的一些自動化和提效實踐。本文在撰寫中涉及的 UI 框架主要以 create-react-app 所產生的 React 項目為主,會輔以部分其他框架的解決方案以說明。

新建項目第一步:腳手架

如果你的項目選型是 Angular 的話,那麼選擇不多可以直接上 Angular CLI;如果是 React 或 Vue 的話,那麼會有不少腳手架可以選擇,國內很多開發者都有開源不同方案。其中,大多方案會有一些組件庫、開源庫的綁定,如果你希望一個更加自由的框架搭建,官方腳手架 create-create-app (CRA) 肯定會是第一選擇。

通過 npx 執行如下命令,我們可以通過 CRA 快速創建一個 TypeScript 項目:

npx create-react-app project --template typescript

但隨着 CRA 的發展,官方腳手架也將原來暴露在模版中的越來越多細節封裝到 react-scripts 包里,簡單舉個例子,比如你想修改項目 webpack 構建流程,用官方模版無法直接上手。

官方模版提供了npm run eject命令,執行這個命令會將潛藏的一系列配置文件和一些依賴項都 「彈出」 到項目中,然後就可以由你自己完全控制增刪,但是該操作是不可逆的。在配置文件被 「彈出」 後,你後續將無法跟隨官方的腳步去升級項目所使用的 react-script 版本了。

那麼,如果你想要一個可以覆蓋配置,但又與官方版本保持同步的方案,則可以試試 cracohttps://github.com/dilanx/craco

樣式隔離方案:CSS Modules

這一部分接着上文繼續。這可能是一個已經被大家熟知且默認開啟的選項了,其起源也在於對組件樣式的隔離需求,且如果你的項目是一個 CRA 官方其實已經支持對 CSS Modules 的啟用,按照規定的格式命名你的樣式文件,CRA 便可自動對樣式進行解析處理。

但如前文所述,如果你想做一些樣式 loader 的修改,就比如默認的解析方式不支持樣式變量的駝峰式命名。要達到目的,你可以在集成 craco 後在配置craco.config.js中這麼寫,擴充 css loader 配置選項:

module.exports = { style: { css: { mode: "extends", loaderOptions: { modules: { auto: true, exportLocalsConvention: 'camelCaseOnly', }, }, }, }, };

當然,當我們在 TypeScript 文件中引用 CSS Modules 變量時,由於 TypeScript 並不知道除了 .ts 以及 .tsx 文件外的文件內容,為了防止 IDE 在語法檢查上報錯,我們還需要針對特定文件後綴聲明下環境變量。針對 CRA 新建的項目,你可以簡單建立一個 react-app-env.d.ts 文件來補充上如下說明:

/// <reference types="node" /> /// <reference types="react" /> /// <reference types="react-dom" /> /// <reference types="react-scripts" /> declare module '*.module.css' { const classes: { readonly [key: string]: string }; export default classes; } declare module '*.module.scss' { const classes: { readonly [key: string]: string }; export default classes; } declare module '*.module.sass' { const classes: { readonly [key: string]: string }; export default classes; }

此外,通過 craco 你還可以修改些其他內容,比如配置 babel 不對 ES6 模塊做轉譯、修改打包路徑、自定義熱更新方案等等。

ESLINT 代碼檢查:兩個自定義 lint 場景分享

為了保證統一一致的代碼風格,比如在項目中避免使用 var,我們可以引入 eslint rules 對提交的代碼進行規範。當然,如今大多數腳手架在新建項目時,應該都替你集成好了 eslint,要麼是.eslintrc.json、.eslintrc.js或者直接在 package.json 中添加 eslintConfig 屬性開干。

除了開啟默認的規則集外,在開發過程中,為了讓項目在多人之間能夠更高效地推進協作開發,肯定有不少細節需要處理,這裡我簡單分享兩例。

第一點,當我們對代碼做了一些增刪操作,就可能產生冗餘的 import 聲明,這個時候我們當然希望它在提交時被刪除。

在 eslint 中,插件可以暴露額外的規則以供使用。如果要達到我們剛剛說的目的,這個時候可以引入 unused-imports 插件對 es6 imports 進行代碼檢查(比如對於未使用到的 import 聲明進行 error 提示):

{ "plugins": ["unused-imports"], "rules": { "no-unused-vars": "off", "unused-imports/no-unused-imports": "error", "unused-imports/no-unused-vars": [ "warn", { "vars": "all", "varsIgnorePattern": "^_", "args": "after-used", "argsIgnorePattern": "^_" } ] } }

藉助 IDE 或者 IDE 插件,我們還可以前置到每次保存時執行對 import 引用的清理。

多人協作中還容易出現一類問題,那就是代碼衝突。當多人同時修改一個文件時,可能會同時引入新的模塊,如果不對代碼進行統一風格管理,那麼很容易出現 「import 衝突」。為了解決這個問題,可以引入 simple-import-sort 插件,插件還支持對 export 進行排序處理:

{ "plugins": ["simple-import-sort"], "rules": { "simple-import-sort/imports": "error", "simple-import-sort/exports": "error" } }

這樣,在代碼提交前執行一遍 eslint —fix 便可完成代碼的檢查與修復,關於這部分我們在後面 git hook 一章中詳細介紹。

其他還有一些與框架相關的,比如從 React 17 開始,我們可以通過配置 eslint rules 達到無需在代碼中引入 React 的目的,關於這一部分的實現可以參考 React 官方指導,以下為一段簡單的前後代碼對比:

// 以前 import React from 'react'; function App() { return <h1>Hello World</h1>; } // 現在 function App() { return <h1>Hello World</h1>; }自動化環境配置:git hook 鈎子定義

husky 是一個為 git 客戶端增加 hook 的工具,可以被用於我們配置本地自動化環境。我們可以安裝 husky 並定製我們需要的 git 鈎子及具體需要執行的任務,這樣可以方便我們在對代碼執行 git 操作時,在特定時機對代碼進行特定的檢查與處理,常見的鈎子有如下兩個:

commit-msg - 提交信息鈎子,在執行 git commit 或者 git merge 時觸發

pre-commit - 預先提交鈎子,在執行 git commit 時觸發

如下為一段 husky 安裝和初始化代碼:

npm install husky -D npx husky-init && npm install // 添加任務一條 commit-msg 鈎子 npx husky add .husky/commit-msg './node_modules/.bin/commitlint --from=HEAD~1'

舉個例子,比如我們可以結合 commitlint 工具,在 commit-msg 階段針對 commit 信息進行檢查和處理(如上代碼所示),又或者在 pre-commit 階段對將要提交的代碼進行格式化操作。

自定義命令調用:代碼風格統一與 commit 信息規範

不僅是對於開發代碼,對於 commit 信息來說,五花八門的書寫風格,也十分不利於閱讀和維護,比如,當我們需要翻閱歷史提交來定位具體 commit 所帶來的代碼變動時,這依賴於規範的 commit 信息。藉助 commitlint 可以幫助我們達到這一點。

commitlint 需要配合 husky 一起使用,具體來說,通過 husky 來保證針對具體鈎子的命令配置,通過 commitlint 保證 命令執行能夠對 commit msg 信息進行特定檢查。一個簡單的 commitlint 安裝和配置如下:

# 安裝 npm install --save-dev @commitlint/{config-conventional,cli} # 配置 echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js

配置了 commitlint 之後,若是使用默認配置,那麼只有當我們的 commit msg 符合如下規範時,commit 操作才能正常完成執行,否則中斷(其中 scope 為可選):

<type>(<scope>): description

如果需要自定義規則,則需要我們更改 commitlint.config.js 文件,為其增添 rules,關於這一部分可以參考官方文檔https://commitlint.js.org/。

此外,在添加了 commitlint 配置文件後,我們可能會看到 IDE 對這個文件的檢查標紅,由於在項目構建中我們並不關注該配置文件的格式等內容(因為他只是對 commitlint 的簡單配置),所以我們可以在 eslint 配置文件中將其忽略掉。同樣的,如果有其他的文件屬於類似的定位,你也可以一併將其加入:

{ "ignorePatterns": ["commitlint.config.js"] }

說完 commit 規範,我們的代碼格式本身也需要規範,比如針對 TypeScript 代碼會有變量命名和排序的規則,針對 html 模版會有縮進、標籤閉合等規則,這些也可以利用工具結合 git hook 來實現,此處,我們介紹下 linter 工具:lint-staged。

lint-staged 是一個在 git 暫存文件上運行 linters 的工具,通過 lint-staged 定製各類文件格式化的操作(主要通過 eslint 和 prettier 保證執行)。結合 pre-commit git hook,我們在此鈎子被觸發時執行 lint-staged,便能完成對相應文件的格式化處理。如何讓工具針對不同類型的文件區分處理呢?這裡可以通過配置達到目的,即在 pakage.json 中對 lint-staged 字段進行聲明:

{ ..., "lint-staged": { "*.{js,jsx,ts,tsx}": [ "eslint -c ./.eslintrc.json --fix" ], "(*.json|.eslintrc|.prettierrc)": [ "jsonlint --in-place" ], "*.{s,}css": [ "prettier --write" ], "*.{html,md}": [ "prettier --write" ] } }

如上代碼表明,lint-staged 可以針對 JavaScript/TypeScript 類文件執行 eslint 處理,針對 json 類文件執行 jsonlint 處理,而對樣式文件和 html 文件都用 prettier 處理。

本地開發代理環境

從本地開發的場景來看,最需要完善的一個功能就是轉發 API 請求了,用以規避可能存在的 CORS 錯誤,或者繞開一些請求限制,形式上可以是針對特定請求變更 header 信息,也可以是針對特定請求變更實際請求的域名與路徑。但無外乎都需要在本地建立一個代理環境。前端項目在本地開發時,我們可以通過一個叫做 http-proxy-middleware 的庫來增強本地 dev server 的能力。

通過文檔介紹,我們知道可以通過調用 createProxyMiddleware API 來構造一個中間件,以供 Node.js 項目使用,針對 express 應用可以通過如下配置完成代理服務器中間件的配置:

import * as express from 'express'; import { createProxyMiddleware } from 'http-proxy-middleware'; const app = express(); app.use( '/api', createProxyMiddleware({ target: 'http://www.example.org/api', changeOrigin: true, }) ); app.listen(3000);

而對於通過 CRA 構建的項目,由於 CRA 已經做了一些封裝工作,於是在本地開發時,我們不再需要顯式地起一個 Node.js 應用,而如上對代理中間件地調用,也可以在指定文件中按照規範書寫(文件名以及方法簽名需要規範,此處不列舉,CRA 官方文檔有說明)。

代碼復用:組件模版與 code snippets

當我們新建一個項目時,我們會找腳手架替我們搭建一個合適的架子,然後我們往裡面填充組件、頁面、服務等等,而針對更細粒度的代碼,我們同樣可以考慮通過代碼復用來提升我們的開發效率,這裡的場景主要可以分為兩類:

Code snippets 類代碼片段

組件粒度的文件代碼

針對第一類場景,我們在 IDE 中安裝的不少插件都替我們達到了這個目的。當我們在特定類型的文件中敲出幾個字母,然後一回車,就能快速生成一段代碼片段。舉個例子,比如當我們使用 RxJS 時經常需要定義 BehaviorSubject 及其 getter & setter,要是輸入 bgs 就能出現如下代碼,便能提高我們在寫一些初始化代碼時的效率:

/* TODO: 數據流定義 */ behaviorName$ = new BehaviorSubject<string>(initialValue); get behaviorName() { return this.behaviorName$; } set behaviorName(value: string) { this.behaviorName$.next(value); }

而我們要做的事情就是將 bgs 和上述代碼(及特定占位符)連接起來,實現方案可以是 VS Code 插件,比如我之前寫過一個https://marketplace.visualstudio.com/items?itemName=hijiangtao.tutor-code-snippets,也可以是WebStorm 插件或者 live templates。

針對第二類場景,我們需要的往往是指定路徑下一套遵循我們命名規範的模版、樣式和 TypeScript 邏輯代碼。舉個例子,在 Angular 項目中,當我們新建一個組件時,我們需要同時生成 HTML、JavaScript、CSS 文件並更新離其最近的 *.module.ts 文件:

CREATE projects/src/app/demo/demo.component.css (0 bytes) CREATE projects/src/app/demo/demo.component.html (19 bytes) CREATE projects/src/app/demo/demo.component.spec.ts (612 bytes) CREATE projects/src/app/demo/demo.component.ts (267 bytes) UPDATE projects/src/app/app.module.ts (1723 bytes)

在 Angular 項目中,我們可以通過 Angular schematics 來很方便的實現這一目的;而針對 React 或者 Vue 項目,社區也有不少實現方案,比如 generate-react-cli。

如果不藉助社區或者官方方案,要實現第二類場景所需的工具,也可以自己開發一個 npm 包並暴露相應 bin 腳本,然後通過 npx 執行來達到目的,關於 npx 的介紹可以參考我之前寫過的一篇博文《記錄一下 npx 的使用場景》。

版本發布:發版與 CHANGELOG 自動化

每當我們需要上線一個新需求時,為了更好的記錄變動,我們一般需要發個版本,並同時記錄一些 CHANGELOG,如果能把這部分工作完全自動化,毋庸置疑可以提升我們的項目規範和發版效率。這裡以 standard-version 舉例。我們引入 standard-version 對自動打 tag 以及 CHANGELOG 生成進行規範,具體來說,是期望通過 standard-version 達到如下目的:

根據指定規則自動升級項目不同級別(major、minor、patch)的版本並打 tag

對比歷史 commit 提交自動生成不同版本間的可閱讀、分類的 CHANGELOG 日誌

通過配置,我們還可以重命名上文提到的 commit 信息中的 type 字段在 CHANGELOG 中的標題展示,如下為一個示例配置:

module.exports = { "types": [ { "type": "feat", "section": "Features" }, { "type": "fix", "section": "Bug Fixes" }, { "type": "test", "section": "Tests" }, { "type": "doc", "section": "Document" }, { "type": "build", "section": "Build System" }, { "type": "ci", "hidden": true } ] }

如下為自動生成的 CHANGELOG 示例:

# Changelog All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. ## [1.11.0](https://git.woa.com/test/project/compare/v1.10.1...v1.11.0) (2022-06-28) ### Features - 測試 feature 提交 @hijiangtao (merge request !65) ([a091125](https://git.woa.com/test/project/commit/xxx)) ### [1.10.1](https://git.woa.com/test/project/compare/v1.10.0...v1.10.1) (2022-06-24) ### Bug Fixes - 修改 XXX,並新增 YYY (merge request !63) ([e3a01ce](https://git.woa.com/test/project/commit/yyy))

在使用 standard-version 的時候,默認的配置可以滿足絕大部分場景了,但更細粒度的控制仍需要我們修改命令或者配置。比如,當我們在 package.json 中指定了倉庫 url 後,我們便可以在 commit 信息中通過 @ 符號指定對應用戶、通過 # 引用對應 issue/PR,這些在生成 CHANGELOG 時都會被轉成對應包含鏈接地址的超鏈接,如下列上一些我在開發中的版本自動生成所涉及的應用場景及注意事項:

// 首次執行(不變更版本) standard-version -a -- --first-release // 但是如果項目版本不符合規範,還是需要手動發布,因為需要保證項目從 v1.0.0 開始 // https://github.com/conventional-changelog/standard-version/issues/131 standard-version -a -- --release-as 1.0.0 // 在 package.json 中確保項目正確配置 repository 對象 repository.url // 帶通知具體 user 的 commit-msg git commit -m "fix: bug produced by @timojiang" // 帶 issue 的 commit-msg git commit -m "fix: implement functionality discussed in issue #2"總結

本文從項目初始化選用腳手架開始、樣式隔離、lint 規則與 git hook 再到模版工具和自動化版本日誌,介紹了本人在開發過程中的一些實踐和思考,旨在指出一個項目從代碼初始化到交付上線過程中可能涉及到的不同流程中,存在的不同提交效率和自動化流程的工作,囿於文章篇幅,沒有針對所有涉及到的流程和工具進行詳細的 API 介紹,但你仍可以針對其中提到的各類關鍵詞在互聯網上進行搜索和查看。

本文在撰寫時也儘量針對不同流程換用了普適的一些描述,以保證所提供的方案在替換了具體工具庫後仍是長期可用的,防止文章內容受工具的時效性影響。

全文來看,主要有這麼幾點實踐總結:

在腳手架的選擇上,你可以使用 create-react-app 或者 umi 等社區方案,但如果想要更靈活的腳手架,當你使用 CRA 的時候,可以一併考慮 craco;

CSS Modules 的作用無需多說,但同樣需要注意 TypeScript 檢查以及你在命名 CSS 變量寫法上的兼容;

ESLINT 現在已經是大部分項目的標配了,如果你的項目涉及多人協作,可以配置一些額外的 plugin 協助保持風格一致、減少代碼合併衝突。當然,在統一代碼風格上,還可以通過 husky 定製 git 鈎子與具體需要執行的任務,比如:

commit-msg

pre-commit

其中通過 lint-staged 在 pre-commit 時機定製各類文件格式化的操作(主要用 eslint 和 prettier 保證執行),通過 commitlint 保證 commit msg 信息符合規範。

本地開發代理環境在很多團隊是必須的,你可以選用一個中間件來增強你的 dev server。

我們同樣可以考慮通過代碼復用來提升我們的開發效率,這裡的場景主要可以分為兩類:code snippets 以及組件級別的文件修改。

每當項目上線,規範來說,都需要發版及記錄日誌變更,我們可以引入 standard-version 對自動打 tag 以及 CHANGELOG 生成進行規範。

關於本文作者:@hijiangtao原文:https://hijiangtao.github.io/2022/07/07/Practice-of-Project-Development-and-Auto-Workflow/

關於【提效】相關閱讀,歡迎讀者自薦投稿,前端早讀課等你

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()