- Notifications
You must be signed in to change notification settings - Fork0
React boilerplate: Webpack5+typescript+react...
qinsong77/webpack5-react-template
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
WIP
- Webpack5
- set-up/config with typescript
- dev/build/analyzer
- React hot refresh
- Typescript
- Test
- Jest
- RTL
- Code style/lint
- husky
- eslint
- prettier
- commitlint
- Babel
- Env
- postcss
- mock serve
- msw for mock server and test, refer:Stop mocking fetch
- not stable, jest failed sometime due to it.
- Tailwindcss
- ui.shadcn
- Router =>TanStack Router
- Zustand
- Generate Api with type:orval
- axios + useQuery?
- e2e test: playwright
- midscenejs using LLM to help testing.
- reuse
data-testid, make it consistent
- Webpack =>rspack
Issues
fork-ts-checker-webpack-plugin会使用tsconfig.json的include的字段里去check文件,导致webpack dev时测试文件类型有问题也会报错,暂时是exclude排除了pnpm run codegen:api报错,和升级prettier有关系,回退到2.8.4没问题 => 使用orval 替换了跑测试axios目前还报错 Network Error, 等msw修复。。"undici": "^5.0.0",
msw结合jest中hack的代码比较多,need to removeorval生成的.msw文件类型报错,显示是手动注释@ts-nocheck,但重新生成会覆盖failed to add
"type": "module",for package.json, due to webpack crash. => Replace ts-node withtsx to solve it. but addthread-loader failed.
这里使用pnpm管理package,pnpm相比npm,yarn最大的优点就是节约磁盘空间并提升安装速度,在我用pnpm-workspace+turborepo搭建monorepo的项目中,感触颇深,得益于pnpm,在monorepo下即使有几十个app+package,安装速度也在接受范围内。所以后续的所有命令都使用pnpm完成。初始化:
mkdir webpack5-react-templatecd webpack5-react-template pnpm init
先稍微介绍下package.json中几个主要的字段如dependencies,devDependencies,peerDependencies,scripts的意思。
- dependencies: 生产环境,项目运行的依赖(如:react,react-dom)
- devDependencies: 开发环境,项目所需的依赖(如:webpack插件,打包插件,压缩插件,eslint等)
- scripts: 指定了运行脚本命令的npm命令行缩写
- private:如果设为true,无法通过
npm publish发布代码。
官网解释文档
pnpm add typescript -D# tsc --init命令创建tsconfig.jsonpnpmexec tsc --init
这个时候项目根目录下会生成一份tsconfig.json文件,删除了多余的注释,内容如下:
{"compilerOptions": {"target":"es5","module":"commonjs","esModuleInterop": true,"forceConsistentCasingInFileNames": true,"strict": true,"skipLibCheck":true }}添加配置如下
{/* Visit https://aka.ms/tsconfig to read more about this file */"$schema":"https://json.schemastore.org/tsconfig","compilerOptions":{"target":"es5",/* 指定要编译到的目标ECMAScript版本:'ES3'、'ES5'(默认)、'ES2015'、'ES2016'、'ES2017'、'ES2018'、'ES2019'、'ES2020' 或 'ESNEXT'。 */"module":"esnext",/* 指定要使用的模块系统 */"lib":["dom","dom.iterable","esnext"],/* 编译过程中需要引入的库文件的列表。 */"allowJs":false,/* 不允许编译器编译JS,JSX文件 */"noEmit":true,/* 不输出文件,即编译后不会生成任何js文件 */"strict":true,/* 启用所有严格的类型检查选项。 */"moduleResolution":"node",/** 模块解析策略,ts默认用node的解析策略,即相对的方式导入 */"allowSyntheticDefaultImports":true,/* 允许从没有默认导出的模块中默认导入。 这不会影响代码发出,只是类型检查。 */"esModuleInterop":true,/* 允许export=导出,由import from 导入 */"noFallthroughCasesInSwitch":true,/* 在switch语句中要求处理所有情况,避免出现漏写break导致的错误。 */"resolveJsonModule":true,/* 允许导入JSON文件作为模块。 */"isolatedModules":true,/* 将每个文件转换为一个单独的模块(类似于 'ts.transpileModule')。 */"jsx":"react-jsx","skipLibCheck":true,/* 跳过对导入的库文件进行类型检查。 */"forceConsistentCasingInFileNames":true,/* 禁止对同一文件的大小写不一致地引用。 */},"include":["src"]}
安装react
pnpm i react react-dom# 安装类型校验pnpm i @types/react @types/react-dom -D新建src目录,和index.tsx和app.tsx文件
// indeximport*asReactfrom'react';import*asReactDOMfrom'react-dom/client';importAppfrom'./App';constroot=ReactDOM.createRoot(document.getElementById('root')asHTMLElement);root.render(<React.StrictMode><App/></React.StrictMode>);// app.tsxconstApp=()=>{return(<divclassName="App"><headerclassName="App-header"><p> Edit<code>src/App.tsx</code> and save to reload.</p><aclassName="App-link"href="https://reactjs.org"target="_blank"rel="noopener noreferrer"> Learn React</a></header></div>);};exportdefaultApp;
示例代码
// constant.jsexportconsta=1constb=2exportdefaultb// index.tsximportconstantfrom'./constant'console.log(constant)
不管是ts 还是babel,在将esm编译为cjs 的时候,对于export default 的处理,都会放在一个default的属性上,即module.exports.default = xxx,上面编译的结果大致为:
"use strict";Object.defineProperty(exports,"__esModule",{value:true});// 标示这是一个 esm 模块exports.a=1;varb=2;exports.default=b;// index.tsxvar_constant=require("./constant");// esm 和 cjs 的兼容处理varconstant_1=_constant.__esModule ?_constant :{default:_constant};console.log(constant_1.default);
在默认情况下ts会将esm模块编译成commonjs
- 对于
export default的变量,ts会将其放在module.exports的default属性上 - 对于
export的变量,ts会将其放在module.exports对应变量名的属性上 - 额外给
module.exports增加一个__esModule: true的属性,用来告诉编译器,这本来是一个 esm 模块
看一下npm包中react的导出
可以看到通过npm方式引用react时默认是以commonjs方式导出的,结合上面ts默认编译的规则,import React from 'react' 会从exports.default 上去拿代码,显然此时default属性不存在commonjs模块中,因此会导致打印undefined;而import * as React from 'react' 则会把React作为为一个对象,因此不会有问题。
首先对于react v16.13.0 之前的版本都是通过export default 导出的,所以使用import React from 'react' 来导入 react,上面的 console.log(constant) 才不会是 undefined
但是从react v16.13.0 开始,react 就改成了用export 的方式导出了,如果在 ts 中使用import React from 'react' 则会有错误提示:
TS1259: Module 'xxxx' has no default export.由于没有了default 属性,所以上面编译后的代码 console.log(constant) 输出的是 `undefined ,ts 会提示有错误。
上面的问题延伸一下,其实不仅仅是引入react,在esm中引入任何commonjs的模块在ts默认编译时都会有这样的问题,ts提供了esModuleInterop 和allowSyntheticDefaultImports 这两个配置来影响ts默认的解析。
allowSyntheticDefaultImports是一个类型检查的配置,它会把import没有exports.default的报错忽略,如果你的target是es6加上这个配置就够了,但如果你的目标代码是es5仅仅加上这个还不行,还需要使用esModuleInterop,因为它才会改变tsc的编译产物:
// tsconfig.json{ "compilerOptions": { "module": "commonjs", "target": "es5", "esModuleInterop":true } } // index.tsximport React from 'react';console.log(React.useEffect)// tsc产物"use strict";var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod };};Object.defineProperty(exports, "__esModule", { value: true });var react_1 = __importDefault(require("react"));console.log(react_1.default.useEffect);在加上esModuleInterop 之后编译产物多了一个_importDefault 辅助函数,而他的作用就是给module.exports 加上default 属性。根据ts官网的说明 开启esModuleInterop的同时也会默认开启allowSyntheticDefaultImports,因此更推荐直接加esModuleInterop。
react-ts-template├── package.json├── public # 存放html模板├── webpack # webpack配置│ ├── config # 配置文件│ ├── utils # │ ├── webpack.common.ts│ ├── webpack.development.ts│ ├── webpack.production.ts├── README.md├── src│ ├── assets # 存放会被 Webpack 处理的静态资源文件:一般是图片等静态资源│ │ ├── fonts # iconfont 目录│ │ ├── images # 图片资源目录│ ├── common # 存放项目通用文件│ ├── components # 项目中通用的组件目录│ ├── feature # 项目中通用的业务组件目录│ ├── config # 项目配置文件│ ├── pages # 项目页面目录│ │ ├── routes.tsx # 路由目录│ ├── typings # 项目中d.ts 声明文件目录│ ├── types # 项目中声明文件│ ├── uiLibrary # 组件库│ ├── services # 和后端相关的文件目录│ ├── store # redux 仓库│ ├── style global style文件夹│ ├── utils # 全局通用工具函数目录│ ├── App.tsx # App全局│ ├── index.tsx # 项目入口文件└── tsconfig.json # TS 配置文件└── tsconfig.webpack.json # 给ts-node指定webpack的tsconfig-paths时使用
pnpm add webpack webpack-cli webpack-dev-server webpack-merge -D
这里webpack的配置文件也使用typescript,需要额外配置,参考官网Configuration Languages
要使用 Typescript 来编写 webpack 配置,需要先安装必要的依赖,比如 Typescript 以及其相应的类型声明,类型声明可以从DefinitelyTyped 项目中获取,依赖安装如下所示:
pnpm add ts-node @types/node @types/webpack -D
值得注意的是你需要确保tsconfig.json 的compilerOptions 中module 选项的值为commonjs,否则 webpack 的运行会失败报错,因为ts-node 不支持commonjs 以外的其他模块规范。
官网有三种设置方式,这里选择第三种
先安装tsconfig-paths 这个 npm 包,如下所示:
pnpm add tsconfig-paths -D
然后添加tsconfig.webpack.json
{"compilerOptions": {"module":"commonjs","target":"es5","esModuleInterop":true,"allowSyntheticDefaultImports":true,"downlevelIteration":true },"include": ["webpack"]}package.json
{"scripts": {"build":"cross-env TS_NODE_PROJECT=\"tsconfig.webpack.json\" webpack" }}之所以要添加cross-env,是因为我们在直接使用TS_NODE_PROJECT 时遇到过 "TS_NODE_PROJECT" unrecognized command 报错的反馈,添加 cross-env 之后该问题也似乎得到了解决,可以查看这个issue
updated: Replace ts-node withtsx
- html-webpack-plugin: 在webpack构建后生成html文件,同时把构建好入口js等文件引入到生成的html文件中。
- mini-css-extract-plugin:抽取css为单独的css文件.
- css-minimizer-webpack-plugin: 使用cssnano 优化和压缩 CSS.
- style-loader: 开发环境选择下使用
style-loader, 它可以使用多个标签将 CSS 插入到 DOM 中,反应会更快 - css-loader:css-loader 会对
@import和url()进行处理,就像 js 解析import/require()一样。 - @pmmmwh/react-refresh-webpack-plugin && react-refresh: react热更新
- dotenv:可以将环境变量中的变量从
.env文件加载到process.env中。 - cross-env:运行跨平台设置和使用环境变量的脚本
- @soda/friendly-errors-webpack-plugin: 用于美化控制台,良好的提示错误。
- fork-ts-checker-webpack-plugin: runs TypeScript type checker on a separate process.
- babel相关,后续单独罗列
- postcss等,后续单独罗列
pnpm add html-webpack-plugin @pmmmwh/react-refresh-webpack-plugin react-refresh dotenv cross-env mini-css-extract-plugin css-minimizer-webpack-plugin style-loader css-loader @soda/friendly-errors-webpack-plugin fork-ts-checker-webpack-plugin -D
添加index.html
<htmllang="en"><head><metacharset="utf-8"/><title><%= htmlWebpackPlugin.options.title %></title><linkrel="icon"href="<%= htmlWebpackPlugin.options.publicPath %>favicon.ico"/><metaname="viewport"content="width=device-width, initial-scale=1"/><metaname="theme-color"content="#000000"/><metaname="description"content="<%= htmlWebpackPlugin.options.description %>"/><linkrel="apple-touch-icon"href="<%= htmlWebpackPlugin.options.publicPath %>logo192.png"/></head><body><noscript>You need to enable JavaScript to run this app.</noscript><divid="root"></div></body></html>
比如filename: '[name].[hash].[ext]'
- hash:以项目为单位,项目内容改变了,则会生成新的
hash,内容不变则hash不变。 整个工程任何一个需要被打包的文件发生了改变,打包结果中的所有文件的hash值都会改变。 - chunkhash:以
chunk为单位,当一个文件内容改变,则整个chunk组的模块hash都会改变。
比如: 假设打包出口有a.123.js和c.123.js,a文件中引入了b文件,修改了b文件的内容,重新的打包结果为a.111.js和c.123.js 的hash值会被影响,但是c的hash值不受影响。
- contenthash:以自身内容为单位,依赖不算。
webpack5 之前,通常使用
- raw-loader 将文件导入为字符串
- url-loader 将文件作为data URL 内联到bundle中
- file-loader 将文件发送到输出目录
相比webpack5之前需要url-loader、file-loader等处理,在webpack5中直接内置了asset 模块,
asset/resource发送一个单独的文件并导出 URL。之前通过使用file-loader实现asset/inline导出一个资源的 data URI。之前通过使用url-loader实现。asset/source导出资源的源代码。之前通过使用raw-loader实现。- asset在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用url-loader,并且配置资源体积限制实现。
关于配置type:'asset'后,webpack 将按照默认条件,自动地在resource 和inline 之间进行选择:小于 8kb 的文件,将会视为inline 模块类型,否则会被视为resource 模块类型。
- dotenv:可以将环境变量中的变量从
.env文件加载到process.env中。 - cross-env:运行跨平台设置和使用环境变量的脚本
- DefinePlugin: 允许在
编译时将代码中的变量替换为其他值或表达式
以REACT_APP_开头的env,DefinePlugin会自动处理替换。
利用这里也可以实现,部署到不同环境下,都只build一次,配置不同的环境变量注入。
关于TS转JS,有三种方案
- tsc: 不好配合
webpack使用,转换es5以后,一些语法特性不能转换。 - ts-loader: 可以做类型检查,可搭配
tsconfig.json使用。 babel-loader+@babel/preset-typescript, 插件丰富,提供缓存机制,后续兼容扩展更强,但做不了类型检查(可以使用Fork TS Checker Webpack Plugin。(推荐)
tsc 生成的代码没有做polyfill 的处理,需要全量引入core-js,而babel 则可以用@babel/preset-env 根据targets 的配置来按需引入core-js 的部分模块,所以生成的代码体积更小。
babel 缺点就是有一些 ts 语法并不支持:
比如不支持const enum(会作为 enum 处理),不支持namespace 的跨文件合并,导出非 const 的值,不支持过时的 export = import = 的模块语法。
但关系不大。
这里选择第三种,安装依赖:
pnpm i babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript core-js -D# 作为project不需要,library使用runtime# pnpm i @babel/plugin-transform-runtime -D# pnpm add @babel/runtime
类库项目的构建如果需要注入
polyfill的话,最好使用@babel/transform-runtime,因为它提供了一种不污染全局作用域的方式。而业务项目中最好使用preset-env的useBuintIns配置来注入 polyfill,这种方式会污染全局作用域。
babel-loader: 使用 Babel 和 webpack 转译 JavaScript 等文件,内部核心转译功能需要
@babel/core这个核心库。@babel/core:@babel/core是babel的核心库,所有的核心api都在这个库里,这些api可供babel-loader调用@babel/preset-env: 这是一个预设的插件集合,包含了一组相关的插件,Babel中是通过各种插件来指导如何进行代码转换。该插件包含所有es6转化为es5的翻译规则。可以做到按需加载我们需要的
polyfill
@babel/prest-env是babel转译过程中的一些预设,它负责将一些基础的es 6+语法,比如const/let...转译成为浏览器可以识别的低级别兼容性语法。这里需要注意的是
@babel/prest-env并不会对于一些es6+高版本语法的实现,比如Promise等polyfill,你可以将它理解为语法层面的转化不包含高级别模块(polyfill)的实现。
- @babel/runtime: is a library that contains Babel modular runtime helpers. preset-env的polyfill会污染全局环境,项目开发可以接受,但做library时最好避免,不应该污染全局,并且应该提供更好的打包体积和效率
- @babel/plugin-transform-runtime: A plugin that enables the re-use of Babel's injected helper code to save on codesize.
- 当开发者使用异步或生成器的时候,自动引入@babel/runtime/regenerator,开发者不必在入口文件做额外引入;
- 提供沙盒环境,避免全局环境的污染
- 移除babel内联的helpers,统一使用@babel/runtime/helpers代替,减小打包体积
- @babel/preset-react: Babel preset for all React plugins.是一组预设,所谓预设就是内置了一系列babel plugin去转化jsx代码成为我们想要的js代码
- @babel/preset-typescript:这是一个插件,使Babel能够将TypeScript代码转化为JavaScript。
- @babel/polyfill:@babel/preset-env只是提供了语法转换的规则,但是它并不能弥补浏览器缺失的一些新的功能,如一些内置的方法和对象,如Promise,Array.from等,此时就需要polyfill来做js的垫片,弥补低版本浏览器缺失的这些新功能。注意:Babel 7.4.0该包将被废弃
- core-js:它是JavaScript标准库的polyfill,而且它可以实现按需加载。使用@babel/preset-env的时候可以配置core-js的版本和core-js的引入方式。
- regenerator-runtime:提供generator函数的转码
babel.config.js
constIS_DEV=process.env.NODE_ENV==='development'/**@type {import('@babel/core').ConfigFunction} */module.exports={presets:[['@babel/preset-env',{useBuiltIns:'usage',// https://babeljs.io/docs/babel-preset-env#corejscorejs:{version:3,proposals:true,// 使用尚在提议阶段特性的 polyfill},},],['@babel/preset-react',{runtime:'automatic',development:IS_DEV,},],'@babel/preset-typescript',],plugins:[].concat(IS_DEV ?['react-refresh/babel'] :[]),}
browserslist实际上就是声明了一段浏览器的合集,我们的工具可以根据这个合集描述,针对性的输出兼容性代码,browserslist应用于babel、postcss等工具当中。
“> 1%”表示兼容市面上使用量大于百分之一的浏览,“last 1 chrome version”表示兼容到谷歌的上一个版本,具体的可以使用命令npx browserslist "> 1%"的方式查看都包含了哪些浏览器
browserslist可以在package.json文件配置,也可以单出写一个.browserslistrc文件进行配置。工具会自动查找.browserslistrc中的配置,如果没有发现.browserslistrc文件,则会去package.json中查找
// 在.browserslistrc中的写法> 1%last 2 versions// 还可以配置不同环境下的规则(在.browserslistrc中)[production]> 1%ie 10[development]last 1 chrome versionlast 1 firefox version// 在package.json中的写法{ "browserslist": ["> 1%", "last 2 versions"]}// 还可以配置不同环境下的规则(在package.json中)// production和development取决你webpack中mode字段的配置{ "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }}postcss其实就是类似css中的babel的作用,
pnpm add tailwindcss -Dpostcss.config.js引入tailwindcss作为plugin- 修改index.css, 引入
tailwindcss组件(如官网@tailwind base;etc...)
- 巨坑,和
css-loader配置modules冲突,modules有了tailwindcss就不work了
{ loader: 'css-loader', options: { modules: { localIdentName: '[local]_[hash:base64:5]', }, }, },pnpm add postcss postcss-loader postcss-preset-env postcss-flexbugs-fixes postcss-normalize -D
- Maybeunocss is better?
ESLint是一个前端标准的静态代码检查工具,它可以根据配置的规则来检查代码是否符合规范。
而Prettier 是一个代码格式化工具。 ESLint 是通过制定的的规范来检查代码的,这里的规范 有两种:
- 代码风格规范
- 代码质量规范
Prettier 主要负责的是代码风格。
ESLint 中extends 和plugins 这两个配置参数的区别总是会困扰。
plugins 只是开启了这个插件,而extends 则会继承别人写好的一份.eslintrc 的配置,这份配置不仅仅包括了rules, 还有parser,plugins 之类的东西。
注意:要把Prettier 的推荐配置plugin:prettier/recommended 放在extends 最后一项。
举个例子,假如我们要配置 ESLint + TypeScript,可以看到官网有这样的配置:
module.exports={root:true,parser:'@typescript-eslint/parser',plugins:['@typescript-eslint',],extends:['eslint:recommended','plugin:@typescript-eslint/recommended',],};
神奇的是,当你去掉plugins 之后发现eslint 依然可以正常工作。更神奇的是,只要你写了extends,那么连parser 也可以不用加,要知道没有指定parser 选项,eslint 可看不懂你的 TypeScript 文件。
所以说,到底是plugins 加上了 TypeScript 的能力还是extends 加上了 TypeScript 的规则呢?很让人困惑,翻找了一下网上的资料发现了这个帖子。
先来说结论吧:plugins 只是开启了这个插件,而extends 则会继承别人写好的一份.eslintrc 的配置,这份配置不仅仅包括了rules 还有parser,plugins 之类的东西。
所以回到问题,为什么在继承了plugin:@typescript-eslint/recommended 之后就可以不写plugins 和parser 呢?因为别人已经把配置都放在recommended 这份配置表里了,这样对使用的人来说,就可以少写很多配置项了。
也就是说,下面两份配置是等价的:
module.exports={parser:"@typescript-eslint/parser",parserOptions:{sourceType:"module"},plugins:["@typescript-eslint"],extends:[],rules:{"@typescript-eslint/explicit-function-return-type":["error",{allowExpressions:true}]}}
以及
module.exports={plugins:[],extends:["plugin:@typescript-eslint/recommended"],rules:{"@typescript-eslint/explicit-function-return-type":["error",{allowExpressions:true}]}}
对于第一份配置:
- 需要手动添加
parser,parserOptions,plugins - 只开启了
@typescript-eslint/explicit-function-return-type一个规则
对于第二份配置:
plugin:@typescript-eslint/recommended自动添加了parser,parserOptions,plugins- 自动加上一些推荐的 TypeScript 的 ESLint 规则
- 自定义了
@typescript-eslint/explicit-function-return-type规则
pnpm add prettier -Dpnpm add eslint -Dpnpm add @typescript-eslint/parser @typescript-eslint/eslint-plugin -Dpnpm add eslint-config-prettier eslint-plugin-prettier -Dpnpm add eslint-plugin-react eslint-plugin-react-hooks -Dpnpm add eslint-plugin-import eslint-import-resolver-typescript -D
eslint-plugin-import: This plugin intends to support linting of ES2015+ (ES6+) import/exportsyntax(支持 ES2015+ (ES6+) 导入/导出语法的 linting), and prevent issues with misspelling of file paths and import names.eslint-import-resolver-typescript: This plugin addsTypeScriptsupport toeslint-plugin-importeslint-plugin-simple-import-sort: Easy autofixable import sorting.
太麻烦了,还不如用biomejs
统一编辑器格式.editorconfig
# Editor configuration, see http://editorconfig.orgroot =true[*]charset =utf-8end_of_line =lfindent_style =spaceindent_size =2insert_final_newline =truetrim_trailing_whitespace =true[*.md]max_line_length =offtrim_trailing_whitespace =false
husky用来绑定Git Hooks,在指定时机(例如pre-commit)执行我们想要的命令,比如可用于提交代码时进行eslint 校验,如果有eslint 报错可阻止代码提交。详细的安装使用方式可参考Husky 文档
lint-staged 能够让lint只检测git缓存区的文件,提升速度。
pnpm add husky lint-staged -D
package.json中添加命令
{"scripts":{"prepare":"husky install & npx only-allow pnpm" },"lint-staged": {"*.{js,jsx,ts,tsx}": ["prettier --write","eslint --fix"] }}或者
pnpm i lint-staged husky -Dpnpm set-script prepare"husky install"# 在package.json中添加脚本pnpm run prepare# 初始化husky,将 git hooks 钩子交由husky执行
接着设置你想要的git hooks
Husky 初始化完成后,pnpm dlx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
.husky下会出现文件commit-msg如下
#!/usr/bin/env sh."$(dirname --"$0")/_/husky.sh"npx --no-install commitlint --edit
添加 lint-staged
pnpm dlx husky add .husky/pre-commit"npx --no-install lint-staged"@commitlint/config-conventional@commitlint/cli 制定了git commit提交规范,团队可以更清晰地查看每一次代码的提交记录
@commitlint/config-conventional 这是一个规范配置,标识采用什么规范来执行消息校验, 这个默认是Angular的提交规范
pnpm add -D @commitlint/config-conventional @commitlint/cli @commitlint/types
在项目根目录下创建commitlint.config.ts
importtype{UserConfig}from'@commitlint/types'constConfiguration:UserConfig={extends:['@commitlint/config-conventional'],}exportdefaultConfiguration
commitizen 的作用主要是为了生成标准化的commit message,符合Angular 规范。
一个标准化的commit message 应该包含三个部分:Header、Body 和 Footer,其中的 Header 是必须的,Body 和 Footer 可以选填。
<type>(<scope>): <subject>// 空一行<body>// 空一行<footer>Header 部分由三个字段组成:type(必需)、scope(可选)、subject(必需)
Type
type必须是下面的其中之一:- feat: 增加新功能
- fix: 修复 bug
- docs: 只改动了文档相关的内容
- style: 不影响代码含义的改动,例如去掉空格、改变缩进、增删分号
- refactor: 代码重构时使用,既不是新增功能也不是代码的bud修复
- perf: 提高性能的修改
- test: 添加或修改测试代码
- build: 构建工具或者外部依赖包的修改,比如更新依赖包的版本
- ci: 持续集成的配置文件或者脚本的修改
- chore: 杂项,其他不需要修改源代码或不需要修改测试代码的修改
- revert: 撤销某次提交
scope
用于说明本次提交的影响范围。scope 依据项目而定,例如在业务项目中可以依据菜单或者功能模块划分,如果是组件库开发,则可以依据组件划分。
- subject
主题包含对更改的简洁描述:
注意三点:
- 使用祈使语气,现在时,比如使用 "change" 而不是 "changed" 或者 ”changes“
- 第一个字母不要大写
- 末尾不要以.结尾
- Body
主要包含对主题的进一步描述,同样的,应该使用祈使语气,包含本次修改的动机并将其与之前的行为进行对比。
- Footer
包含此次提交有关重大更改的信息,引用此次提交关闭的issue地址,如果代码的提交是不兼容变更或关闭缺陷,则Footer必需,否则可以省略。
使用方法:
如果需要在项目中使用commitizen 生成符合AngularJS 规范的提交说明,还需要安装cz-conventional-changelog 适配器。
pnpm i commitizen cz-conventional-changelog -D
安装指令和命令行的展示信息
pnpm set-script commit"git-cz"# package.json 中添加 commit 指令, 执行 `git-cz` 指令
初始化commit指令(可能出错)
pnpm dlx commitizen init cz-conventional-changelog --save-dev --save-exact
或者直接在package.json添加
{"config": {"commitizen": {"path":"cz-conventional-changelog" } }}接下来就可以使用$ pnpm commit 来代替$ git commit 进行代码提交了。
也可以自定义提交规范,cz-conventional-changelog就可以移除了
pnpm i commitlint-config-cz cz-customizable -D
增加.cz-config.js如下并修改配置:
{"config": {"commitizen": {"path":"node_modules/cz-customizable" } }}官方example
"use strict";module.exports = { types: [ { value:"✨新增", name:"新增: 新的内容" }, { value:"🐛修复", name:"修复: 修复一个Bug" }, { value:"📝文档", name:"文档: 变更的只有文档" }, { value:"💄格式", name:"格式: 空格, 分号等格式修复" }, { value:"♻️重构", name:"重构: 代码重构,注意和特性、修复区分开" }, { value:"⚡️性能", name:"性能: 提升性能" }, { value:"✅测试", name:"测试: 添加一个测试" }, { value:"🔧工具", name:"工具: 开发工具变动(构建、脚手架工具等)" }, { value:"⏪回滚", name:"回滚: 代码回退" } ], scopes: [ { name:"javascript" }, { name:"typescript" }, { name:"react" }, { name:"test" } { name:"node" } ], // it needs to match the valuefor field type. Eg.:'fix' /* scopeOverrides: { fix: [ {name:'merge'}, {name:'style'}, {name:'e2eTest'}, {name:'unitTest'} ] },*/ // override the messages, defaults are as follows messages: { type:"选择一种你的提交类型:", scope:"选择一个scope (可选):", // usedif allowCustomScopes istrue customScope:"Denote the SCOPE of this change:", subject:"短说明:\n", body:"长说明,使用\"|\"换行(可选):\n", breaking:"非兼容性说明 (可选):\n", footer:"关联关闭的issue,例如:#31, #34(可选):\n", confirmCommit:"确定提交说明?(yes/no)" }, allowCustomScopes: true, allowBreakingChanges: ["特性","修复"], // limit subject length subjectLimit: 100};
webpack --profile --json> stats.jsonpnpm i webpack-bundle-analyzer -g webpack-bundle-analyzer stats.json将optimization.runtimeChunk 设置为true 或multiple,会为每个入口添加一个只含有runtime 的额外chunk。此配置的别名如下:
webpack.config.js
module.exports={//...optimization:{runtimeChunk:{name:(entrypoint)=>`runtime~${entrypoint.name}`,},},};
运行时的chunk文件,形如import('abc').then(res=>{})这种异步加载的代码,在webpack中即为运行时代码。比如这样异步引入一个组件:
constButton=React.lazy(()=>import(/* webpackChunkName: "Button" */'./components/Button'));
如果不设置runtimeChunk,默认是false,第一次打包:
可以看到入口文件main的hash也变了。而我们明明只改了button组件。
可设置runtimeChunk为true
设置runtimeChunk是将包含chunks 映射关系的 list单独从 main.js里提取出来,因为每一个 chunk 的 id 基本都是基于内容 hash 出来的,所以每次改动都会影响它,如果不将它提取出来的话,等于 main.js每次都会改变。缓存就失效了。设置runtimeChunk之后,webpack就会生成一个个
runtime~xxx.js的文件。然后每次更改所谓的运行时代码文件时,打包构建时 main.js的hash值是不会改变的。如果每次项目更新都会更改 main.js的hash值,那么用户端浏览器每次都需要重新加载变化的app.js,如果项目大切优化分包没做好的话会导致第一次加载很耗时,导致用户体验变差。现在设置了runtimeChunk,就解决了这样的问题。所以这样做的目的是避免文件的频繁变更导致浏览器缓存失效,所以其是更好的利用缓存。提升用户体验。
但是这样又有一个问题,runtime.js size很小,如果chunk有变化,这个文件每次构建都会变,多个一个http请求。每次重新构建上线后,浏览器每次都需要重新请求它,它的 http 耗时远大于它的执行时间了,所以建议不要将它单独拆包,而是将它内联到我们的 index.html 之中.
可使用插件script-ext-html-webpack-plugin解决。但这个插件虽然能用,但和webpack5不兼容了。可以使用插件hwp-inline-runtime-chunk-plugin代替。
module.exports={//...optimization:{runtimeChunk:true,},plugins:[// ...newScriptExtHtmlWebpackPlugin({inline:/runtime~.+\.js$/,}),]};
在 Webpack 中,启动 Tree Shaking 功能必须同时满足三个条件:
- 使用 ESM 规范编写模块代码(
importandexport) - 配置
optimization.usedExports为 true(默认值),启动标记功能 - 启动代码优化功能,可以通过如下方式实现:
- 配置
mode = production - 配置
optimization.minimize = true(默认值) - 提供
optimization.minimizer数组, 注入Terser(minimize为true时如果不覆盖选项,默认启用,覆盖了要单独引入使用)、UglifyJS插件
- 配置
sideEffects
usedExports是检查上下文有没有引用,如果没有引用,就会注入魔法注释,通过terser压缩进行去除未引入的代码
而sideEffects是对没有副作用的代码进行去除
css tree shaking
https://blog.csdn.net/pfourfire/article/details/126505335
// webpack.config.jsmodule.exports={entry:"./src/index",mode:"production",devtool:false,optimization:{usedExports:true,},};
每个打包以后的 bundle 文件里面,真正包含哪些内容,项目里的 module、js、component、html、css、img 最后都被放到哪个对应的 bunlde 文件里了。
每个 bundle 文件里,列出了每一个的 module、component、js 具体 size,同时会列出 start size、parsed size、gzip size 这三种不同的形式下到底多大,方便优化。
start size:原始没有经过 minify 处理的文件大小
parse size:比如 webpack plugin 里用了 uglify,就是 minified 以后的文件大小
gzip size:被压缩以后的文件大小
Zero-runtime Stylesheets in TypeScript. But not now for using vanilla
使用Swagger Petstore - OpenAPI 3.0 测试
- pont, 不是特别好用,懒得配置
Pont 把 swagger、rap、dip 等多种接口文档平台,转换成 Pont 元数据。Pont 利用接口元数据,可以高度定制化生成前端接口层代码,接口 mock 平台和接口测试平台。
- OpenAPI Typescript Generate TypeScript interfaces, REST clients, and JSON Schemas from OpenAPI specifications.
- orval orval is able to generate client with appropriate type-signatures (TypeScript) from any valid OpenAPI v3 or Swagger v2 specification, either in yaml or json formats.
- openapi-typescript Tools for consuming OpenAPI schemas in TypeScript.
- swagger-typescript-api Generate the API Client for Fetch or Axios from an OpenAPI Specification
- ts-codegen 一个生成前端接口层代码和对应 TypeScript 定义的工具。
对比了下,功能都差不多,但orval更自由些,支持自定义API client,没有强绑定,且:
- Generate typescript models
- Generate HTTP Calls
- Generate Mocks with MSW
About
React boilerplate: Webpack5+typescript+react...
Topics
Resources
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Uh oh!
There was an error while loading.Please reload this page.

