本文档从零开始配置 react + typescript
项目。
-
webpack webpack 打包工具
-
webpack-cli webpack 命令行接口
-
webpack-dev-server 在本地启动一个http服务,运行应用
yarn add -D webpack webpack-cli webpack-dev-server
在项目根目录创建 webpack.config.js
配置文件,基本结构如下:
const isProduction = process.env.NODE_ENV === 'production';
const config = {
// webpack config
};
module.exports = () => {
if (isProduction) {
config.mode = 'production';
} else {
config.mode = 'development';
}
return config;
}
配置 entry
,output
,devtool
, devServer
,resolve
{
devtool: isProduction ? false : 'inline-source-map',
entry: {
app: [
'./src/index.tsx',
],
},
output: {
path: path.resolve(__dirname, 'dist'),
// pathinfo: true,
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js',
publicPath: '/',
},
devServer: {
open: true,
host: 'localhost',
port: 3000,
hot: true, // 热更新
historyApiFallback: true,
},
resolve: {
extensions: ['.tsx', '.ts', '.jsx', '.js', '...'],
alias: {
'@': path.join(__dirname, 'src'),
},
}
}
在入口文件 src/index.js
尾部添加:
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
// 支持热更新功能
module?.hot?.accept();
为防止 ts 报错,安装 @types/webpack-env
yarn add -D @types/webpack-env
配置 package.json
中的 scripts
字段,添加start、build、lint等命令
"scripts": {
"start": "NODE_ENV=development webpack serve",
"build": "webpack --mode=production --node-env=production",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
},
创建应用入口html,同时自动添加 bundle.js
文件,通过 yarn add -D html-webpack-plugin
命令安装,基本配置如下:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const config = {
plugins: [
new HtmlWebpackPlugin({
template: path.resolve('public/index.html'),
filename: 'index.html',
minify: true,
inject: true,
title: 'Webpack App',
}),
]
}
为项目添加 process.env
环境变量,通过 yarn add -D dotenv-webpack
命令安装,基本配置如下:
const Dotenv = require('dotenv-webpack');
const config = {
plugins: [
new Dotenv({
path: path.join(__dirname, `.env.${process.env.NODE_ENV}`),
safe: true,
// hide any errors
silent: true,
// load all the predefined 'process.env' variables which will trump anything local per dotenv specs.
systemvars: true,
// Allows your variables to be "expanded" for reusability within your .env file.
expand: true,
// allow empty variables (e.g. `FOO=`) (treat it as empty string, rather than missing)
allowEmptyValues: true,
// load '.env.defaults' as the default values if empty.
defaults: path.join(__dirname, '.env.defaults'),
}),
]
}
webpack 5.20.0+ 版本中通过如下配置,可替换 clean-webpack-plugin 插件功能。
module.exports = {
//...
output: {
clean: true, // 在生成文件之前清空 output 目录
},
};
将CSS提取到单独的CSS文件中,通过 yarn add -D mini-css-extract-plugin
安装,基本配置如下:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
...
module.exports = () => {
if (isProduction) {
config.mode = 'production';
config.plugins.push(new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
chunkFilename: '[id].[contenthash].css',
}));
} else {
config.mode = 'development';
}
return config;
};
css压缩优化,通过 yarn add -D css-minimizer-webpack-plugin
安装,基本配置如下:
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const config = {
optimization: {
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
// `...`,
new CssMinimizerPlugin(),
],
},
};
terser-webpack-plugin
内部封装了 terser
库,用于处理 js
的压缩和混淆,通过 webpack plugin
的方式对代码进行处理。
webpack v5
开箱即带有最新版本的 terser-webpack-plugin
。如果你使用的是 webpack v5
或更高版本,同时希望自定义配置,那么仍需要安装 terser-webpack-plugin
。如果使用 webpack v4
,则必须安装 terser-webpack-plugin v4
的版本。
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
optimization: {
// 告知 webpack 使用 TerserPlugin 或其它在 optimization.minimizer定义的插件压缩 bundle。
minimize: true,
// 允许你通过提供一个或多个定制过的 TerserPlugin 实例,覆盖默认压缩工具(minimizer)。
minimizer: [new TerserPlugin()],
},
};
在打包构建过程中输出当前的进度信息
const webpack = require('webpack');
const config = {
plugins: [
new webpack.ProgressPlugin({
activeModules: false,
entries: true,
handler(percentage, message, ...args) {
// custom logic
},
modules: true,
modulesCount: 5000,
profile: false,
dependencies: true,
dependenciesCount: 10000,
percentBy: null,
})
]
}
去除无用样式,通过 `yarn add -D purgecss-webpack-plugin`安装,基本配置如下:
const glob = require('glob');
const { PurgeCSSPlugin } = require('purgecss-webpack-plugin');
const config = {
plugins: [
new PurgeCSSPlugin({
paths: glob.sync(`${path.resolve(__dirname, './src')}/**/*.{tsx,scss,less,css}`, { nodir: true }),
whitelist: ['html', 'body']
}),
]
}
打包分析工具,通过 `yarn add -D webpack-bundle-analyzer`安装,基本配置如下:
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const config = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static',
analyzerHost: '127.0.0.1',
analyzerPort: 8888,
}),
]
}
本插件实现了对 source map 生成内容进行更细粒度的控制。它也可以根据 devtool 配置选项的某些设置来自动启用。
module.exports = (conf) => {
console.log('conf', conf, process.env.NODE_ENV)
if (isProduction) {
config.mode = 'production';
config.plugins.push(new webpack.SourceMapDevToolPlugin({
test: /\.(tsx|jsx|js)$/,
filename: '[file].map',
publicPath: '/',
}));
} else {
config.mode = 'development';
}
return config;
}
将 TypeScript
转化为 JavaScript
,通过 yarn add -D ts-loader
安装,基本配置如下:
const config = {
module: {
rules: [
{
// test: /\.ts(x?)$/,
test: /\.(ts|tsx)$/i,
use: [{
loader: 'ts-loader',
options: {
// 跳过ts类型检查
transpileOnly: true,
},
}],
exclude: ['/node_modules/'],
},
]
}
}
esbuild-loader
是一个构建在 esbuild 上的 webpack loader,且可以替代 babel-loader
或 ts-loader
来提高构建速度
const config = {
module: {
rules: [
{
// Match js, jsx, ts & tsx files
test: /\.[jt]sx?$/,
loader: 'esbuild-loader',
options: {
// JavaScript version to compile to
target: 'es2015'
}
},
],
},
}
esbuild-loader
可替换 TerserPlugin 和 CssMinimizerPlugin
const { EsbuildPlugin } = require('esbuild-loader')
const config = {
optimization: {
minimizer: [
new EsbuildPlugin({
target: 'es2015' // Syntax to compile to (see options below for possible values)
})
]
},
}
-
style-loader 将js文件中引入的css插入到html模板文件
-
mini-css-extract-plugin 和
style-loader
功能一样,只是打包后会单独生成 css 文件而非直接写在 html 文件中,用于生产环境,开发环境不需要另外生成文件 -
css-loader 让js文件可以通过
import
或require
等命令导入css代码 -
sass-loader 将sass代码转换为css代码
-
less-loader 将less代码转换为css代码
-
postcss-loader 处理css代码,因为是处理css,所以postcss-loader要放在sass-loader等之后,可以在
webpack.config.js
中直接进行配置,或在postcss.config.js
中进行配置,postcss-loader会自动加载该配置文件。 -
postcss-preset-env 将最新的css语法转换为目标环境浏览器能够理解的语法,新版本已内置autoprefixer功能
postcss.config.js
module.exports = {
plugins: [
[
"postcss-preset-env",
{
// Options
},
],
],
};
通过下面命令批量安装所需的css相关loader
yarn add -D style-loader css-loader sass-loader less-loader postcss-loader postcss-preset-env postcss sass
css 相关loader配置如下:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const isProduction = process.env.NODE_ENV === 'production';
const stylesHandler = isProduction ? MiniCssExtractPlugin.loader : 'style-loader';
const config = {
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [stylesHandler, 'css-loader', 'postcss-loader', 'sass-loader'],
},
{
test: /\.less$/,
use: [stylesHandler, 'css-loader', 'postcss-loader',
{
loader: 'less-loader',
options: {
lessOptions: {
modifyVars: {
'primary-color': '#0080FF',
},
javascriptEnabled: true,
math: 'always',
},
},
},
],
},
{
test: /\.css$/i,
use: [stylesHandler, 'css-loader', 'postcss-loader'],
},
]
}
}
在 webpack 5 之前,通常使用:
-
raw-loader 将文件导入为字符串
-
url-loader 将文件作为 data URI 内联到 bundle 中
-
file-loader 将文件发送到输出目录
const config = {
module: {
rules: [
{
test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
type: 'asset',
},
]
}
}
缓存生成的 webpack 模块和 chunk,来改善构建速度。cache 会在开发 模式被设置成 type: 'memory' 而且在 生产 模式 中被禁用。
const config = {
cache: {
type: 'filesystem',
buildDependencies: {
// This makes all dependencies of this file - build dependencies
config: [__filename],
// 默认情况下 webpack 与 loader 是构建依赖。
},
}
}
externals
配置项提供了阻止将某些 import 的包(package)打包到 bundle
中的功能,在运行时(runtime)再从外部获取这些扩展依赖(external dependencies)
root = true
[*] # 匹配全部文件
charset = utf-8 # 设置字符集
indent_style = space # 缩进风格,可选 space、tab
indent_size = 2 # 缩进的空格数
end_of_line = lf # 结尾换行符,可选 lf、cr、crlf
insert_final_newline = true # 在文件结尾插入新行
trim_trailing_whitespace = true # 删除一行中的前后空格
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab
代码格式化工具,目前总共有23个配置项。通过 yarn add -D prettier
安装
在项目根目录下新建 .prettierrc.js
module.exports = {
// 每行代码长度 默认80
printWidth: 100,
// 每个tab相当于多少个空格 默认2
tabWidth: 2,
// 是否使用tab进行缩进 默认false
useTabs: false,
// 声明结尾使用分号 默认true
semi: true,
// 使用单引号 默认false
singleQuote: true,
/**
* 对象属性的引号使用
*
* "as-needed" 仅在需要时在对象属性周围添加引号。
* "consistent" 如果对象中至少有一个属性需要引号,请引用所有属性。
* "preserve" 保留用户输入的情况
*/
quoteProps: 'as-needed',
// 在JSX中使用单引号而不是双引号,默认值为false
jsxSingleQuote: true,
// 在对象或数组最后一个元素后面是否加逗号(在ES5中加尾逗号)
trailingComma: 'es5',
// 字面量对象括号中的空格,默认值为true
bracketSpacing: true,
/**
* 多行JSX中的 > 放置在最后一行的结尾,而不是另起一行 默认值为false
*
* false:
* <button
* className="prettier-class”
* id="prettier-id”
* onClick={this.handleClick}
* >
* Click Here
* </button>
*
* true:
* <button
* className="prettier-class”
* id="prettier-id”
* onClick={this.handleClick}>
* Click Here
* </button>
*/
jsxBracketSameLine: false,
/**
* 箭头函数参数括号 默认avoid 可选 avoid always
* avoid 能省略括号的时候就省略 例如x => x
* always 总是有括号
*/
arrowParens: 'avoid',
// 格式化文件中某一段代码,默认格式化整个文件
rangeStart: 0,
rangeEnd: Infinity,
// 格式化的解析器,默认值为babylon(until v1.13.0)
parser: 'babylon',
/**
* 指定要使用的文件名,以推断要使用哪个解析器。
* 该选项仅在CLI和API中有用。在配置文件中使用它没有意义。
*/
filepath: '',
/**
* Prettier可以限制自己只格式化在文件顶部包含特殊注释(称为pragma)的文件。
* 这在逐渐将大型、未格式化的代码库转换为更漂亮的代码库时非常有用。
*
* @prettier
*/
requirePragma: false,
/**
* Prettier可以在文件顶部插入一个特殊的@format标记,指定该文件已使用Prettier进行格式化。
* 当与——require-pragma选项一起使用时,效果很好。
* 如果在文件的顶部已经有一个文档块,那么这个选项将添加一个带有@format标记的换行符。
*
* @prettier
*/
insertPragma: false,
/**
* "always" - Wrap prose if it exceeds the print width.
* "never" - Un-wrap each block of prose into one line.
* "preserve" - Do nothing, leave prose as-is. First available in v1.9.0
*/
proseWrap: 'preserve',
/**
* "css" - 遵守CSS display 属性的默认值
* "strict" - 空格被认为是敏感的
* "ignore" - 空格被认为是不敏感的
*/
htmlWhitespaceSensitivity: 'ignore',
// 是否缩进Vue文件中的脚本和样式标签 默认值为false
vueIndentScriptAndStyle: false,
/**
* 设置统一的行结尾样式(适用于v1.15.0+) 默认值为lf
*
* "lf" – 仅换行(\ n),在Linux和macOS以及git repos内部通用
* "crlf" - 回车符+换行符(\ r \ n),在Windows上很常见
* "cr" - 仅回车符(\ r),很少使用
* "auto" - 保持现有的行尾(通过查看第一行后的内容对一个文件中的混合值进行归一化)
*/
endOfLine: 'lf',
/**
* 控制Prettier是否格式化文件中嵌入的引用代码。
*
* "auto" - 如果pretty可以自动识别,则格式化嵌入的代码。
* "off" - 永远不要自动格式化嵌入代码。
*/
embeddedLanguageFormatting: 'auto',
// 在HTML、Vue和JSX中是否强制每行使用单个属性。默认值为false
singleAttributePerLine: false
};
如果配置了 prettier
注意配置规则与eslint保持一致,或者仅配置eslint规则即可。
在不同的前端工具之间共用目标浏览器和 Node
版本的配置文件。它主要被以下工具使用:Autoprefixer,Babel,post-preset-env,eslint-plugin-compat,stylelint-unsupported-browser-features,postcss-normalize 。
在 package.json
中配置:
{
"browserslist": [
"> 0.25%",
"not dead",
"ie 10",
"chrome 45",
"ios 9",
"android 4.4",
]
}
在 .browserslistrc
中配置:
# 默认值
> 0.5%
last 2 versions
Firefox ESR
not dead
# 快速初始化一个eslint配置
npm init @eslint/config
npx install-peerdeps --dev eslint-config-airbnb
# 等同于运行下面命令
yarn add [email protected] eslint@^8.2.0 eslint-plugin-import@^2.25.3 eslint-plugin-jsx-a11y@^6.5.1 eslint-plugin-react@^7.28.0 eslint-plugin-react-hooks@^4.3.0 --dev
yarn add eslint-config-airbnb-typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser --dev
-
eslint
-
eslint-config-airbnb
-
eslint-plugin-import
-
eslint-plugin-jsx-a11y
-
eslint-plugin-react
-
eslint-plugin-react-hooks
-
@typescript-eslint/eslint-plugin
-
@typescript-eslint/parser
在项目根目录下新建 .eslintrc.cjs
文件
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
env: { browser: true, es2020: true, node: true, },
parser: '@typescript-eslint/parser',
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
plugins: ['react-refresh'],
rules: {
// 使用2个空格缩进
'indent': ['error', 2, { SwitchCase: 1 }],
},
}
当保存代码时,按照eslint设置的规则自动修复代码,配置如下:
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
}