一、初始化项目
先手动初始化一个react + ts 项目。新建文件夹template_react_ts,在项目中翻开终端。履行:
npm init -y
指令履行之后能够看到文件夹的根目录多了一个package.json
文件,初始化好package.json
后,在项目下新增以下所示目录结构和文件
├── build
| ├── webpack.base.js # 公共装备
| ├── webpack.dev.js # 开发环境装备
| └── webpack.prod.js # 打包环境装备
├── public
│ └── index.html # html模板
├── src
| ├── App.tsx
│ └── index.tsx # react运用进口页面
├── tsconfig.json # ts装备
└── package.json
1、装置webpack依靠
npm i webpack webpack-cli -D
or
yarn add webpack webpack-cli -D
2、装置react依靠
npm i react react-dom -S
or
yarn add react react-dom -S
3、装置react类型依靠
npm i @types/react @types/react-dom -D
or
yarn add @types/react @types/react-dom -D
4、添加public/index.html内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack5-react-ts</title>
</head>
<body>
<!-- 容器节点 -->
<div id="root"></div>
</body>
</html>
5、添加tsconfig.json内容
{
"compilerOptions": {
"target": "es5",
// 指定要包含在编译中的 library
"lib": [
"dom",
"dom.iterable",
"esnext"
],
// 答应 ts 编译器编译 js 文件
"allowJs": true,
// 越过类型声明文件的类型查看
"skipLibCheck": true,
// es 模块 互操作,屏蔽 ESModule 和 CommonJS 之间的差异
"esModuleInterop": true,
// 答应经过 import x from 'y' 即便模块没有显式指定 default 导出
"allowSyntheticDefaultImports": true,
// 敞开严厉方法
"strict": true,
// 对文件称号强制区分大小写
"forceConsistentCasingInFileNames": true,
// 为 switch 句子启用过错陈述
"noFallthroughCasesInSwitch": true,
// 生成代码的模块化标准
"module": "esnext",
// 模块解析(查找)策略
"moduleResolution": "node",
// 答应导入扩展名为.json的模块
"resolveJsonModule": true,
// 是否将没有 import/export 的文件视为旧(大局而非模块化)脚本文件
"isolatedModules": true,
// 编译时不生成任何JS文件(只进行类型查看)
"noEmit": true,
// 指定将 JSX 编译成什么方法
"jsx": "react-jsx"
},
// 指定答应ts处理的文件目录
"include": [
"src"
]
}
6、添加src/App.tsx内容
import React from 'react'
function App() {
return (
<div>
<h2>template_react_ts</h2>
</div>
)
}
export default App
7、添加 src/index.tsx 内容
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
root.render(
<React.StrictMode>
<App />,
</React.StrictMode>,
);
到此项目事务代码现已添加好了,接下来就要开始装备webpack
的代码
二、装备根底版React+ts环境
1、webpack公共装备
修正webpack.base.js
装备进口文件
// webpack.base.js
const path = require('path')
module.exports = {
entry: path.join(__dirname, '../src/index.tsx'), // 进口文件
}
装备出口文件
// webpack.base.js
const path = require("path")
module.exports = {
......
// 打包文件出口
output: {
filename: 'static/js/[name].js', // 每个输出js的称号
path: path.join(__dirname, '../dist'), // 打包成果输出途径
clean: true, // webpack4需求装备clean-webpack-plugin来删去dist文件,webpack5内置了
publicPath: '/' // 打包后文件的公共前缀途径
},
}
装备loader解析ts和jsx
webpack
默许只能辨认js文件,不能辨认jsx
和ts
语法,需求装备loader的预设@babel/preset-typescript
来先将TS
语法转化成JS
语法,再经过@babel/preset-react
来辨认jsx语法。
装置babel
中心模块和preset
预设:
npm i babel-loader @babel/core @babel/preset-react @babel/preset-typescript -D
or
yarn add babel-loader @babel/core @babel/preset-react @babel/preset-typescript -D
在webpack.base.js
中添加module.rules
装备:
// webpack.base.js
module.exports = {
// ...
module: {
rules: [
{
test: /.(ts|tsx)$/, // 匹配.ts, tsx文件
use: {
loader: 'babel-loader',
options: {
// 预设履行次序由右往左,所以先处理ts,再处理jsx
presets: [
'@babel/preset-react',
'@babel/preset-typescript'
]
}
}
}
]
}
}
装备extensions
extebsions
是webpack的resolve
解析装备下的选项,在==引进模块时不带入文件后缀==的时分,会在该装备数组中依次添加后缀查找文件。因为ts不支撑引进以.ts
、.tsx
为后缀的文件,所以要在extensions
中要装备,在许多第三方库中里边许多引进js文件且没有带后缀,所以也要装备下js。
修正webpack.base.js
,注意要把高频呈现的文件后缀放在前面:
// webpack.base.js
module.exports = {
// ...
resolve: {
extensions: ['.jsx', '.js', '.tsx', '.ts'],
}
}
这儿只装备了js、tsx、ts,其他引进的文件都要求带后缀,这样能够提高构建的速度
添加 html-webpack-plugin
插件
webpack
需求把终究构建好的静态资源都引进到HTML
文件中,这样才能在浏览器中运转,html-webpack-plugin
插件便是用来做这个的,首要装置依靠:
npm i html-webpack-plugin -D
or
yarn add html-webpack-plugin -D
因为html-webpack-plugin
在开发和构建打包方法都会用到,所以仍是在公共装备文件webpack.base.js里边装备
// webpack.base.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html'), // 模板取界说root节点的模板
inject: true, // 主动注入静态资源
})
]
}
到这儿一个最根底的react
根本公共装备就现已装备好了,可是项目开发是需求区分隔发环境和打包装备,因而需求在此根底上分别装备开发环境和打包环境。
2、webpack开发环境下的装备
装置webpack-dev-server
发环境装备代码在webpack.dev.js
中,需求凭借webpack-dev-server在开发环境发动服务器来辅佐开发,还需求依靠webpack-merge来兼并根本装备,装置依靠:
npm i webpack-dev-server webpack-merge -D
or
yarn add webpack-dev-server webpack-merge -D
修正webpack.dev.js
代码, 兼并公共装备,并添加开发方法装备
// webpack.dev.js
const path = require('path')
const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.base.js')
// 兼并公共装备,并添加开发环境装备
module.exports = merge(baseConfig, {
mode: 'development', // 开发方法,打包更加快速,省了代码优化过程
devtool: 'eval-cheap-module-source-map', // 源码调试方法,后边会讲
devServer: {
port: 3000, // 服务端口号
compress: false, // gzip紧缩,开发环境不敞开,提高热更新速度
hot: true, // 敞开热更新,后边会讲react模块热替换详细装备
historyApiFallback: true, // 处理history路由404问题
static: {
directory: path.join(__dirname, "../public"), //保管静态资源public文件夹
},
}
})
package.json
添加dev脚本
在package.json
的scripts
中添加
// package.json
"scripts": {
"start": "webpack-dev-server -c build/webpack.dev.js"
},
履行npm run start
,就能看到项目现已发动起来了,拜访http://localhost:3000/,就能够看到项目界面,详细完善的react
模块热替换鄙人面会讲到。
3、webpack打包环境装备
修正webpack.prod.js代码
// webpack.prod.js
const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.base.js')
module.exports = merge(baseConfig, {
mode: 'production', // 出产方法,会敞开tree-shaking和紧缩代码,以及其他优化
})
package.json
添加build打包指令脚本
// package.json
"scripts": {
"build": "webpack -c build/webpack.prod.js"
},
履行npm run build
,终究打包在dist
文件中, 打包成果:
dist
├── static
| ├── js
| ├── main.js
├── index.html
打包之后的dist
能够在本地凭借node
服务器serve
翻开,先大局装置serve
依靠
nom i serve -g
or
yarn add serve -g
然后在项目根目录指令行履行serve -s dist
,就能够发动打包后的项目了。
到现在一个根底的支撑react
和ts
的webpack5
根底项目就现已装备好了,但实践项目开发过程中这些装备是远远不够的,因而咱们还需求装置更多装备;
三、根底功用装备
1、装备环境变量
环境变量按作用来分分两种
- 区分是开发方法仍是打包构建方法
- 区分项目事务环境,开发/测验/预测/正式环境
区分隔发方法仍是打包构建方法能够用process.env.NODE_ENV
,因为许多第三方包里边判别都是选用的这个环境变量。
区分项目接口环境能够自界说一个环境变量process.env.BASE_ENV
,设置环境变量能够凭借cross-env和webpack.DefinePlugin来设置。
-
cross-env
:兼容各体系的设置环境变量的包 -
webpack.DefinePlugin
:webpack
内置的插件,能够为事务代码注入环境变量
装置cross-env
依靠
npm i cross-env -D
or
yarn add cross-env -D
修正package.json
的scripts
脚本字段,删去原先的start
和build
,改为
"scripts": {
"start:dev": "cross-env NODE_ENV=development BASE_ENV=development webpack-dev-server -c build/webpack.dev.js",
"start:pre": "cross-env NODE_ENV=development BASE_ENV=preRelease webpack-dev-server -c build/webpack.dev.js",
"start:prod": "cross-env NODE_ENV=development BASE_ENV=production webpack-dev-server -c build/webpack.dev.js",
"build:dev": "cross-env NODE_ENV=production BASE_ENV=development webpack -c build/webpack.prod.js",
"build:pre": "cross-env NODE_ENV=production BASE_ENV=preRelease webpack -c build/webpack.prod.js",
"build:prod": "cross-env NODE_ENV=production BASE_ENV=production webpack -c build/webpack.prod.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
process.env.NODE_ENV
环境变量webpack
会主动依据设置的mode
字段来给事务代码注入对应的development
和prodction
,这儿在指令中再次设置环境变量NODE_ENV
是为了在webpack
和babel
的装备文件中拜访到。
能够在webpack.base.js
里边打印一下NODE_ENV
和BASE_ENV
两个变量
// webpack.base.js
// ...
console.log('NODE_ENV', process.env.NODE_ENV)
console.log('BASE_ENV', process.env.BASE_ENV)
履行yarn start:dev
之后能够看到打印如下:
// NODE_ENV development
// BASE_ENV development
当时是开发方法,事务环境是开发环境,这儿需求把process.env.BASE_ENV
注入到事务代码里边,就能够经过该环境变量设置对应环境的接口地址和其他数据,要凭借webpack.DefinePlugin
插件。
修正webpack.base.js
// webpack.base.js
// ...
const webpack = require('webpack')
module.export = {
// ...
plugins: [
// ...
new webpack.DefinePlugin({
'process.env.BASE_ENV': JSON.stringify(process.env.BASE_ENV)
})
]
}
装备后会把值注入到事务代码里边去,webpack
解析代码匹配到process.env.BASE_ENV
,就会设置到对应的值。测验一下,在src/index.tsx
打印一下两个环境变量
// src/index.tsx
// ...
console.log('NODE_ENV', process.env.NODE_ENV)
console.log('BASE_ENV', process.env.BASE_ENV)
2、处理css和less文件
在前端开发项意图时分别的一个重要的装备便是css
和less
文件处理,webpack
默许只知道js
,是不辨认css
文件的,所以需求装置依靠来处理css
和less
文件,不然当进口文件index.jsx
引进css
文件时,运转yarn start:dev
操控台就会报错,因而需求运用loader
来解析css
;
3、装置依靠
npm i style-loader css-loader -D
or
yarn add style-loader css-loader -D
-
style-loader
: 把解析后的css
代码从js
中抽离,放到头部的style
标签中(在运转时做的) -
css-loader:
解析css
文件代码
因为解析css
的装备开发和打包环境都会用到,所以在公共装备webpack.base.js
中添加装备
// webpack.base.js
// ...
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /.css$/, //匹配 css 文件
use: ['style-loader','css-loader']
}
]
},
// ...
}
loader
履行次序是从右往左,从下往上的,匹配到css
文件后先用css-loader
解析css
, 终究凭借style-loader
把css
刺进到头部style
标签中。
装备完结后再yarn start:dev
打包发动后在浏览器查看,这时能够看到操控台不报错了,而且款式收效了。
4、支撑less或scss
项目开发中,为了更好的提高开发体验,一般会运用css
超集less
或许scss
,关于这些超集也需求对应的loader
来辨认解析,咱们以less
为例,首要装置解析less
的相关依靠:
npm i less-loader less -D
or
yarn add less-loader less -D
假设是运用scss的话装置以下依靠
npm i node-sass sass-loader -D
or
yarn add node-sass sass-loader -D
-
less-loader
: 解析less
文件代码,把less
编译为css
-
less
:less
中心依靠
完结支撑less
也很简单,只需求在rules
中添加less
文件解析,遇到less
文件,运用less-loader
解析为css
,再进行css
解析流程,修正webpack.base.js
(ps: 假设是scss
的话运用sass-loader
):
// webpack.base.js
module.exports = {
// ...
module: {
// ...
rules: [
// ...
{
test: /.(css|less)$/, //匹配 css和less 文件
use: ['style-loader','css-loader', 'less-loader']
}
]
},
// ...
}
测验一下,新增src/app.less
#root {
h2 {
font-size: 20px;
}
}
在App.tsx
中引进app.less
,履行yarn start:dev
发动之后,能够看到less
文件编写的款式编译css
后也刺进到style
标签了。
5、处理css3前缀兼容
尽管css3
现在浏览器支撑率现已很高了, 但有时分需求兼容一些低版别浏览器,需求给css3
加前缀,能够凭借插件来主动加前缀,postcss-loader便是来给css3
加浏览器前缀的,装置依靠:
npm i postcss-loader autoprefixer -D
or
yarn add postcss-loader autoprefixer -D
-
postcss-loader
:处理css
时主动加前缀 -
autoprefixer
:决议添加哪些浏览器前缀到css
中
修正webpack.base.js
, 在解析css
和less
的规矩中添加装备:
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /.(css|less)$/, //匹配 css和less 文件
use: [
'style-loader',
'css-loader',
// 新增
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['autoprefixer']
}
}
},
'less-loader'
]
}
]
},
// ...
}
装备完结后,需求有一份要兼容浏览器的清单,让postcss-loader
知道要加哪些浏览器的前缀,在根目录创立 .browserslistrc
文件,在文件内添加以下内容:
IE 9 # 兼容IE 9
chrome 35 # 兼容chrome 35
以兼容到ie9
和chrome35
版别为例,装备好后,履行yarn start:dev
发动之后,能够看到打包后的css
文件现已加上了ie
和谷歌内核
的前缀;
上面能够看到解析css
和less
有许多重复装备,能够进行提取postcss-loader
装备优化一下
postcss.config.js
是postcss-loader
的装备文件,会主动读取装备,根目录新建postcss.config.js
:
module.exports = {
plugins: ['autoprefixer']
}
修正webpack.base.js
, 撤销postcss-loader
的options
装备
// webpack.base.js
// ...
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /.(css|less)$/, //匹配 css和less 文件
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
]
},
// ...
}
6、babel预设处理js兼容
现在js不断新增许多便利好用的标准语法来便利开发,甚至还有非标准语法比方装修器,都极大的提高了代码可读性和开发功率。但前者标准语法许多低版别浏览器不支撑,后者非标准语法一切的浏览器都不支撑。需求把最新的标准语法转化为低版别语法,把非标准语法转化为标准语法才能让浏览器辨认解析,而babel便是来做这件事的,这儿只讲装备,更详细的能够看Babel 那些事儿。
装置依靠
npm i babel-loader @babel/core @babel/preset-env core-js -D
or
yarn add babel-loader @babel/core @babel/preset-env core-js -D
-
babel-loader: 运用
babel
加载最新js代码并将其转化为ES5
(上面现已装置过) -
@babel/corer:
babel
编译的中心包 -
@babel/preset-env:
babel
编译的预设,能够转化现在最新的js
标准语法 -
core-js: 运用低版别
js
语法模拟高版别的库,也便是垫片
修正webpack.base.js
// webpack.base.js
module.exports = {
// ...
module: {
rules: [
{
test: /.(ts|tsx)$/,
use: {
loader: 'babel-loader',
options: {
// 履行次序由右往左,所以先处理ts,再处理jsx,终究再试一下babel转化为低版别语法
presets: [
[
"@babel/preset-env",
{
// 设置兼容方针浏览器版别,这儿能够不写,babel-loader会主动寻觅上面装备好的文件.browserslistrc
// "targets": {
// "chrome": 35,
// "ie": 9
// },
"useBuiltIns": "usage", // 依据装备的浏览器兼容,以及代码中运用到的api进行引进polyfill按需添加
"corejs": 3, // 装备运用core-js低版别
}
],
'@babel/preset-react',
'@babel/preset-typescript'
]
}
}
}
]
}
}
此刻再打包就会把语法转化为对应浏览器兼容的语法了,为了防止webpack
装备文件过于庞大,能够把babel-loader
的装备抽离出来, 在项意图根目录新建babel.config.js
文件,运用js
作为装备文件,是因为能够拜访到process.env.NODE_ENV
环境变量来区分是开发仍是打包方法。
// babel.config.js
module.exports = {
// 履行次序由右往左,所以先处理ts,再处理jsx,终究再试一下babel转化为低版别语法
"presets": [
[
"@babel/preset-env",
{
// 设置兼容方针浏览器版别,这儿能够不写,babel-loader会主动寻觅上面装备好的文件.browserslistrc
// "targets": {
// "chrome": 35,
// "ie": 9
// },
"useBuiltIns": "usage", // 依据装备的浏览器兼容,以及代码中运用到的api进行引进polyfill按需添加
"corejs": 3 // 装备运用core-js运用的版别
}
],
"@babel/preset-react",
"@babel/preset-typescript"
]
}
移除webpack.base.js
中babel-loader
的options
装备,修正后内容如下:
// webpack.base.js
module.exports = {
// ...
module: {
rules: [
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
use: 'babel-loader'
},
// ...
]
}
}
7、babel处理js非标准语法
现在react
干流开发都是函数组件和react-hooks
,但有时也会用类组件,能够用装修器简化代码。
新增src/components/Class.tsx
组件, 在App.tsx
中引进该组件运用
import React, { PureComponent } from "react";
// 装修器为,组件添加age特点
function addAge(Target: Function) {
Target.prototype.age = 111
}
// 运用装修圈
@addAge
class Class extends PureComponent {
age?: number
render() {
return (
<h2>我是类组件---{this.age}</h2>
)
}
}
export default Class
需求敞开一下ts
装修器支撑,修正tsconfig.json
文件
// tsconfig.json
{
"compilerOptions": {
// ...
// 敞开装修器运用
"experimentalDecorators": true
}
}
上面Class组件代码中运用了装修器,现在js
标准语法是不支撑的,现在运转或许打包会报错,不辨认装修器语法,需求凭借babel-loader
插件,装置以下依靠:
npm i @babel/plugin-proposal-decorators -D
or
yarn add @babel/plugin-proposal-decorators -D
在babel.config.js
中添加插件
module.exports = {
// ...
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }]
]
}
8、仿制public文件夹内容
一般public
文件夹都会放一些静态资源,能够直接依据绝对途径引进,比方图片
,css
,js
文件等,不需求webpack
进行解析,只需求打包的时分把public
下内容仿制到构建出口文件夹中,能够凭借copy-webpack-plugin插件,装置依靠:
npm i copy-webpack-plugin -D
or
yarn add copy-webpack-plugin -D
开发环境现已在devServer
中装备了static
保管了public
文件夹,在开发环境运用绝对途径能够拜访到public
下的文件,但打包构建时不做处理睬拜访不到,所以现在需求在打包装备文件webpack.prod.js
中新增copy
插件装备。
// webpack.prod.js
// ..
const path = require('path')
const CopyPlugin = require('copy-webpack-plugin');
module.exports = merge(baseConfig, {
mode: 'production',
plugins: [
// 仿制文件插件
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, '../public'), // 仿制public下文件
to: path.resolve(__dirname, '../dist'), // 仿制到dist目录中
filter: source => {
return !source.includes('index.html') // 疏忽index.html
}
},
],
}),
]
})
在上面的装备中,疏忽了index.html
,因为html-webpack-plugin
会以public
下的index.html
为模板生成一个index.html
到dist
文件下,所以不需求再额外装备仿制该文件了。
html-webpack-plugin
装备好之后,能够在public
中新增一个favicon.ico
图标文件,在index.html
中引进
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- 绝对途径引进图标文件 -->
<link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>template_react_ts</title>
</head>
<body>
<!-- 容器节点 -->
<div id="root"></div>
</body>
</html>
再履行yarn build:dev
打包,就能够看到public
下的favicon.ico
图标文件被仿制到dist
文件中了。
9、图片文件处理
关于图片文件,webpack4
运用file-loader
和url-loader
来处理的,但webpack5
不运用这两个loader
了,而是选用自带的asset-module来处理;
修正webpack.base.js
,添加图片解析装备
module.exports = {
module: {
rules: [
// ...
{
test:/.(png|jpg|jpeg|gif|svg)$/, // 匹配图片文件
type: "asset", // type挑选asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转base64位
}
},
generator:{
filename:'static/images/[name][ext]', // 文件输出目录和命名
},
},
]
}
}
测验一下,预备一张小于10kb的图片和大于10kb的图片,放在src/assets/imgs
目录下, 修正App.tsx
:
import React from 'react'
import smallImg from './assets/imgs/5kb.png'
import bigImg from './assets/imgs/20kb.png'
import './app.css'
import './app.less'
function App() {
return (
<>
<img src={smallImg} alt="小于10kb的图片" />
<img src={bigImg} alt="大于于10kb的图片" />
</>
)
}
export default App
这个时分在引进图片的当地会报:
找不到模块“./assets/imgs/20kb.png”或其相应的类型声明
,这是因为在ts
项目内引进图片是需求添加一个静态文件的声明文件来着
新增src/index.d.ts
文件,添加内容
declare module '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
declare module '*.tiff'
declare module '*.less'
declare module '*.css'
添加图片声明文件后,就能够正常引进图片了, 然后履行yarn build:dev
打包,凭借serve -s dist查看作用,能够看到能够正常解析图片了,而且小于10kb的图片被转成了base64位格局的。
10、处理字体和媒体文件
字体文件和媒体文件这两种资源处理方法和处理图片是相同的,只需求把匹配的途径和打包后放置的途径修正一下就能够了。修正webpack.base.js
文件:
// webpack.base.js
module.exports = {
module: {
rules: [
// ...
{
test:/.(woff2?|eot|ttf|otf)$/, // 匹配字体图标文件
type: "asset", // type挑选asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转base64位
}
},
generator:{
filename:'static/fonts/[name][ext]', // 文件输出目录和命名
},
},
{
test:/.(mp4|webm|ogg|mp3|wav|flac|aac)$/, // 匹配媒体文件
type: "asset", // type挑选asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转base64位
}
},
generator:{
filename:'static/media/[name][ext]', // 文件输出目录和命名
},
},
]
}
}
四、装备react模块热更新
热更新上面现已在devServer
中装备hot
为true
, 在webpack4
中,还需求在插件中添加了HotModuleReplacementPlugin
,在webpack5
中,只需devServer.hot
为true
了,该插件就现已内置了。
现在开发方法下修正css
和less
文件,页面款式能够在不改写浏览器的状况实时收效,因为此刻款式都在style
标签里边,style-loader
做了替换款式的热替换功用。可是修正App.tsx
,浏览器会主动改写后再显示修正后的内容,但咱们想要的不是改写浏览器,而是在不需求改写浏览器的前提下模块热更新,而且能够保存react
组件的状况。
能够凭借@pmmmwh/react-refresh-webpack-plugin插件来完结,该插件又依靠于react-refresh, 装置依靠:
npm i @pmmmwh/react-refresh-webpack-plugin react-refresh -D
or
yarn add @pmmmwh/react-refresh-webpack-plugin react-refresh -D
装备react
热更新插件,修正webpack.dev.js
// webpack.dev.js
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = merge(baseConfig, {
// ...
plugins: [
new ReactRefreshWebpackPlugin(), // 添加热更新插件
]
})
为babel-loader
装备react-refesh
改写插件,修正babel.config.js
文件
const isDEV = process.env.NODE_ENV === 'development' // 是否是开发方法
module.exports = {
// ...
"plugins": [
isDEV && require.resolve('react-refresh/babel'), // 假设是开发方法,就发动react热更新插件
// ...
].filter(Boolean) // 过滤空值
}
新增或许删去页面
hooks
时,热更新时组件状况不会保存。
五、优化构建速度
1、构建耗时剖析
当进行优化的时分,必定要先知道时刻都花费在哪些过程上了,而speed-measure-webpack-plugin插件能够帮咱们做到,装置依靠:
npm i speed-measure-webpack-plugin -D
or
yarn add speed-measure-webpack-plugin -D
运用的时分为了不影响到正常的开发/打包方法,咱们挑选新建一个装备文件,新增webpack
构建剖析装备文件build/webpack.analy.js
并添加以下内容:
const prodConfig = require('./webpack.prod.js') // 引进打包装备
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin'); // 引进webpack打包速度剖析插件
const smp = new SpeedMeasurePlugin(); // 实例化剖析插件
const { merge } = require('webpack-merge') // 引进兼并webpack装备办法
// 运用smp.wrap办法,把出产环境装备传进去,因为后边或许会加剖析装备,所以先留出兼并空位
module.exports = smp.wrap(merge(prodConfig, {
}))
在根目录的package.json
文件里边的script
里边添加发动webpack
打包剖析脚本指令
2、敞开耐久化存储缓存
在webpack5
之前做缓存是运用babel-loader
缓存处理js
的解析成果,cache-loader
缓存css
等资源的解析成果,还有模块缓存插件hard-source-webpack-plugin
,装备好缓存后第二次打包,经过对文件做哈希对比来验证文件前后是否共同,假设共同则选用上一次的缓存,能够极大地节省时刻。
webpack5
较于 webpack4
,新增了耐久化缓存、改善缓存算法等优化,经过装备 webpack 耐久化缓存
,来缓存生成的 webpack
模块和 chunk
,改善下一次打包的构建速度,可提速 90%
左右,装备也简单,修正webpack.base.js
内容如下:
// webpack.base.js
// ...
module.exports = {
// ...
cache: {
type: 'filesystem', // 运用文件缓存
},
}
缓存的存储位置在
node_modules/.cache/webpack
,里边又区分了development
和production
缓存
3、敞开多线程loader
webpack
的loader
默许在单线程履行,现代电脑一般都有多核cpu
,能够凭借多核cpu
敞开多线程loader
解析,能够极大地提高loader
解析的速度,thread-loader便是用来敞开多进程解析loader的,装置依靠:
npm i thread-loader -D
or
yarn add thread-loader -D
运用时,需将此loader
放置在其他loader
之前。放置在此loader
之后的loader
会在一个独立的worker
池中运转。在webpack.base.js
文件内添加如下内容:
// webpack.base.js
module.exports = {
// ...
module: {
rules: [
{
test: /.(ts|tsx)$/,
use: ['thread-loader', 'babel-loader']
}
]
}
}
因为thread-loader
不支撑抽离css
插件MiniCssExtractPlugin.loader
(下面会讲),所以这儿只装备了多进程解析js
,敞开多线程也是需求发动时刻,大约600ms
左右,所以适合规模比较大的项目。
4、装备alias别号
webpack
支撑设置别号alias
,设置别号能够让后续引用的当地削减途径的复杂度。要添加别号在webpack.base.js
添加以下内容:
module.export = {
// ...
resolve: {
// ...
alias: {
'@': path.join(__dirname, '../src')
}
}
}
同时也要修正tsconfig.json
,添加baseUrl
和paths
{
"compilerOptions": {
// ...
"baseUrl": ".",
"paths": {
"@/*": [
"src/*"
]
}
}
}
装备修正完结后,在项目中运用 @/xxx.xx
,就会指向项目中src/xxx.xx
,在js/ts
文件和css
文件中都能够用。
5、缩小loader作用规模
一般第三库都是现已处理好的,不需求再次运用loader
去解析,能够依照实践状况合理装备loader
的作用规模,来削减不必要的loader
解析,节省时刻,经过运用include
和exclude
两个装备项,能够完结这个功用,常见的例如:
-
include
:只解析该选项装备的模块 -
exclude
:不解该选项装备的模块,优先级更高
修正webpack.base.js
如下内容:
// webpack.base.js
const path = require('path')
module.exports = {
// ...
module: {
rules: [
{
include: [path.resolve(__dirname, '../src')], 只对项目src文件的ts,tsx进行loader解析
test: /.(ts|tsx)$/,
use: ['thread-loader', 'babel-loader']
}
]
}
}
其他loader
也是相同的装备方法,假设除src
文件外也还有需求解析的,就把对应的目录地址加上就能够了,比方需求引进antd
的css
,能够把antd
的文件目录途径添加解析css
规矩到include
里边。
6、精确运用loader
loader
在webpack
构建过程中运用的位置是在webpack
构建模块依靠联系引进新文件时,会依据文件后缀来倒序遍历rules
数组,假设文件后缀和test
正则匹配到了,就会运用该rule
中装备的loader
依次对文件源代码进行处理,终究拿到处理后的sourceCode
成果,能够经过防止运用无用的loader
解析来提高构建速度,比方运用less-loader
解析css
文件。
能够拆分上面装备的less
和css
, 防止让less-loader
再去解析css
文件
// webpack.base.js
// ...
module.exports = {
module: {
// ...
rules: [
// ...
{ // css文件处理
test: /.(css|less)$/, //匹配 css 文件
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
]
}
}
将webpack.base.js
以上内容修正为如下内容:
// webpack.base.js
// ...
module.exports = {
module: {
// ...
rules: [
// ...
{
test: /.css$/, //匹配一切的 css 文件
include: [path.resolve(__dirname, '../src')],
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
},
{
test: /.less$/, //匹配一切的 less 文件
include: [path.resolve(__dirname, '../src')],
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
]
}
}
7、缩小模块搜索规模
node
里边模块有三种
-
node
中心模块 -
node_modules
模块 - 自界说文件模块
运用require
和import
引进模块时假设有精确的相对或许绝对途径,就会去按途径查询,假设引进的模块没有途径,会优先查询node
中心模块,假设没有找到会去当时目录下node_modules
中寻觅,假设没有找到会查从父级文件夹查找node_modules
,一直查到体系node
大局模块。
这样会有两个问题,一个是当时项目没有装置某个依靠,可是上一级目录下node_modules
或许大局模块有装置,就也会引进成功,可是布置到服务器时或许就会找不到造成报错,另一个问题便是一级一级查询比较耗费时刻。能够告知webpack
搜索目录规模,来规避这两个问题。
修正webpack.base.js
// webpack.base.js
const path = require('path')
module.exports = {
// ...
resolve: {
// ...
// 假设用的是pnpm 就暂时不要装备这个,会有鬼魂依靠的问题,拜访不到许多模块。
modules: [path.resolve(__dirname, '../node_modules')], // 查找第三方模块只在本项意图node_modules中查找
},
}
8、devtool 装备
开发过程中或许打包后的代码都是webpack
处理后的代码,假设进行调试必定期望看到源代码,而不是编译后的代码, source map便是用来做源码映射的,不同的映射方法会显着影响到构建和重新构建的速度, devtool选项便是webpack
供给的挑选源码映射方法的装备。
devtool
的命名规矩为 ^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$
关键字 | 描绘 |
---|---|
inline | 代码内经过 dataUrl 方法引进 SourceMap |
hidden | 生成 SourceMap 文件,但不运用 |
eval |
eval(...) 方法履行代码,经过 dataUrl 方法引进 SourceMap |
nosources | 不生成 SourceMap |
cheap | 只需求定位到行信息,不需求列信息 |
module | 展现源代码中的过错位置 |
开发环境推荐:eval-cheap-module-source-map
- 本地开发首次打包慢点没联系,因为
eval
缓存的原因, 热更新会很快 - 开发中,咱们每行代码不会写的太长,只需求定位到行就行,所以加上
cheap
- 咱们期望能够找到源代码的过错,而不是打包后的,所以需求加上
module
修正webpack.dev.js
// webpack.dev.js
module.exports = {
// ...
devtool: 'eval-cheap-module-source-map'
}
9、其他优化装备
除了上面的装备外,webpack
还供给了其他的一些优化方法,本次搭建没有运用到,所以只简单罗列下:
-
externals
: 外包拓宽,打包时会疏忽装备的依靠,会从上下文中寻觅对应变量; -
module.noParse
: 匹配到设置的模块,将不进行依靠解析,适合jquery
,boostrap
这类不依靠外部模块的包; -
ignorePlugin
: 能够运用正则疏忽一部分文件,常在运用多语言的包时能够把非中文语言包过滤掉;
六、优化构建成果文件
1、webpack包剖析工具
webpack-bundle-analyzer是剖析webpack
打包后文件的插件,运用交互式可缩放树形图可视化webpack
输出文件的大小。经过该插件能够对打包后的文件进行观察和剖析,能够便利咱们对不完美的当地针对性的优化,装置依靠:
npm install webpack-bundle-analyzer -D
or
yarn add webpack-bundle-analyzer -D
修正webpack.analy.js
// webpack.analy.js
const prodConfig = require('./webpack.prod.js')
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const smp = new SpeedMeasurePlugin();
const { merge } = require('webpack-merge')
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') // 引进剖析打包成果插件
module.exports = smp.wrap(merge(prodConfig, {
plugins: [
new BundleAnalyzerPlugin() // 装备剖析打包成果插件
]
}))
装备好后,履行yarn build:analy
指令,打包完结后浏览器会主动翻开窗口,能够看到打包文件的剖析成果页面,能够看到各个文件所占的资源大小。
2、抽取css款式文件
在开发环境咱们期望css
嵌入在style
标签里边,便利款式热替换,但打包时咱们期望把css
独自抽离出来,便利装备缓存策略。而插件mini-css-extract-plugin便是来帮咱们做这件事的,装置依靠:
npm i mini-css-extract-plugin -D
or
yarn add mini-css-extract-plugin -D
修正webpack.base.js
, 依据环境变量设置开发环境运用style-looader
,打包方法抽离css
// webpack.base.js
// ...
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const isDev = process.env.NODE_ENV === 'development' // 是否是开发方法
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /.css$/, //匹配一切的 css 文件
include: [path.resolve(__dirname, '../src')],
use: [
isDev ? 'style-loader' : MiniCssExtractPlugin.loader, // 开发环境运用style-looader,打包方法抽离css
'css-loader',
'postcss-loader'
]
},
{
test: /.less$/, //匹配一切的 less 文件
include: [path.resolve(__dirname, '../src')],
use: [
isDev ? 'style-loader' : MiniCssExtractPlugin.loader, // 开发环境运用style-looader,打包方法抽离css
'css-loader',
'postcss-loader',
'less-loader'
]
},
]
},
// ...
}
再修正webpack.prod.js
, 打包时添加抽离css插件
// webpack.prod.js
// ...
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = merge(baseConfig, {
mode: 'production',
plugins: [
// ...
// 抽离css插件
new MiniCssExtractPlugin({
filename: 'static/css/[name].css' // 抽离css的输出目录和称号
}),
]
})
装备完结后,在开发方法css
会嵌入到style
标签里边,便利款式热替换,打包时会把css
抽离成独自的css
文件。
3、紧缩css文件
上面装备了打包时把css
抽离为独自css
文件的装备,翻开打包后的文件查看,能够看到默许css
是没有紧缩的,需求手动装备一下紧缩css
的插件。
能够凭借css-minimizer-webpack-plugin来紧缩css,装置依靠:
npm i css-minimizer-webpack-plugin -D
or
yarn add css-minimizer-webpack-plugin -D
修正webpack.prod.js
文件, 需求在优化项optimization下的minimizer特点中装备
// webpack.prod.js
// ...
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
// ...
optimization: {
minimizer: [
new CssMinimizerPlugin(), // 紧缩css
],
},
}
这时分再履行打包指令之后能够看见css文件现已被紧缩
4、紧缩js文件
设置mode
为production
时,webpack
会运用内置插件terser-webpack-plugin紧缩js
文件,该插件默许支撑多线程紧缩,可是上面装备optimization.minimizer
紧缩css
后,js
紧缩就失效了,需求手动再添加一下,webpack
内部装置了该插件,因为pnpm
处理了鬼魂依靠问题,假设用的pnpm
的话,需求手动再装置一下依靠。
npm i terser-webpack-plugin -D
or
yarn add terser-webpack-plugin -D
修正webpack.prod.js
文件
// ...
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
// ...
optimization: {
minimizer: [
// ...
new TerserPlugin({ // 紧缩js
parallel: true, // 敞开多线程紧缩
terserOptions: {
compress: {
pure_funcs: ["console.log"] // 删去console.log
}
}
}),
],
},
}
装备完结后再打包,css
和js
就都能够被紧缩了。
5、合理装备打包文件hash
项目维护的时分,一般只会修正一部分代码,能够合理装备文件缓存,来提高前端加载页面速度和削减服务器压力,而hash
便是浏览器缓存策略很重要的一部分。webpack
打包的hash
分三种:
-
hash
:跟整个项意图构建相关,只需项目里有文件更改,整个项目构建的hash
值都会更改,而且悉数文件都共用相同的hash
值; -
chunkhash
:不同的进口文件进行依靠文件解析、构建对应的chunk
,生成对应的哈希值,文件自身修正或许依靠文件修正,chunkhash
值会改变; -
contenthash
:每个文件自己独自的hash
值,文件的改动只会影响自身的hash
值;
hash
是在输出文件时装备的,格局是filename: "[name].[chunkhash:8][ext]"
, [xx]
格局是webpack
供给的占位符, :8
是生成hash
的长度。
占位符 | 解释 |
---|---|
ext | 文件后缀名 |
name | 文件名 |
path | 文件相对途径 |
folder | 文件地点文件夹 |
hash | 每次构建生成的仅有 hash 值 |
chunkhash | 依据 chunk 生成 hash 值 |
contenthash | 依据文件内容生成hash 值 |
因为js
咱们在出产环境里会把一些公共库和程序进口文件区分隔,独自打包构建,选用chunkhash
的方法生成哈希值,那么只需咱们不改动公共库的代码,就能够保证其哈希值不会受影响,能够持续运用浏览器缓存,所以js
适合运用chunkhash
;
css
和图片资源媒体资源一般都是独自存在的,能够选用contenthash
,只要文件自身改变后会生成新hash
值。
修正webpack.base.js
,把js
输出的文件称号格局加上chunkhash
,把css
和图片媒体资源输出格局加上contenthash
。
// webpack.base.js
// ...
module.exports = {
// 打包文件出口
output: {
filename: 'static/js/[name].[chunkhash:8].js', // // 加上[chunkhash:8]
// ...
},
module: {
rules: [
{
test:/.(png|jpg|jpeg|gif|svg)$/, // 匹配图片文件
// ...
generator:{
filename:'static/images/[name].[contenthash:8][ext]' // 加上[contenthash:8]
},
},
{
test:/.(woff2?|eot|ttf|otf)$/, // 匹配字体文件
// ...
generator:{
filename:'static/fonts/[name].[contenthash:8][ext]', // 加上[contenthash:8]
},
},
{
test:/.(mp4|webm|ogg|mp3|wav|flac|aac)$/, // 匹配媒体文件
// ...
generator:{
filename:'static/media/[name].[contenthash:8][ext]', // 加上[contenthash:8]
},
},
]
},
// ...
}
再修正webpack.prod.js
,修正抽离css
文件称号格局
// webpack.prod.js
// ...
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = merge(baseConfig, {
mode: 'production',
plugins: [
// 抽离css插件
new MiniCssExtractPlugin({
filename: 'static/css/[name].[contenthash:8].css' // 加上[contenthash:8]
}),
// ...
],
// ...
})
再次打包就能够看到文件后边的hash
了
6、代码分割第三方包和公共模块
一般第三方包的代码改变频率比较小,能够独自把node_modules
中的代码独自打包, 当第三包代码没改变时,对应chunkhash
值也不会改变,能够有用利用浏览器缓存,还有公共的模块也能够提取出来,防止重复打包加大代码整体体积, webpack
供给了代码分隔功用, 需求咱们手动在优化项optimization中手动装备下代码分隔splitChunks规矩。
修正webpack.prod.js
module.exports = {
// ...
optimization: {
// ...
splitChunks: { // 分隔代码
cacheGroups: {
vendors: { // 提取node_modules代码
test: /node_modules/, // 只匹配node_modules里边的模块
name: 'vendors', // 提取文件命名为vendors,js后缀和chunkhash会主动加
minChunks: 1, // 只需运用一次就提取出来
chunks: 'initial', // 只提取初始化就能获取到的模块,不管异步的
minSize: 0, // 提取代码体积大于0就提取出来
priority: 1, // 提取优先级为1
},
commons: { // 提取页面公共代码
name: 'commons', // 提取文件命名为commons
minChunks: 2, // 只需运用两次就提取出来
chunks: 'initial', // 只提取初始化就能获取到的模块,不管异步的
minSize: 0, // 提取代码体积大于0就提取出来
}
}
}
}
}
7、tree-shaking清理未运用css
js
中会有未运用到的代码,css
中也会有未被页面运用到的款式,能够经过purgecss-webpack-plugin插件打包的时分移除未运用到的css
款式,这个插件是和mini-css-extract-plugin插件合作运用的,在上面现已装置过,还需求glob-all来挑选要检测哪些文件里边的类名和id
还有标签称号, 装置依靠:
npm i purgecss-webpack-plugin@4 glob-all -D
or
yarn add purgecss-webpack-plugin@4 glob-all -D
本文版别是4版别最新的5版别导入方法需求改为 const { PurgeCSSPlugin } = require(‘purgecss-webpack-plugin’)
修正webpack.prod.js
// webpack.prod.js
// ...
const globAll = require('glob-all')
const PurgeCSSPlugin = require('purgecss-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// ...
plugins: [
// 抽离css插件
new MiniCssExtractPlugin({
filename: 'static/css/[name].[contenthash:8].css'
}),
// 清理无用css
new PurgeCSSPlugin({
// 检测src下一切tsx文件和public下index.html中运用的类名和id和标签称号
// 只打包这些文件中用到的款式
paths: globAll.sync([
`${path.join(__dirname, '../src')}/**/*.tsx`,
path.join(__dirname, '../public/index.html')
]),
}),
]
}
插件自身也供给了一些白名单safelist
特点,符合装备规矩挑选器都不会被删去掉,比方运用了组件库antd, purgecss-webpack-plugin
插件检测src
文件下tsx
文件中运用的类名和id
时,是检测不到在src
中运用antd
组件的类名的,打包的时分就会把antd
的类名都给过滤掉,能够装备一下安全挑选列表,防止删去antd
组件库的前缀ant
。
new PurgeCSSPlugin({
// ...
safelist: {
standard: [/^ant-/], // 过滤以ant-开头的类名,哪怕没用到也不删去
}
})
8、资源懒加载
像react
,vue
等单页运用打包默许会打包到一个js
文件中,尽管运用代码分割能够把node_modules
模块和公共模块
别离,但页面初始加载仍是会把整个项意图代码下载下来,其实只需求公共资源和当时页面的资源就能够了,其他页面资源能够等运用到的时分再加载,能够有用提高首屏加载速度。
webpack
默许支撑资源懒加载,只需求引进资源运用import
语法来引进资源,webpack
打包的时分就会主动打包为独自的资源文件,等运用到的时分动态加载。
以懒加载组件和css
为例,新建懒加载组件src/components/LazyDemo.tsx
import React from "react";
function LazyDemo() {
return <h3>我是懒加载组件</h3>
}
export default LazyDemo
修正App.tsx
import React, { lazy, Suspense, useState } from 'react'
const LazyDemo = lazy(() => import('@/components/LazyDemo')) // 运用import语法合作react的Lazy动态引进资源
function App() {
const [ show, setShow ] = useState(false)
// 点击事件中动态引进css, 设置show为true
const onClick = () => {
import("./app.css")
setShow(true)
}
return (
<>
<h2 onClick={onClick}>展现</h2>
{/* show为true时加载LazyDemo组件 */}
{ show && <Suspense fallback={<span>加载中</span>}><LazyDemo /></Suspense> }
</>
)
}
export default App
点击展现文字时,才会动态加载app.css
和LazyDemo
组件的资源。
9、资源预加载
上面装备了资源懒加载后,尽管提高了首屏渲染速度,可是加载到资源的时分会有一个去恳求资源的延时,假设资源比较大会呈现延迟卡顿现象,能够凭借link
标签的rel
特点prefetch
与preload
,link
标签除了加载css
之外也能够加载js
资源,设置rel
特点能够规矩link
提前加载资源,可是加载资源后不履行,等用到了再履行。
rel的特点值
-
preload
是告知浏览器页面必定需求的资源,浏览器一定会加载这些资源。 -
prefetch
是告知浏览器页面或许需求的资源,浏览器不一定会加载这些资源,会在空闲时加载。
关于当时页面很有必要的资源运用preload
,关于或许在将来的页面中运用的资源运用prefetch
。
webpack v4.6.0+
添加了对预获取和预加载的支撑,运用方法也比较简单,在import
引进动态资源时运用webpack
的魔法注释;
// 单个方针
import(
/* webpackChunkName: "my-chunk-name" */ // 资源打包后的文件chunkname
/* webpackPrefetch: true */ // 敞开prefetch预加载
/* webpackPreload: true */ // 敞开preload预获取
'./module'
);
测验一下,在src/components
目录下新建PreloadDemo.tsx
,PreFetchDemo.tsx
// src/components/PreloadDemo.tsx
import React from "react";
function PreloadDemo() {
return <h3>我是PreloadDemo组件</h3>
}
export default PreloadDemo
// src/components/PreFetchDemo.tsx
import React from "react";
function PreFetchDemo() {
return <h3>我是PreFetchDemo组件</h3>
}
export default PreFetchDemo
修正App.tsx
import React, { lazy, Suspense, useState } from 'react'
// prefetch
const PreFetchDemo = lazy(() => import(
/* webpackChunkName: "PreFetchDemo" */
/*webpackPrefetch: true*/
'@/components/PreFetchDemo'
))
// preload
const PreloadDemo = lazy(() => import(
/* webpackChunkName: "PreloadDemo" */
/*webpackPreload: true*/
'@/components/PreloadDemo'
))
function App() {
const [ show, setShow ] = useState(false)
const onClick = () => {
setShow(true)
}
return (
<>
<h2 onClick={onClick}>展现</h2>
{/* show为true时加载组件 */}
{ show && (
<>
<Suspense fallback={null}><PreloadDemo /></Suspense>
<Suspense fallback={null}><PreFetchDemo /></Suspense>
</>
) }
</>
)
}
export default App
然后打包后查看作用,页面初始化时预加载了PreFetchDemo.js
组件资源,可是不履行里边的代码,等点击展现按钮后从预加载的资源中直接取出来履行,不用再从服务器恳求,节省了许多时刻。
10、打包时生成gzip文件
前端代码在浏览器运转,需求从服务器把html
,css
,js
资源下载履行,下载的资源体积越小,页面加载速度就会越快。一般会选用gzip
紧缩,现在大部分浏览器和服务器都支撑gzip
,能够有用削减静态资源文件大小,紧缩率在 70%
左右。
nginx
能够装备gzip: on
来敞开紧缩,可是只在nginx
层面敞开,会在每次恳求资源时都对资源进行紧缩,紧缩文件会需求时刻和占用服务器cpu
资源,更好的方法是前端在打包的时分直接生成gzip
资源,服务器接收到恳求,能够直接把对应紧缩好的gzip
文件回来给浏览器,节省时刻和cpu
。
webpack
能够凭借compression-webpack-plugin插件在打包时生成gzip
文章,装置依靠;
npm i compression-webpack-plugin -D
or
yarn add compression-webpack-plugin -D
添加装备,修正webpack.prod.js
const glob = require('glob')
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
// ...
plugins: [
// ...
new CompressionPlugin({
test: /.(js|css)$/, // 只生成css,js紧缩文件
filename: '[path][base].gz', // 文件命名
algorithm: 'gzip', // 紧缩格局,默许是gzip
test: /.(js|css)$/, // 只生成css,js紧缩文件
threshold: 10240, // 只要大小大于该值的资源会被处理。默许值是 10k
minRatio: 0.8 // 紧缩率,默许值是 0.8
})
]
}
装备完结后再打包,能够看到打包后js的目录下多了一个 .gz
结束的文件
七、项目标准
1、装置eslint
详情eslint+prettier首要经过指令创立.eslintrc.js
文件。首要装置eslint
依靠:
npm i eslint -D
or
yarn add eslint -D
然后运用eslint
指令装置相关依靠
npx eslint --init
会主动装置下方依靠
eslint-plugin-import@2.25.4
eslint-plugin-jsx-a11y@6.5.1
eslint-config-airbnb@19.0.4
eslint-plugin-react@7.28.0
eslint@8.8.0
eslint-plugin-react-hooks@4.3.0
<@typescript-eslint/parser@5.11.0>
@typescript-eslint/eslint-plugin@5.11.0
npx 是用于履行项目下装置的模块指令。如:咱们在项目下装置了 webpack 模块,可是咱们并不能直接在指令行履行 webpack 指令。只能运用以下方法履行,或许添加到
package.json
脚本模块中去履行node_modules/.bin/webpack
npx 的原理很简单,便是运转的时分,会到node_modules/.bin
途径和环境变量$PATH
里边,查看指令是否存在。因为 npx 会查看环境变量$PATH
,所以体系指令也能够调用; 而且 npx 随用随删,关于不存在的模块指令、会先去下载履行完后会删去模块;
✔ How would you like to use ESLint? style
✔ What type of modules does your project use? esm
✔ Which framework does your project use? react
✔ Does your project use TypeScript? No / Yes
✔ Where does your code run? browser
✔ How would you like to define a style for your project? guide
✔ Which style guide do you want to follow? airbnb
✔ What format do you want your config file to be in? JavaScript
eslint-plugin-react@^7.28.0 @typescript-eslint/eslint-plugin@latest eslint-config-airbnb@latest eslint@^7.32.0 || ^8.2.0 eslint-plugin-import@^2.25.3 eslint-plugin-jsx-a11y@^6.5.1 eslint-plugin-react-hooks@^4.3.0 @typescript-eslint/parser@latest
✔ Would you like to install them now with npm? No / Yes
装置完结之后项目根目录就会主动生成一个 eslint 装备文件.eslintrc.js
, .eslintrc.js
文件内容如下:
module.exports = {
env: {
browser: true,
es2021: true
},
extends: [
'plugin:react/recommended',
],
overrides: [
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module'
},
plugins: [
'react'
],
rules: {
}
}
装置typescript相关依靠
因为项目运用的是Typescript
,所以得改下 eslint 解释器,参阅typescript-eslint,依靠装置指令如下:
npm install @typescript-eslint/parser @typescript-eslint/eslint-plugin -D
or
yarn add @typescript-eslint/parser @typescript-eslint/eslint-plugin -D
修正.eslintrc.js
文件内容如下:
module.exports = {
env: {
browser: true,
es2021: true,
node: true
},
extends: [
'plugin:react/recommended',
'plugin:import/recommended',
'plugin:@typescript-eslint/recommended',
],
parser: '@typescript-eslint/parser',
overrides: [
],
parserOptions: {
ecmaVersion: 'latest', // 指定ECMAScript 语法为最新
sourceType: 'module', // 指定代码为 ECMAScript 模块
ecmaFeatures: {
jsx: true, // 启用jsx
}
},
plugins: [
'react',
'@typescript-eslint',
],
rules: {
}
}
运用airbnd
代码风格
至此eslint
的根底装备现已搭建完结,可是实践项目开发过程中,咱们需求或许运用到一些第三方的eslint
标准,当时咱们项目运用airbnd
, airbnd
也是当时github
star 最多的, 详细能够参阅airbnb,假设不需求就省掉这一步,首要需求装置airbnd
相关装备,装置依靠之前咱们需求先简单了解一下相关依靠
-
eslint-config-airbnb
是Airbnb JavaScript
风格的 eslint 同享装备库,检测规矩包括ES6+
和React
,它依靠于eslint-plugin-import
、eslint-plugin-react
、eslint-plugin-react-hooks
、eslint-plugin-jsx-a11y
包。 -
eslint-config-airbnb-base
,假设咱们不需求React
,能够装置这个包代替eslint-config-airbnb
-
eslint-config-airbnb-typescript
,支撑 typescript,依靠于eslint-config-airbnb
因为咱们现在的项目是React+Ts,所以要装置eslint-config-airbnb
和eslint-config-airbnb-typescript
这两个包。
咱们先履行npm info "eslint-config-airbnb@latest" peerDependencies
,了解eslint-config-airbnb
的依靠包版别;
warning " > autoprefixer@10.4.14" has unmet peer dependency "postcss@^8.1.0".
eslint: '^7.32.0 || ^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'
}
直达依靠包版别之后,装置依靠相应版别(ps: 这一步在咱们初始化eslint
的时分其实就现已主动装置了相关依靠,假设不是运用eslint --init
初始化eslint
就按下面的依靠装置一遍)
npm i 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 -D
or
yarn add 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 -D
依照eslint-config-airbnb-typescript 装备过程, 修正.eslintrc.js
文件
module.exports = {
env: {
browser: true,
es2021: true,
node: true
},
extends: [
'airbnb',
'airbnb-typescript',
'airbnb/hooks',
// 'plugin:react/recommended',
// 'plugin:import/recommended',
'plugin:@typescript-eslint/recommended',
],
parser: '@typescript-eslint/parser',
overrides: [
],
parserOptions: {
ecmaVersion: 'latest', // 指定ECMAScript 语法为最新
sourceType: 'module', // 指定代码为 ECMAScript 模块
ecmaFeatures: {
jsx: true, // 启用jsx
}
},
plugins: [
'react',
'@typescript-eslint',
],
rules: {
// eslint (http://eslint.cn/docs/rules)
'react/jsx-filename-extension': ['error', { extensions: ['.js', '.jsx', '.ts', '.tsx'] }],
'class-methods-use-this': 'off',
'no-param-reassign': 'off',
'no-unused-expressions': 'off',
'no-plusplus': 0,
'no-restricted-syntax': 0,
'consistent-return': 0,
'@typescript-eslint/ban-types': 'off',
// "import/no-extraneous-dependencies": "off",
'@typescript-eslint/no-non-null-assertion': 'off',
'import/no-unresolved': 'off',
'import/prefer-default-export': 'off', // 封闭默许运用 export default 方法导出
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
'@typescript-eslint/no-use-before-define': 0,
'no-use-before-define': 0,
'@typescript-eslint/no-var-requires': 0,
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-namespace': 'off', // 制止运用自界说 TypeScript 模块和命名空间。
'no-shadow': 'off',
// "@typescript-eslint/no-var-requires": "off"
'import/extensions': [
'error',
'ignorePackages',
{
'': 'never',
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never'
}
]
// "no-var": "error", // 要求运用 let 或 const 而不是 var
// "no-multiple-empty-lines": ["error", { max: 1 }], // 不答应多个空行
// "no-use-before-define": "off", // 制止在 函数/类/变量 界说之前运用它们
// "prefer-const": "off", // 此规矩旨在符号运用 let 关键字声明但在初始分配后从未重新分配的变量,要求运用 const
// "no-irregular-whitespace": "off", // 制止不规矩的空白
// // typeScript (https://typescript-eslint.io/rules)
// "@typescript-eslint/no-unused-vars": "error", // 制止界说未运用的变量
// "@typescript-eslint/no-inferrable-types": "off", // 能够轻松推断的显式类型或许会添加不必要的冗长
// "@typescript-eslint/no-namespace": "off", // 制止运用自界说 TypeScript 模块和命名空间。
// "@typescript-eslint/no-explicit-any": "off", // 制止运用 any 类型
// "@typescript-eslint/ban-ts-ignore": "off", // 制止运用 @ts-ignore
// "@typescript-eslint/ban-types": "off", // 制止运用特定类型
// "@typescript-eslint/explicit-function-return-type": "off", // 不答应对初始化为数字、字符串或布尔值的变量或参数进行显式类型声明
// "@typescript-eslint/no-var-requires": "off", // 不答应在 import 句子中运用 require 句子
// "@typescript-eslint/no-empty-function": "off", // 制止空函数
// "@typescript-eslint/no-use-before-define": "off", // 制止在变量界说之前运用它们
// "@typescript-eslint/ban-ts-comment": "off", // 制止 @ts-<directive> 运用注释或要求在指令后进行描绘
// "@typescript-eslint/no-non-null-assertion": "off", // 不答应运用后缀运算符的非空断言(!)
// "@typescript-eslint/explicit-module-boundary-types": "off", // 要求导出函数和类的公共类办法的显式回来和参数类型
// // react (https://github.com/jsx-eslint/eslint-plugin-react)
// "react-hooks/rules-of-hooks": "error",
// "react-hooks/exhaustive-deps": "off"
},
settings: {
'import/extensions': ['.js', '.jsx', '.ts', '.tsx'],
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx']
},
'import/resolver': {
node: {
paths: ['src'],
extensions: ['.js', '.jsx', '.ts', '.tsx'],
moduleDirectory: ['node_modules', 'src/']
}
}
}
}
至此,airbnd
校验规矩就装备完结了,咱们现在能够在package.json
添加eslint
指令来检验咱们的代码质量:
{
"script": {
"lint:js": "eslint --ext .js,.jsx,.ts,.tsx ./src"
}
}
有时分,咱们需求在代码中疏忽
esLint
的某些规矩查看,此刻咱们能够经过参加代码注释的方法处理:能够指定整个文件、某一行、某一区块敞开/封闭 某些或悉数规矩查看;
在项目目录添加.eslintignore
,将需求疏忽的文件内容添加到文件:
.eslintrc.js
node_modules
dist
.idea
README.md
.gitignore
2、装置prettier
每个人写代码的风格习气不相同,比方代码换行,结束是否带分号,单双引号,缩进等,而且不能只靠口头标准来约束,项目紧迫的时分或许会不太注意代码格局,这时分需求有工具来帮咱们主动格局化代码,而prettier
便是帮咱们做这件事的,因而prettier
也是咱们一般项目开发中必不可少的。
依靠装置
依靠装置指令如下:
npm i prettier -D
or
yarn add prettier -D
新建 .prettier.js
依靠装置完结之后,在项意图根目录新增.prettier.js
文件,添加咱们常用的代码风格
// .prettierrc.js
module.exports = {
tabWidth: 2, // 一个tab代表几个空格数,默许便是2
useTabs: false, // 是否启用tab取代空格符缩进,.editorconfig设置空格缩进,所以设置为false
printWidth: 100, // 一行的字符数,假设超过会进行换行
semi: false, // 行尾是否运用分号,默许为true
singleQuote: true, // 字符串是否运用单引号
trailingComma: 'none', // 对象或数组结尾是否添加逗号 none| es5| all
jsxSingleQuote: true, // 在jsx里是否运用单引号,你看着办
bracketSpacing: true, // 对象大括号直接是否有空格,默许为true,作用:{ foo: bar }
arrowParens: 'avoid' // 箭头函数假设只要一个参数则省掉括号
}
新建 .prettierignore
项目目录添加.prettierignore
,疏忽一些不需求 prettier
格局化的文件
package.json
dist
node_modules
.idea
README.md
.gitignore
装备履行指令
至此,咱们能够在package.json
添加指令
{
"script": {
"lint:prettier": "prettier -c --write \"src/**/*\""
}
}
履行yarn lint:prettier
就能够格局化咱们项意图代码了
处理eslint
和 prettier
抵触
防止 eslint 和 prettier 抵触,咱们需求再装置两个包eslint-config-prettier
、eslint-plugin-prettier
。
eslint-config-prettier
的作用是封闭 eslint 中一切不必要的或或许与 prettier 抵触的规矩,让 eslint 检测代码时不会对这些规矩报错或告警。比方 eslint 规矩是双引号,而咱们用 prettier 格局化代码时是用单引号,会存在抵触。咱们在eslint-config-prettier 代码能够看到,例如缩进、引号等格局规矩都被封闭了。封闭后,咱们能够彻底自界说 prettier 来格局化咱们的代码,而不受 eslint 影响。
eslint-plugin-prettier
是一个 ESLint 插件。上面咱们说封闭了一些 eslint 的代码格局规矩。假定咱们约好 prettier 规矩运用双引号,但是敲代码写成单引号,我仍是期望能够按 prettier 的规矩给我一些代码不标准的报错或警告提示。那么eslint-config-prettier
是封闭了 eslint 中与 prettier 抵触的规矩,eslint-plugin-prettier
便是敞开了以 prettier 为准的规矩,并将陈述过错给 eslint。
装置依靠
npm i eslint-config-prettier eslint-plugin-prettier -D
yarn add eslint-config-prettier eslint-plugin-prettier -D
装置后咱们只需求在.eslintrc.js
文件添加一行即可
{
extends: [
// ...
'plugin:prettier/recommended'
]
}
3、装置 stylelint
检测 css 款式代码质量,其实许多项目都是不检测的,假设不做这步能够疏忽。
装置依靠
依照官网 docs,咱们首要装置依靠:
npm i stylelint stylelint-config-standard -D
or
yarn add stylelint stylelint-config-standard -D
新建.stylelintrc.js
在项目根目录创立.stylelintrc.js
文件,添加如下内容:
module.exports = {
extends: ['stylelint-config-standard'],
};
至此,咱们能够在package.json
添加指令
{
"script": {
"lint:style": "stylelint \"**/*.css\""
}
}
装置 stylelint-prettier
相同的,咱们统一用 prettier 来格局化 css 代码。 需求装置stylelint插件来防止与prettier抵触。
-
stylelint-config-prettier
,和eslint-config-prettier
相似,作用是封闭 stylelint 一切不必要的或或许与 prettier 抵触的规矩。可是在 Stylelint v15 版别之后,Stylelint 默许封闭了一切与 prettier 相抵触的风格规矩,所以不需求装置stylelint-config-prettier
了。 -
stylelint-prettier
,和eslint-plugin-prettier
相似,敞开了以 prettier 为准的规矩,并将陈述过错给 stylelint。
上面了解后,咱们只需求装置stylelint-prettier
。
npm i stylelint-prettier -D
or
yarn add stylelint-prettier -D
修正.stylelintrc.js
文件内容如下:
module.exports = {
extends: ['stylelint-config-standard', 'stylelint-prettier/recommended'],
};
新建.stylelintignore
有时分有些目录或许文件咱们不想进行检测这时分咱们就能够在项意图根目录新建.stylelintignore
文件,在文件内添加咱们想要stylelint
疏忽的内容,例如:
dist
public
env
build
.vscode
.DS_Store
README.md
node_modules
.idea
装备 .vscode/settings.json
终究记住在.vscode/settings.json
中参加:
{
// ...
"editor.codeActionsOnSave": {
"source.fixAll.stylelint": true
},
"stylelint.validate": [
"css",
"less",
"sass",
"stylus",
"postcss"
]
}
4、editorconfig
在项目中引进 editorconfig
是为了在多人协作开发中坚持代码的风格和共同性。不同的开发者运用不同的编辑器或IDE,或许会有不同的缩进(比方有的人喜爱4个空格,有的喜爱2个空格)、换行符、编码格局等。甚至相同的编辑器因为开发者自界说装备的不同也会导致不同风格的代码,这会导致代码的可读性降低,添加代码抵触的或许性,降低了代码的可维护性。
EditorConfig 使不同编辑器能够坚持相同的装备。因而,咱们得以无需在每次编写新代码时,再依靠 Prettier 来依照团队约好格局化一遍(呈现保存时格局化突然改变的状况) 。当然这需求在你的 IDE 上装置了必要的 EditorConfig 插件或扩展。
EditorConfig for VS Code
现在干流的编辑器或许 IDE 根本上都有对应的EditorConfig
插件,有的是内置支撑的(比方,WebStorm
不需求独立装置EditorConfig
的插件),有的需求独立装置。
然后在插件的介绍页上点击设置的齿轮,而且挑选Add to Workspace Recommendations,就能够将其参加清单。也能够直接敞开.vscode/extensions.json
进行编辑:
{
"recommendations": ["editorconfig.editorconfig"]
}
为什么要做这个操作? 假设哪天项目新来一个协同开发的同学,当他拉取取项目,用
vscode
翻开项意图时分,编辑器就会主动提醒他装置这个插件,并将相关的装备做设定。上面的eslint
和prettier
插件也是相似。
新建 .editorconfig
在项意图根目录新建.editorconfig
文件
# https://editorconfig.org
root = true # 设置为true表明根目录,操控装备文件 .editorconfig 是否收效的字段
[*] # 匹配悉数文件,匹配除了`/`途径分隔符之外的任意字符串
charset = utf-8 # 设置字符编码,取值为 latin1,utf-8,utf-8-bom,utf-16be 和 utf-16le,当然 utf-8-bom 不推荐运用
end_of_line = lf # 设置运用的换行符,取值为 lf,cr 或许 crlf
indent_size = 2 # 设置缩进的大小,即缩进的列数,当 indexstyle 取值 tab 时,indentsize 会运用 tab_width 的值
indent_style = space # 缩进风格,可选space|tab
insert_final_newline = true # 设为true表明使文件以一个空白行结束
trim_trailing_whitespace = true # 删去一行中的前后空格
[*.md] # 匹配悉数 .md 文件
trim_trailing_whitespace = false
上面的装备能够标准本项目中文件的缩进风格,和缩进空格数等,会掩盖vscode
的装备,来到达不同编辑器中代码默许行为共同的作用。