# 使用 webpack 从零配置 React 开发环境及打包优化
使用的 webpack 版本是@4.39.3,不过@5 beta 版已经发布了,emmmm...
# 1.实现对 JSX, JS, PNG, LESS 等文件进行打包
目标:将 JSX, LESS 等文件进行打包成 bundle.js,在 HMTL 中引入并可以在浏览器访问
# 1.1 初始化
新建 first 文件夹
在文件夹中使用
npm init -y
生成package.json
文件
# 1.2 安装 webpack
使用npm
或者yarn
安装下面依赖,本文全部使用的yarn
yarn add webpack webpack-cli -D
注释: webpack-cli 是 webpack 的命令行工具 -D 是 --save--dev 的缩写, -S 是 --save 的缩写。版本号:webpack@4.39.3,webpack-cli@3.3.7
# 1.3 安装样式相关 loader
yarn add css-loader style-loader less less-loader -D
注释: css-loader 可以配置 css-module,css-loader 用于处理.css 文件,style-loader 用于把样式插入 dom
# 1.4 安装 babel,react 相关 loader,安装 react 相关依赖
安装 babel
yarn add babel-loader @babel/core @babel/preset-env -D
注释: babel,将 es6 转换为 es5,@babel/core 是 babel-core 的第七版,@babel/preset-env 是 babel-preset-env 升级版,用于配置浏览器的兼容度以及使用最新 ES6 语法。不需要安装 babel-preset-stage-0,因为 preset-env 已经集成了这几个 stage 的功能。但是 preset-env 不支持修饰器,动态引入,静态属性等等 ES6+ 最新的语法
安装 babel-react
yarn add @babel/preset-react -D
注释: @babel/preset-react 是 babel-preset-react 升级版, 用于将 jsx 转换成 js
安装 react 相关依赖
yarn add react react-dom -S
注释, babel 转换主要使用到的是 React.createElement()方法
# 1.5 安装图片处理相关 loader
yarn add url-loader file-loader -D
# 1.6 项目目录结构
新建配置文件及开发文件
-- dist // 用于存放打包后的文件
-- node_modules // 依赖包,自动生成
-- webpack.config.js // webpack配置
-- .babelrc // babel配置
-- src
-- assets
-- avatar.png
-- index.jsx
-- app.jsx
-- app.less
-- index.html
-- package.json
-- .gitignore // 设置git忽略文件
可以按照这个顺序对文件进行配置与修改 src/app.js
以及 src/app.less
---> src/index.jsx
---> webpack.config.js
---> .babelrc
贴一下主要文件的内容
关于 webpack 的一些配置,可以参考上一篇的内容 (opens new window)
# 1.7 踩坑点
- webpack 配置文件需要使用 Common.js 模块规范(因为是 node 可执行文件),ES6 模块语法无法使用,但是其他文件如 app.js 可以使用 ES6 语法(因为有 babel-loader 可以进行转换)
index.tsx
,ReactDOM.render(element, document.getElementById('app'))
要与HTML
的节点id
对应,其次,引入的import ReactDOM from 'react-dom'
名字是不做严格区分的- 构建完成的 script 文件要放在 id 为 app 的 div 下面
# webpack 配置文件容易出错的部分
- webpack 容易写错的部分,output 的是 filename 以及 path,不是 pathname
- webpack module rules 里面书写的是 loader 的规则
- 注意:rules 之下的规则,test 后面跟正则表达式,而不是加引号的字符串
- 添加 mode,让 webpack 打包时进行相应的优化
# 与 react 配置有关的注意事项
- 如果使用了 babel-loader 处理包含 jsx 语法的 js 文件,最好配置.babelrc(rc 可以解为 resource 缩写)里面的 presets 配置项,preset-react,preset-env 可以配置要兼容浏览器支持语法的程度。当然,.babelrc 也可以配置 plugins 选项,这里并没有用到
# 1.8 进行打包
在package.json
文件的script
中添加 build
命令即可。
"script": {
"build": "webpack --config webpack.config.js"
}
命令行输入yarn build
,可以看到在build
目录多出了一个bundle.js
文件,把src
目录下的index.html
在浏览器中打开,就可以看到我们实现的页面了
# 2. 配置 ts 以及 dev 环境
# 2.1 配置 ts 开发环境
在此之前,我们先配置一下 ts-react 开发环境。
新建目录 second,全局安装typescript
,目的是为了能够使用tsc
命令
分别执行以下命令
npm init -y // 生成package.json
tsc --init // 生成tsconfig.json
按照上一节的内容安装依赖
yarn add webpack webpack-cli css-loader style-loader less less-loader babel-loader @babel/core @babel/preset-env @babel/preset-react url-loader file-loader -D
yarn add react react-dom -S
安装项目需要的 typescript 相关依赖,以及 react type 库
yarn add typescript @babel/preset-typescript -D
yarn add @types/react-dom @types/react -D
注释:types 依赖包含.d.ts 声明文件,在 react 中用 TS 语法时,会自动对变量进行类型检测
因为要区分开发环境与正式环境的 webpack 配置,这里我们新建base dev prod
等三个文件对 webpack 进行配置
文件目录
-- dist // 用于存放打包后的文件
-- node_modules // 依赖包,自动生成
-- webpack.config.base.js // webpack基础配置
-- webpack.config.dev.js // webpack开发环境配置
-- webpack.config.prod.js // webpack正式环境配置
-- .babelrc // babel配置
-- src
-- assets
-- avatar.png
-- index.tsx
-- app.tsx
-- app.less
-- index.html
-- package.json
-- tsconfig.json
-- .gitignore // 设置git忽略文件
当我们按照上一节的内容把index.tsx app.tsx app.less
几个文件写好后,发现多处报红,我们需要把tsconfig.json
文件的jsx
选项的值改成react
并取消注释。
关于 JSX 部分的代码不报红了,但是引入的 less 文件,png 文件报红了,为什么呢?是因为 TS 没有找到这两个文件对应的.d.ts
声明文件,那应该如何解决?
可以自己写一个.d.ts
文件对 png 以及 less 文件进行 intrface 定义,或者是安装依赖包进行自动生成,这里举例第一种方法
src 目录下新建globalType.d.ts
declare module '*.less' {
const content: any
export = content
}
declare module '*.png' {
const content: any
export = content
}
这时候就没有红色警告啦~
但是还有一个问题,在index.tsx
中,引入的app.tsx
报红,但是把.tsx
去掉webpack
会找不到app
文件而打包失败,这时候我们需要配置webpack
的resolve
配置项
resolve: {
// 省略文件后缀,让webpack自动查找
extensions: ['.js', '.jsx', '.ts', '.tsx']
},
好了,页面终于没警告了~
紧接着配置.babelrc
,其实主要就是多添加一个@babel/preset-typescript
的 presets
配置 webpack,安装依赖
yarn add webpack-merge -D // 该依赖可以合并webpack配置
紧接着配置webpack.config.base.js
以及webpack.config.dev.js
,详细见源码 (opens new window)
最后在package.json
文件添加build
命令"build": "webpack --config webpack.config.prod.js"
并执行
显示打包文件过大,因为less png
等文件也被打包成 js 文件了,这里暂时先放着,后面会进行优化
在index.html
中引入bundle.js
,在浏览器打开,发现加载成功
# 2.2 配置 dev 环境
如果每次改变一个文件,都要输入打包命令再去浏览器里面查看应用,开发会变得很麻烦,在开发环境下,我们可以使用基于 express 的 webpack 提供 webpack-dev-server,帮助我们更好的在开发环境中开发。
在此之前,我们需要安装两个插件,做到动态生成 bundle
以及 index.html
文件
yarn add html-webpack-plugin clean-webpack-plugin -D // 每次生成新的html以及清理html
yarn add webpack-dev-server -D // devServer服务
把src/index.html
<script src="../dist/bundle.js"></script>
这行代码删除掉,因为配置了html-webpack-plugin
会自动引入打包后的包
配置webpack.config.base.js
文件,配置输出文件名字,使得生成的包每次生成的名字都不一样,防止浏览器缓存
// webpack.config.base.js
// ...
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
// 入口
entry: {
App: './src/index.tsx',
},
// 打包后的动态文件及目录
output: {
path: path.resolve(__dirname, './dist'),
filename: '[chunkhash:4][name]bundle.js',
},
// ...
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html', //打包后的文件名
}),
new CleanWebpackPlugin(),
],
}
//webpack.config.dev.js
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.config.base.js')
module.exports = merge(baseWebpackConfig, {
mode: 'development',
devServer: {
contentBase: './dist', // 根目录
open: true,
port: '3000', //默认是8080
stats: 'errors-only', //终端仅打印 error
},
})
在package.json
文件添加dev
命令dev
并运行
"script": {
"dev": "webpack-dev-server --config webpack.config.dev.js",
"build": "webpack --config webpack.config.prod.js",
}
执行yarn dev
浏览器自动打开http://localhost:3000
即项目 html,当我们修改app.tsx
文件时,浏览器会自动刷新。
在 dev 模式下,我们在dist
目录下是看不到任何文件的,因为 dev 把这些文件存放在内存中了
执行yarn build
进行打包,发现打包出的app.js
有343K
,webpack提出了警告,后面一小节会来说这个问题
# 2.3 优化 react-ts 使用体验
- 使用 babel 支持较新的语法
- 在 tsx 中使用 class 组件,使用函数组件
- 在 tsx 中使用 antd
# 2.3.1 使用较新的语法
在 class 组件中,我们可能会用到这些比较新的语法(以下用的 js 语法)
babel 的插件 (opens new window)
babel7 的插件包 (opens new window)
// @babel/plugin-proposal-decorators
@connect()
class Demo extends React.Component {
// @babel/plugin-proposal-class-properties
a = 1
// 此特性需要安装 babel-plugin-transform-class-properties
// babel7的包名是 @babel/plugin-proposal-class-properties
handleTest = () => {
console.log(12312)
}
}
配置 .babelrc
,需要哪些你就可以装哪些,这里我只是用@babel/plugin-proposal-class-properties
做一下演示
{
"presets": [...],
"plugins": ["@babel/plugin-proposal-class-properties"]
}
# 2.3.2 在 React 中使用 TS
可以去看源码 (opens new window)或者前面的这篇文章 (opens new window)
# 2.3.3 在 React 中使用 ant-design
yarn add antd -S // 不需要安装type库
但是当我们引入Input Button
组件时,发现样式并没有生效,当我们在app.jsx引入import 'antd/dist/antd.css'
发现报错了,因为webpack默认只处理src
目录下的less
文件,我们需要把include
包含node_modules
,更改配置后发现不报错了,但是还是没有样式效果
const srcRoot = path.resolve(__dirname, 'src')
const modules = path.resolve(__dirname, 'node_modules')
modules: {
rules: [
{
test: /\.(css|less)$/,
include: [modules, srcRoot]
}
]
}
// less-loader要配置 javascriptEnabled: true 开启JS编译
打包之后发现包竟然变到了1.75M
...这个CSS有点太大了吧。。而且还没加载成功。。_
antd已经支持动态引入,tree-shaking了,所以说不用再配置babel-plugin-import
https://ant.design/docs/react/introduce-cn 注意:antd 默认支持基于 ES module 的 tree shaking,不使用以下插件也会有按需加载的效果。
进行打包yarn build
,我们发现包的大小从 343K
到了 477K
而我们仅仅引入了两个组件Input Button
,如果再增加Rate
组件,会变到544K
... 不过要是把antd分离出来,就不是很大。看下面一节
# 3.优化
loader 用于帮我们处理不同类型的文件,plugins 用于在打包过程中做优化,在使用它们的时候,我们可以思考一下为什么出现了这些 loader 以及 plugin,它们解决了前端的哪些问题
开发环境和生产环境的优化可以分离开来
我们先按照上一节的内容安装依赖并配置,源码 (opens new window)
npm init -y
tsc --init
yarn add webpack webpack-cli css-loader style-loader less less-loader babel-loader @babel/core @babel/preset-env @babel/preset-react url-loader file-loader -D
yarn add react react-dom -S
yarn add @types/react-dom @types/react -D
yarn add typescript @babel/preset-typescript -D
yarn add antd -S
yarn add webpack-dev-server -D
yarn add html-webpack-plugin clean-webpack-plugin -D
yarn add cross-env -D // 设置环境变量
-- dist // 用于存放打包后的文件
-- node_modules // 依赖包,自动生成
-- build
-- template
-- index.html
-- webpack.config.js
-- .babelrc // babel配置
-- src
-- assets
-- avatar.png
-- components
-- types // 存放声明文件
-- index.tsx
-- app.tsx
-- app.less
-- package.json
-- tsconfig.json
-- .gitignore // 设置git忽略文件
编辑tsconfig.json
,比如配置react,路径别名等
编辑 src
下文件,todoList以及antd等等
编辑package.json
,添加dev以及build命令
编辑build下文件,比如webpack.config.json
和对应的rules,以及plugins
跟上面一节的内容大致相同,我们在此基础上进行优化,
# 查看依赖
yarn add webpack-bundle-analyzer -D
安装依赖并在plugin中配置,用于查看依赖包大小
# css文件优化
首先把CSS文件分离出来
yarn add mini-css-extract-plugin -D
分别配置loader以及plugin,这里不做赘述
# babel-loader打包速度优化
bebel-loader设置缓存
use: [
'babel-loader?cacheDirectory=true',
],
# 针对包的大小进行优化实践
未优化前,DOM加载完毕需要7.5s,这是在GZIP已经开启了的情况下,因为服务器带宽只有1M所以加载比较慢。
把 antd moment axios 使用 splitChunks 抽离出来(建议用了splitChunks就不要用dellPlugins了)
可以看到之前的common已经被分离出antd与vender两个文件
但是加载时间基本没有变化
我们可以看到app.js太大了,加载时间太长了,我们使用webpack-plugin-analyzer进行打包分析。
发现highlight.js和@ant-design的包太大了,antd 只要引用了Icon 其中引用的 @ant-design的包太大了,antd4 icon与 antd是分开的,这里使用antd4优化。
并且Icon图标放到CDN上,封装ICON组件用于图标展示
还有一部分rc-form的包占用也比较大,因为antd4 form没有引用rc-form,所以包的大小
接着我们把react, react-dom, moment, highlightjs这些库全部使用CDN在HTML中引入,并且使用webpack, externals配置全局变量。
externals: {
highlight: 'hljs',
moment: 'moment',
react: 'React',
'react-dom': 'ReactDOM'
},
对首页的图片进行压缩
做完这些优化之后,首页加载只需要2.3秒,效果还是很明显的
# 其他优化
- 为-webkit-等 CSS 属性加上前缀 postcss-loader (opens new window)
# 3.43 踩坑
二级路由加载路径有问题,比如,页面二级路由为admin/add-artilce
,webpack 加载的时候,加载的路径是/admin/add-artilce.js
实际上应该加载的路径是/add-article
。在 ouput 设置,publicPath: '/'
单页面应用,部署到nginx,页面刷新404
配置nginx
location / {
try_files $uri /index.html;
}