項(xiàng)目及工程化配置以及主要功能配置、規(guī)范化管理和使用細(xì)節(jié)應(yīng)該都沒(méi)啥問(wèn)題了其弊。
后面若發(fā)現(xiàn)構(gòu)建及配置問(wèn)題朱盐、以及可優(yōu)化的細(xì)節(jié)部分、包括功能的進(jìn)一步迭代也會(huì)使項(xiàng)目不斷更新完善的~
目前主要是做了規(guī)范及工程化的配置扑庞,后期打算開(kāi)發(fā)并開(kāi)源一個(gè)react-admin
標(biāo)準(zhǔn)后臺(tái)管理系統(tǒng),用于實(shí)踐并鞏固一些前端技術(shù)框架與組件開(kāi)發(fā)等拒逮。
效果圖
最簡(jiǎn)單的demo運(yùn)行效果
背景
為什么要自己搭建一個(gè)webpack-react-ts
的標(biāo)準(zhǔn)化項(xiàng)目罐氨?
原因之一
在很早之前的公司時(shí),項(xiàng)目實(shí)際開(kāi)發(fā)過(guò)程中產(chǎn)生過(guò)這種需求:
每次開(kāi)發(fā)新項(xiàng)目時(shí)希望有個(gè)完整功能滩援、配置好用栅隐、標(biāo)準(zhǔn)化規(guī)范的模板可以快速使用
完整功能:如果是使用
create-react-app
創(chuàng)建項(xiàng)目,屬于最簡(jiǎn)易版的一個(gè)實(shí)現(xiàn)了玩徊,各種loader
租悄、plugin
、babel
等webpack
配置缺失恩袱,作為現(xiàn)代前端開(kāi)發(fā)不可或缺的構(gòu)建工具泣棋,其對(duì)項(xiàng)目的大部分功能支持是必須的。配置好用:
webpack
對(duì)各種插件的配置與集成畔塔,基本路由模塊潭辈、頁(yè)面模塊、組件模塊澈吨、store狀態(tài)管理模塊等基本常用功能的簡(jiǎn)要示例及demo把敢,要有一定的覆蓋率。標(biāo)準(zhǔn)規(guī)范:這個(gè)可能是工程化構(gòu)建最重要的部分之一了棚辽,
eslint
技竟、prettier
冰肴、stylelint
屈藐、commitlint
等lint規(guī)范和格式化以及test測(cè)試榔组,還有目錄結(jié)構(gòu)規(guī)范、命名規(guī)范等联逻。對(duì)于規(guī)范化的意義其重要性不言而喻搓扯,可讀性、可維護(hù)性包归、開(kāi)發(fā)效率锨推、bug率等可見(jiàn)一斑
原因之二
當(dāng)然網(wǎng)上肯定有很多優(yōu)秀的輪子供我們使用,他們有的功能更全公壤、有的細(xì)節(jié)更好换可,有的示例更完整。這都是我們學(xué)習(xí)的榜樣厦幅,對(duì)于其源碼閱讀和學(xué)習(xí)也是極為有益的沾鳄,不過(guò),始終避免不了一點(diǎn)确憨,看的再多都不如自己實(shí)踐來(lái)的有效果译荞,所以,這也是我考慮的原因之一休弃,為了自己實(shí)踐和操作來(lái)提升技術(shù)熟練度吞歼。
原因三
這也是源于在實(shí)際開(kāi)發(fā)過(guò)程中的需求,即塔猾,不同技術(shù)平臺(tái)及場(chǎng)景適應(yīng)于不同的技術(shù)框架和路線篙骡。
舉個(gè)栗子,
管理后臺(tái)系統(tǒng):這個(gè)多適用于PC端桥帆,使用單頁(yè)面應(yīng)用開(kāi)發(fā)医增,主流技術(shù)框架
React
、Vue
老虫、Angular
等網(wǎng)站類(lèi):這類(lèi)項(xiàng)目一般追求美觀和SEO搜索的重要性叶骨,而不會(huì)像管理后臺(tái)系統(tǒng)一樣有很復(fù)雜的邏輯,更多的是一些動(dòng)效及UI交互祈匙,因?yàn)?code>SPA的首屏加載及
SEO
問(wèn)題忽刽,社區(qū)有成熟的服務(wù)端渲染SSR
方案:next
、nuxt
夺欲,分別對(duì)應(yīng)react
和vue
技術(shù)棧跪帝,對(duì)于習(xí)慣了單頁(yè)面開(kāi)發(fā)的同學(xué)還是很友好的。移動(dòng)端:輕量化些阅、輕量化伞剑、輕量化,重要的事情說(shuō)三遍市埋。手機(jī)端流量第一黎泣,項(xiàng)目越小越輕量性能越好加載也就越快恕刘,對(duì)于用戶(hù)體驗(yàn)也就越好。比較手機(jī)端一般對(duì)應(yīng)的都是C端用戶(hù)抒倚,不像管理后臺(tái)主要是給B端及公司內(nèi)部使用褐着。用戶(hù)體驗(yàn)要求是完全不一樣的。如托呕,移動(dòng)端版
react
——preact
含蓉,又如svelte
、solidjs
等無(wú)虛擬DOM
框架项郊,減少大量運(yùn)行時(shí)代碼馅扣。
所以,我認(rèn)為每個(gè)技術(shù)都有其對(duì)應(yīng)的優(yōu)勢(shì)和適合的場(chǎng)景着降,單一框架并不是完全適合梭哈到底的岂嗓。
這也是我開(kāi)發(fā)的node-cli
自動(dòng)化初始化項(xiàng)目的一個(gè)框架模板之一。
接下來(lái)對(duì)各個(gè)不同技術(shù)方案及最佳實(shí)踐也會(huì)考慮構(gòu)建具有實(shí)戰(zhàn)意義的模板以供使用
主要方向大概分為:webpack+react/vue+typescript
等webpack
方向鹊碍、vite+react/vue+typescript
等vite
方向厌殉、nuxt/next/egg/koa
等服務(wù)端渲染SSR
方向、svelte/solidjs
等無(wú)虛擬DOM
的輕量級(jí)框架方向侈咕、rollup/parcel
等構(gòu)建工具方向公罕、esbuild/swc
等新型編譯器與構(gòu)建工具的結(jié)合使用等、以及小程序/跨平臺(tái)等耀销。
有點(diǎn)飄啊楼眷,想法還挺多的??~
開(kāi)搞
目標(biāo)
- 技術(shù)棧:
Webpack5.x
+React17.x
+Antd4.x
+TypeScript4.x
+Less
- 工程規(guī)范/格式化:
Eslint
+Stylelint
+Prettier
+Commitlint
-
ES6
、JSX
熊尉,Babel
支持 - 支持
HMR
熱更新 - 支持低版本瀏覽器兼容
- 支持
Antd
按需加載罐柳、自定義主題、css module
- 支持
js
狰住、css
壓縮张吉、chunk
拆分、Gzip
依賴(lài)
dependencies
"dependencies": {
"antd": "^4.16.10", // 懂得都懂
"axios": "^0.21.1", // 懂得都懂
"clsx": "^1.1.1", // 條件處理 React className 類(lèi)名
"core-js": "3", // 現(xiàn)代JS語(yǔ)法polyfills庫(kù)催植,用于兼容瀏覽器及ES6肮蛹、ES7語(yǔ)法
"dayjs": "^1.10.6", // 日期處理庫(kù),比moment小很多创南,只有幾kb
"history": "^5.0.1", // H5 history庫(kù)
"lodash": "^4.17.21", // 非常全面的方法庫(kù)
"react": "^17.0.2", // 懂得都懂
"react-dom": "^17.0.2", // 懂得都懂
"react-helmet-async": "^1.0.9", // 在react中優(yōu)雅的添加 HTML header 各種屬性
"react-router-dom": "^5.2.0" // react 路由庫(kù)伦忠,經(jīng)典 react 三件套
}
devDependencies
"devDependencies": {
// babel
"@babel/cli": "^7.14.8", // 可以讓babel以cli的方式執(zhí)行 如:babel src --out-dir dist --watch
"@babel/core": "^7.15.0", // babel 核心包
"@babel/plugin-proposal-class-properties": "^7.14.5", // @babel/preset-env 插件已包含
"@babel/plugin-syntax-dynamic-import": "^7.8.3", // 動(dòng)態(tài)導(dǎo)入、懶加載
"@babel/plugin-transform-modules-commonjs": "^7.15.0", // 轉(zhuǎn)化成CommonJS 規(guī)范的代碼
"@babel/plugin-transform-react-constant-elements": "^7.14.5", // React 常量元素轉(zhuǎn)換器 : 它會(huì)尋找不隨 props 改變的所有靜態(tài)元素稿辙,并將它們從渲染方法(或者無(wú)狀態(tài)函數(shù)式組件)中抽離出來(lái)昆码,以避免多余地調(diào)用 createElement
"@babel/plugin-transform-react-inline-elements": "^7.14.5", // React 行內(nèi)元素轉(zhuǎn)換器 : 它會(huì)將所有 JSX 聲明(或 者 createElement 調(diào)用)替換成優(yōu)化過(guò)的版本,以便代碼可以更快執(zhí)行
"@babel/plugin-transform-runtime": "^7.15.0", // 抽離提取 Babel的注入代碼,防止重復(fù)加載赋咽,減小體積
"@babel/preset-env": "^7.15.0", // 提供的預(yù)設(shè)笔刹,允許我們使用最新的JavaScript
"@babel/preset-react": "^7.14.5", // react 支持
"@babel/preset-typescript": "^7.15.0", // typescript 支持
"babel-plugin-dynamic-import-node": "^2.3.3", // 為node提供加載轉(zhuǎn)換 import => require
"babel-plugin-import": "^1.13.3", // 按需引入、加載
"babel-plugin-lodash": "^3.3.4", // 按需加載
"babel-plugin-transform-react-remove-prop-types": "^0.4.24", // 從生產(chǎn)生成中刪除不必要的類(lèi)型
// commitlint 是 git commit 執(zhí)行規(guī)則相關(guān)插件
"@commitlint/cli": "^13.1.0",
"@commitlint/config-conventional": "^13.1.0",
// react hot 配合webpack實(shí)現(xiàn)熱更新插件
"react-refresh": "^0.10.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
// @types 開(kāi)頭的是對(duì)應(yīng)包的 TypeScript 類(lèi)型聲明
"@types/enzyme": "^3.10.9",
"@types/enzyme-adapter-react-16": "^1.0.6",
"@types/enzyme-to-json": "^1.5.4",
"@types/react-dom": "^17.0.9",
"@types/react-helmet": "^6.1.2",
"@types/react-router-dom": "^5.1.8",
"@types/webpack-env": "^1.16.2",
// enzyme 測(cè)試庫(kù)
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.6",
"enzyme-to-json": "^3.6.2",
// eslint
"eslint": "^7.32.0",
"eslint-config-airbnb-typescript": "^12.3.1", // airbnb 規(guī)范
"eslint-config-prettier": "^8.3.0", // 關(guān)閉所有不必要或可能與[Prettier]沖突的規(guī)則冬耿。
"eslint-import-resolver-typescript": "^2.4.0", // 添加 ts 語(yǔ)法支持 eslint-plugin-import
"eslint-import-resolver-webpack": "^0.13.1", // 支持 eslint-plugin-import 讀寫(xiě)模塊解析
"eslint-plugin-import": "^2.23.4", // ES6+ import/export 語(yǔ)法支持
"eslint-plugin-jsx-a11y": "^6.4.1", // JSX元素的可訪問(wèn)性規(guī)則的靜態(tài)AST檢查器
"eslint-plugin-prettier": "^3.4.0", // 以 eslint的規(guī)則運(yùn)行 prettier 格式化
"eslint-plugin-react": "^7.24.0", // react 相關(guān)規(guī)則
"eslint-plugin-react-hooks": "^4.2.0", // react-hooks 相關(guān)規(guī)則
"eslint-plugin-redux-saga": "^1.2.1", // redux-saga 相關(guān)規(guī)則
"@typescript-eslint/eslint-plugin": "^4.29.0", // 使 eslint 支持 typescript,.eslintrc.js 的 plugins 參數(shù)
"@typescript-eslint/parser": "^4.29.0", // 使 eslint 支持 typescript 萌壳,.eslintrc.js 的 parser 參數(shù)
// webpack
"webpack": "^5.49.0",
"webpack-bundle-analyzer": "^4.4.2", // 包依賴(lài)分析 可視化
"webpack-cli": "^4.7.2", // cli
"webpack-dev-middleware": "^5.0.0", // 中間件亦镶,可配合 express以服務(wù)的方式開(kāi)發(fā)使用
"webpack-dev-server": "^3.11.2", // dev-server
"webpack-hot-middleware": "^2.25.0", // 熱加載
"webpack-pwa-manifest": "^4.3.0", // 生成 pwa 相關(guān)配置
// webpack loader:解析對(duì)應(yīng)文件
"babel-loader": "^8.2.2",
"style-loader": "^3.2.1", // 添加 css 到 HTML
"css-loader": "^6.2.0", // css加載器 處理 @import/url()
"postcss-loader": "^6.1.1", // 處理 css
"less-loader": "^10.0.1", // less => css
"file-loader": "^6.2.0", // 通過(guò) import/require() 加載的圖片等解析為 url
"html-loader": "^2.1.2", // 壓縮HTML
"svg-url-loader": "^7.1.1",
"url-loader": "^4.1.1",
// webpack plugin
"html-webpack-plugin": "^5.3.2", // 簡(jiǎn)化HTML文件的創(chuàng)建 ,配合webpack包含hash的bundle使用
"mini-css-extract-plugin": "^2.2.0", // css 壓縮
"terser-webpack-plugin": "^5.1.4", // 使用 terser 壓縮 js (terser 是一個(gè)管理和壓縮 ES6+ 的工具)
"clean-webpack-plugin": "^4.0.0-alpha.0", // 用于刪除/清理生成的 build 文件
"compression-webpack-plugin": "^8.0.1", // Gzip壓縮
// prettier 格式化
"prettier": "^2.3.2",
"pretty-quick": "^3.1.1", // 在更改的文件上運(yùn)行 prettier
// stylelint css樣式規(guī)范
"stylelint": "^13.13.1",
"stylelint-config-recess-order": "^2.4.0", // 按照session和Bootstrap的方式對(duì)CSS屬性進(jìn)行排序袱瓮。
"stylelint-config-standard": "^22.0.0", // 基本規(guī)范
// 工具
"husky": "^7.0.1", // 自動(dòng)配置 Git hooks 鉤子
"lint-staged": "^11.1.2", // 對(duì)暫存的git文件運(yùn)行l(wèi)inter
"rimraf": "^3.0.2", // 刪除cli缤骨,兼容不同平臺(tái)
"yargs": "^17.1.0", // 讀取命令參數(shù)
// 其他
"typescript": "^4.3.5",
"less": "^4.1.1", // less 的解析庫(kù)
"postcss": "^8.3.6", // 專(zhuān)門(mén)處理樣式的工具
"postcss-nested": "^5.0.6", // 解析處理嵌套規(guī)則
"autoprefixer": "^10.3.1", // 自動(dòng)生成各瀏覽器前綴 postcss 的一個(gè)插件
"serve": "^12.0.0", // 本地啟動(dòng)一個(gè)服務(wù),可以查看靜態(tài)文件
}
目錄規(guī)劃
├── dist // 默認(rèn)的 build 輸出目錄
├── .husky // pre-commit hook
├── config // 全局配置文件及webpack配置文件
├── public // 靜態(tài)文件
├── test // 測(cè)試目錄
└── src // 源碼目錄
├── assets // 公共的文件(如image尺借、css绊起、font等)
├── components // 項(xiàng)目組件
├── constants // 常量/接口地址等
├── layout // 全局布局
├── routes // 路由
├── store // 狀態(tài)管理器
├── utils // 工具庫(kù)
├── pages // 頁(yè)面模塊
├── Home // Home模塊,建議組件統(tǒng)一大寫(xiě)開(kāi)頭
├── ...
├── App.tsx // react頂層文件
├── main.ts // 項(xiàng)目入口文件
├── typing.d.ts // ts類(lèi)型文件
├── .editorconfig // IDE格式規(guī)范
├── .eslintignore // eslint忽略
├── .eslintrc // eslint配置文件
├── .gitignore // git忽略
├── .npmrc // npm配置文件
├── .prettierignore // prettierc忽略
├── .prettierrc // prettierc配置文件
├── .stylelintignore // stylelint忽略
├── .stylelintrc // stylelint配置文件
├── babel.config.js // babel配置文件
├── commitlint.config.js // commit配置文件
├── LICENSE.md // LICENSE
├── package.json // package
├── postcss.config.js // postcss
├── README.md // README
├── setupEnzyme.ts // Enzyme測(cè)試配置文件
├── tsconfig.json // typescript配置文件
使用 npx create-react-app xxx --typescript
可以快速創(chuàng)建 TS 項(xiàng)目燎斩,我們可以基于創(chuàng)建完的項(xiàng)目再進(jìn)行一些自定義配置虱歪。
ES6+、JSX 栅表、TypeScript 支持
這里我們使用 babel
處理笋鄙,當(dāng)然對(duì)于 TypeScript
也可以使用 ts-loader
解析,這里就不做演示了怪瓶,網(wǎng)上也有很多對(duì)比測(cè)試
JavaScript 語(yǔ)法特性支持
- 首先安裝
babel
相關(guān)依賴(lài):yarn add @babel/preset-env -D
- 然后在
.babelrc.js
中添加預(yù)設(shè)
module.exports = {
presets: [
[
'@babel/preset-env',
{
// 這里可以配置相關(guān)瀏覽器兼容規(guī)則萧落,暫時(shí)我們先不管
// corejs: 3,
// debug: true,
// useBuiltIns: 'usage', // 開(kāi)啟瀏覽器兼容 polyfills,會(huì)根據(jù)browserslist配置洗贰,引入需要的庫(kù)找岖,需要安裝對(duì)應(yīng)版本的 core-js@3
}
]
]
}
React的JSX語(yǔ)法支持
- 安裝依賴(lài):
yarn add @babel/preset-react -D
- 然后在
.babelrc.js
中添加預(yù)設(shè)
module.exports = {
presets: ['@babel/preset-env', '@babel/preset-react']
}
TypeScript 支持
- 安裝依賴(lài):
yarn add typescript @babel/preset-typescript -D
- 然后在
.babelrc.js
中添加預(yù)設(shè)
module.exports = {
presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript']
}
- 接下來(lái)在
webpack
中配置loader
module.exports = {
module: {
rules: [
// .ts .tsx
{
test: /\.(ts|tsx)$/,
use: 'babel-loader',
exclude: /node_modules/,
},
]
}
}
Antd按需加載、主題定制
antd
作為react
的好基友敛滋,在國(guó)內(nèi)后臺(tái)系統(tǒng)開(kāi)發(fā)中幾乎快成為標(biāo)配了许布,我們以antd
為例配置按需加載和主題定制化
babel配置按需加載
- 安裝antd:
yarn add antd
- 安裝依賴(lài):
yarn add babel-plugin-import -D
- 然后在
.babelrc.js
中配置plugins
module.exports = {
presets: [],
plugins: [
[
'import',
{
libraryName: 'antd',
libraryDirectory: 'es',
style: true, // or 'css'
},
'antd',
]
]
}
webpack/loader配置antd主題
css-loader、style-loader 也是必須的
module.exports = {
module: {
rules: [
// 處理 .css
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
// 處理 .less
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
// less-loader
{
loader: 'less-loader',
options: {
lessOptions: {
// 替換antd的變量绎晃,去掉 @ 符號(hào)即可
// https://ant.design/docs/react/customize-theme-cn
modifyVars: {
'primary-color': '#1DA57A',
},
javascriptEnabled: true, // 支持js
},
},
},
],
},
]
}
}
工程化規(guī)范配置
Eslint代碼檢查
安裝ts解析器:
yarn add @typescript-eslint/parser -D
安裝相關(guān)拓展:
yarn add eslint-config-airbnb-typescript eslint-plugin-prettier -D
安裝相關(guān)插件:
yarn add eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y @typescript-eslint/eslint-plugin -D
添加
.eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
// 使用 airbnb 拓展插件規(guī)范相關(guān)庫(kù)
// prettier 已內(nèi)置了許多相關(guān)插件
extends: ['airbnb-typescript', 'prettier'],
// 拓展和支持相關(guān)能力的插件庫(kù)
plugins: ['prettier', 'react', 'react-hooks', 'jsx-a11y', '@typescript-eslint'],
}
- ES6+規(guī)范 import/export 導(dǎo)入導(dǎo)出配置:
yarn add eslint eslint-plugin-import eslint-import-resolver-typescript eslint-import-resolver-webpack -D
module.exports = {
parser: '@typescript-eslint/parser',
// 使用 airbnb 拓展插件規(guī)范相關(guān)庫(kù)
// prettier 已內(nèi)置了許多相關(guān)插件
extends: ['airbnb-typescript', 'prettier'],
// 拓展和支持相關(guān)能力的插件庫(kù)
plugins: ['prettier', 'react', 'react-hooks', 'jsx-a11y', '@typescript-eslint'],
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
webpack: {
config: './config/webpack.base.js',
},
typescript: {
alwaysTryTypes: true, // always try to resolve types under `<root>@types` directory even it doesn't contain any source code, like `@types/unist`
directory: './tsconfig.json',
},
},
'import/ignore': ['types'], // Weirdly eslint cannot resolve exports in types folder (try removing this later)
}
}
Prettier 代碼格式化
- Eslint配置中我們已經(jīng)添加
prettier
插件爹脾,支持以eslint
的規(guī)范進(jìn)行格式化,并提供保存時(shí)存在沖突時(shí)的解決方案 - 添加
.prettierrc
箕昭,這里再定義下格式化基本風(fēng)格
yarn add prettier -D
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"arrowParens": "avoid",
"endOfLine": "auto"
}
Stylelint 樣式規(guī)范
- 安裝依賴(lài):
yarn add stylelint stylelint-config-recess-order stylelint-config-standard -D
- 添加
.stylelintrc.js
module.exports = {
extends: ['stylelint-config-standard', 'stylelint-config-recess-order'],
};
針對(duì)以上代碼規(guī)范檢查灵妨,我們也應(yīng)該添加對(duì)應(yīng) ignore 文件以忽略非必要的檢查文件,以?xún)?yōu)化性能
如:.eslintignore
node_modules
dist
Commitlint 代碼提交規(guī)范
git hook
鉤子的實(shí)現(xiàn)落竹,這里我們使用 husky
+ lint-staged
方案
- 安裝依賴(lài):
yarn add husky lint-staged -D
git commit
規(guī)范泌霍,這里我們使用 @commitlint/config-conventional
- 安裝依賴(lài):
yarn add "@commitlint/cli @commitlint/config-conventional -D
這里也可以使用
pretty-quick
代替prettier
格式化:yarn add pretty-quick -D
,使得prettier
格式化更簡(jiǎn)單易用
- 根目錄下創(chuàng)建
.husky
文件夾與commitlint.config.js
文件,并在.husky
文件夾下添加precommit
朱转、commit-msg
蟹地、以及.gitignore
commitlint.config.js
用于配置 commitlint
相關(guān)規(guī)范
// git commit 規(guī)范
// <類(lèi)型>[可選的作用域]: <描述>
// # 主要type
// feat: 增加新功能
// fix: 修復(fù)bug
// # 特殊type
// docs: 只改動(dòng)了文檔相關(guān)的內(nèi)容
// style: 不影響代碼含義的改動(dòng),例如去掉空格藤为、改變縮進(jìn)怪与、增刪分號(hào)
// build: 構(gòu)造工具的或者外部依賴(lài)的改動(dòng),例如webpack缅疟,npm
// refactor: 代碼重構(gòu)時(shí)使用
// revert: 執(zhí)行g(shù)it revert打印的message
// # 暫不使用type
// test: 添加測(cè)試或者修改現(xiàn)有測(cè)試
// perf: 提高性能的改動(dòng)
// ci: 與CI(持續(xù)集成服務(wù))有關(guān)的改動(dòng)
// chore: 不修改src或者test的其余修改分别,例如構(gòu)建過(guò)程或輔助工具的變動(dòng)
module.exports = {
extends: ['@commitlint/config-conventional'],
// rules: [] // 可以自定義規(guī)則
};
precommit
git提供的鉤子函數(shù),在該階段執(zhí)行eslint
等驗(yàn)證規(guī)范存淫,再根據(jù)執(zhí)行結(jié)果判斷是否提交
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# npx --no-install pretty-quick --staged
npx --no-install lint-staged
commit-msg
驗(yàn)證 git commit -m "xx"
提交的 message
信息格式和規(guī)范
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no-install commitlint --edit $1
.gitignore
作用是為了忽略 husky
安裝產(chǎn)生的非項(xiàng)目代碼文件
_
package.json
配置
"scripts": {
// 這里換成 postinstall 也可以耘斩,npm提供的一個(gè)生命周期鉤子
// 主要是為了我們?cè)诎惭b完 husky依賴(lài)后可以執(zhí)行husky腳本,使git hook鉤子生效
"prepare": "husky install",
"commitlint": "commitlint --from=master",
// lint 相關(guān)規(guī)則和規(guī)范執(zhí)行命令
"lint:eslint:fix": "eslint --ext .ts,.tsx --no-error-on-unmatched-pattern --quiet --fix",
"lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
"lint": "npm run lint:eslint:fix && npm run lint:style",
// prettier 格式化的一個(gè)拓展庫(kù)
"pretty-quick": "pretty-quick",
},
"lint-staged": {
"*.{ts,tsx}": [
"npm run lint:eslint:fix",
"git add --force"
],
"*.{md,json}": [
"pretty-quick --staged",
"git add --force"
]
}
vscode 配置
- 安裝
eslint
桅咆、prettier
拓展插件 - 添加
.vscode/settings.json
配置文件括授,或者全局設(shè)置vscode settings.json
// 保存時(shí)格式化
"editor.formatOnSave": true,
// 相當(dāng)于是 vscode 保存時(shí)調(diào)用的鉤子事件
"editor.codeActionsOnSave": {
// 保存時(shí)使用 ESLint 修復(fù)可修復(fù)錯(cuò)誤
"source.fixAll.eslint": true
}
// 針對(duì)對(duì)應(yīng)文件設(shè)置默認(rèn)格式化插件
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
HMR 熱更新配置
這里我們使用 react-refresh
+webpack
方案
- 安裝依賴(lài):
yarn add react-refresh @pmmmwh/react-refresh-webpack-plugin -D
- 添加
webpack
配置
const { HotModuleReplacementPlugin } = require('webpack');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = {
devServer: {
port: 3010, // 端口
publicPath: '/', // 基礎(chǔ)路徑
open: true, // 打開(kāi)頁(yè)面
hot: true, // 開(kāi)啟熱更新
historyApiFallback: true, // 把404響應(yīng)定位到 index.html
// proxy:{} // 開(kāi)發(fā)環(huán)境,接口代理
},
plugins: [new HotModuleReplacementPlugin(), new ReactRefreshWebpackPlugin()]
}
生產(chǎn)環(huán)境打包壓縮岩饼、chunk拆分
- 安裝
css
壓縮插件:yarn add mini-css-extract-plugin css-minimizer-webpack-plugin -D
- 安裝
js
壓縮插件:yarn add terser-webpack-plugin -D
- 安裝
gzip
壓縮插件:yarn add compression-webpack-plugin -D
- 如果想查看對(duì)包模塊的可視化分析可使用插件:
webpack-bundle-analyzer
css
壓縮的webpack
配置:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
// 支持動(dòng)態(tài)注入 link 標(biāo)簽到 html中
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
module: {
rules: [
// .css
{
test: /\.css$/,
use: [
// 該插件是為了把 CSS提取到單獨(dú)的文件中
MiniCssExtractPlugin.loader,
'css-loader'
],
},
// less
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader'
],
},
]
},
plugins: [
// 簡(jiǎn)化 HTML 文件的創(chuàng)建
new HtmlWebpackPlugin({
template: 'public/index.html',
}),
// MiniCss
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css",
}),
],
optimization: {
minimizer: [
// 壓縮
new CssMinimizerPlugin(),
]
}
}
js
壓縮的webpack
配置:
webpack5.x內(nèi)置有terser-webpack-plugin
荚虚,不過(guò)如果想要自定義配置還是需要安裝的
// 使用 terser 壓縮 JavaScript
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
optimization: {
minimize: true, // 開(kāi)啟壓縮
minimizer: [
// 1. 簡(jiǎn)單使用默認(rèn)配置
new TerserPlugin() // 使用插件
// 2. 自定義配置一些參數(shù)
new TerserPlugin({
terserOptions: {
output: {
comments: false, // 在輸出中省略注釋
},
// ie8: true 開(kāi)啟 ie8 支持
},
}),
],
},
}
chunk
拆分
這里的可玩性還是比較高的,各種性能優(yōu)化啥的籍茧,有興趣的朋友可以研究研究曲管,我們這里只做簡(jiǎn)單使用配置??~
module.exports = {
optimization: {
splitChunks: {
chunks: 'all', // 處理哪種chunk,可選值為:all硕糊、async院水、initial(all包括異步和非異步)
maxInitialRequests: 10, // 入口點(diǎn)的最大并行請(qǐng)求數(shù)。
minSize: 0, // 生成 chunk 的最小體積(以 bytes 為單位)
// 緩存組简十,該組內(nèi)配置可繼承檬某、覆蓋splitChunks中的配置
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/, // [\\/] 是為了分別匹配 win、Unix系統(tǒng)路徑
// 拆分 vendor螟蝙,自定義name(vendor默認(rèn)是合并為一個(gè) chunk)
name(module) {
// 把 npm 庫(kù)拆分獨(dú)立打包
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
return `npm.${packageName.replace('@', '')}`;
},
},
},
}
},
}
Gzip
壓縮
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
plugins: [
new CompressionPlugin({
algorithm: 'gzip', // 壓縮算法
test: /\.js$|\.css$|\.html$/,
threshold: 10240, // 只壓縮大于 10240 bytes 的chunk
minRatio: 0.8, // 只壓縮大于該值的 minRatio = Compressed Size / Original Size
})
]
}
【圖 1】chunk拆分恢恼、gzip壓縮后的效果
【圖 2】Analyzer可視化分析
總結(jié)
本文著重描述了項(xiàng)目的工程化配置,涉及相關(guān)代碼規(guī)范胰默、開(kāi)發(fā)環(huán)境搭建场斑、生產(chǎn)環(huán)境優(yōu)化等,旨在打造出一個(gè)可快速使用的現(xiàn)代化webpack+react+typescript
模板牵署,以供在以后的實(shí)際業(yè)務(wù)場(chǎng)景需求中零成本使用漏隐。
目前這只算是最簡(jiǎn)陋的一個(gè)demo,對(duì)webpack
相關(guān)的各種調(diào)優(yōu)與項(xiàng)目?jī)?yōu)化還有很多方向可研究奴迅。
接下來(lái)就是搞實(shí)際場(chǎng)景的業(yè)務(wù)組件的開(kāi)發(fā)青责、第三方開(kāi)源組件的集成使用、通用組件的使用和開(kāi)發(fā)封裝了~
項(xiàng)目地址:https://github.com/JS-banana/webpack-react-ts