本编文章接着前两篇文章进行项目webpack打包的优化css
一、react+webpack4搭建前端项目(一)html
二、react+webpack4搭建前端项目(二)react全家桶的使用前端
主要从如下几个方面进行:vue
react
路由的异步加载css
处理
mini-css-extract-plugin
把css
从bundle
包中抽取optimize-css-assets-webpack-plugin
压缩css
代码postcss-loader,autoprefixer
对浏览器兼容性的css
代码加前缀js
的处理
uglifyjs-webpack-plugin
代码压缩js
的bundle
包的提取(拆包)注意antd
版本"antd": "^3.8.3",
,高版本的antd
官方把图标库也构建到release包,因此致使打包变得很大,仅仅icon
图标库就有几百KB,请看下图。若是遇到这个问题,请下降antd
的使用版本到3.8.3之前 node
下边打包优化的基础代码请点击 源码1.0.3。有不熟悉的同窗能够看一下,下载该版本1.0.3,在项目根目录执行 npm run dev
;同时切换到mock目录,执行 npm run dev
,打开http://localhost:8081便可看到效果 主要实现的功能以下图:react
简历管理的查询,删除,修改:webpack
用户模块的查询,修改: ios
用户模块的添加:git
首先咱们看一下没有优化前的js包大小,执行npm run build
github
这时候打包出的文件只有三个 index.html
模板文件 reset.min.css
是从静态目录copy进去的 app.1a9adec2b6012290869f.js
是咱们利用webpack
打包生成的。这里边包括项目中的全部js代码,css代码以及图片data资源
工欲善其事必先利其器,咱们先安装两个很是有用的webpack插件
npm install -D clean-webpack-plugin webpack-bundle-analyzer
复制代码
修改webpack.prod.config.js
,在plugins属性下添加
new CleanWebpackPlugin(),
new BundleAnalyzerPlugin(),
复制代码
咱们知道想文件的异步加载须要使用import("xxx")
,或者require.ensure
这种方法适用webapck1.x
2.x
。因此这里采用import("xxx")
。
在vue中实现路由的异步加载很简单,经过()=>import("xxx")
就能够,那么在react
中咱们也能够这样异步加载
咱们这里实现路由的异步加载借助react-loadable
插件
详细使用请点击 react-loadable使用方法
一、首页编写一个loadable.js
实现异步加载组件
import Loadable from 'react-loadable';
const LoadableComponent = (component) => Loadable({
loader: component,
loading: ()=>null,
});
export default LoadableComponent;
复制代码
二、修改路由组件的加载方式
把container/index.js
文件组件的直接导入
import BlogIndex from "@/blog"
import ResumeIndex from "@/resume"
import UserIndex from "@/user"
复制代码
改为使用react-loadable
插件包装一层加载组件的方式
import LoadableComponent from "@/loadable"
const BlogListPage = LoadableComponent(()=>import("./pages/list"))
const AddBlogPage = LoadableComponent(()=>import("./pages/add"))
复制代码
接着修改user/index.js
,blog/index.js
,把路由组件改为异步加载,改完以后测试一下打包以下图
这时候已经把异步加载的路由组件单独打包到其它单独的文件。从922k减少到487k。
你会发现1.a64085be1c517b7e1ef2.js
单独打包出来的200多k,能够看到下图包含了antd
的组件,这也证实antd按需加载的使用成功
在webpack4.x以前咱们使用extract-text-webpack-plugin
压缩抽取css。
在webpack4.x咱们须要使用mini-css-extract-plugin
插件进行抽取css,mini-css-extract-plugin详细使用文档 修改webpack.base.config.js
对css
,less
文件的处理,以下
{
test: /\.css$/,
use:[
{
loader:MiniCssExtractPlugin.loader,
options:{
hmr: utils.isDev(), // 开发的时候,修改css热更新,可是试了下不起做用
reloadAll:true,
}
},
// {
// loader: 'style-loader', // 建立 <style></style> // MiniCssExtractPlugin 有冲突,因此删掉
// },
{
loader: 'css-loader', // 转换css
options: { importLoaders: 1 }
}
]
},
{
test: /\.less$/,
use: [
{
loader:MiniCssExtractPlugin.loader,
options:{
hmr: utils.isDev(), // 开发的时候,修改less热更新可是试了下不起做用
reloadAll:true,
}
},
// {
// loader: 'style-loader',
// },
{
loader: 'css-loader',
},
{
loader: 'less-loader', // 编译 Less -> CSS
}
],
},
复制代码
由于style-loader
和MiniCssExtractPlugin.loader
有冲突,在配置的时删除了style-loader
对样式的处理,测试打包结果以下
此时已经成功把css样式从bundle抽离出。bundle包从487k减少到381k。
css
代码咱们打开任意一个打包后的css文件,发现css代码没有压缩。因此咱们须要对css压缩。安装optimize-css-assets-webpack-plugin
,optimize-css-assets-webpack-plugin详细使用文档
npm install -D optimize-css-assets-webpack-plugin
复制代码
在webpack.prod.config.js添加
optimization`属性(webpack4.x的代码压缩和拆包都在这里处理,这是和webpack3.x的不一样)
optimization: {
// 压缩css
minimizer: [
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
discardComments: { removeAll: true } // 移除注释
}
})
]
}
复制代码
测试打包:
对比一下两次打包的css文件大小,已经有必定的减少或者打开打包后的css文件代码也压缩了,这就表明压缩成功。
可是咱们发现js的bundle包变大了,这是为何呢? 由于咱们重写了optimization
属性的minimizer
,会把webpack自带的压缩方式给覆盖掉,这里须要咱们本身定义js的压缩方式。
这里和webpack3.x同样使用uglifyjs-webpack-plugin
插件,uglifyjs-webpack-plugin详细使用文档,安装
npm install -D uglifyjs-webpack-plugin
复制代码
而后在minimizer
属性下添加下边代码
// 自定义js优化配置,将会覆盖默认配置
new UglifyJsPlugin({
parallel: true, //使用多进程并行运行来提升构建速度
sourceMap: false,
uglifyOptions: {
warnings: false,
compress: {
unused: true,
drop_debugger: true,
drop_console: true,
},
output: {
comments: false // 去掉注释
}
}
})
复制代码
从新测试打包比较和以前的js文件的大小同样!到此咱们对css的处理告一段落!
在webpack3.x的时候咱们都是用webpack内置的CommonsChunkPlugin
来拆包。webpack4.x发生了很大变化。 webpack4.x要想进行拆包,须要先对splitChunks
有必定的了解。splitChunks
就算你什么配置都不作它也是生效的,源于webpack有一个默认配置,这也符合webpack4的开箱即用的特性。
splitChunks
的默认配置以下
splitChunks: {
// async表示只从异步加载得模块(动态加载import())里面进行拆分
// initial表示只从入口模块进行拆分
// all表示以上二者都包括
chunks: "async",
minSize: 30000, // 大于30k会被webpack进行拆包
minChunks: 1, // 被引用次数大于等于这个次数进行拆分
// import()文件自己算一个
// 只计算js,不算css
// 若是同时有两个模块知足cacheGroup的规则要进行拆分,可是maxInitialRequests的值只能容许再拆分一个模块,那尺寸更大的模块会被拆分出来
maxAsyncRequests: 5, // 最大的按需加载(异步)请求次数
// 最大的初始化加载请求次数,为了对请求数作限制,不至于拆分出来过多模块
// 入口文件算一个
// 若是这个模块有异步加载的不算
// 只算js,不算css
// 经过runtimeChunk拆分出来的runtime不算在内
// 若是同时又两个模块知足cacheGroup的规则要进行拆分,可是maxInitialRequests的值只能容许再拆分一个模块,那尺寸更大的模块会被拆分出来
maxInitialRequests: 3,
automaticNameDelimiter: '~', // 打包分隔符
name:true,
cacheGroups: {
// 默认的配置
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
// 默认的配置,vendors规则不命中的话,就会命中这里
default: {
minChunks: 2, // 引用超过两次的模块 -> default
priority: -20,
reuseExistingChunk: true
},
},
}
复制代码
打包测试,和不添加splitChunks
打包结果一致
由于默认是async
,只从异步加载的模块拆分。能够看到只有app.js
是入口文件(同步加载)没有对app.js
进行拆分。这个项目使用react-loadable
异步加载,本项目中有7个组件采用这种方式加载。可是从打包结果能够看异步加载的组件拆分出来10个chunk
。那么是为何呢?
使用webpack-bundle-analyzer
分析能够看出有三个chunk
是异步组件引用antd
中的组件进行拆分出来的chunk
。
假如咱们把chunks:async
改为chunks: "initial"
进行打包测试:
打包结果以下图
打包结果彻底不一样,由于app.js
是同步加载,app.js
被拆分,此时发现app.js
很是小。相反拆分出来的vendors~app.4fd9181b8f618e9fcac6.js
比较大,这是由于这个chunk
包含项目入口文件包含的全部第三方库。而异步加载的7个组件最终打包出来的仍是7个chunk
,这些异步加载的组件打包出来的每一个chunk
包含除了vendors~app.4fd9181b8f618e9fcac6.js
打包进去的第三方库之外的代码和组件自己代码。
那么同窗们会想了,把chunks:initial
改为chunks: "all"
会是什么结果呢?那么咱们进行测试一下
打包结果以下图
咱们知道all
不只从同步组件拆分,还从异步加载中拆分。
从打包结果看,是对initial
和async
的合并。即把异步组件拆分,也把同步组件拆分。
那么结论来了,由于本项目包含异步加载,须要对异步组件和同步组件同时拆分,因此此项目采用chunks: "all"
进行bundle
的拆分。若是项目中同步加载的组件chunk
不大,能够不对同步加载组件进行拆分,使用chunks:async
。固然若是项目中异步加载的组件chunk
不大,也能够不对异步加载组件进行拆分,使用chunks:initial
。固然也能够混用,对于缓存组单独设置
既使采用chunks: "all"
的方式咱们发现。拆分出来venders~app.js
的chunk
(node_modules
的第三方库)也比较大。随着第三方插件使用的增多这个chunk
会变的愈来愈大。因此咱们这里对他进行拆分。也就是把node_modules
中使用的插件也拆分红不一样的chunk
咱们拆包的策略是按照体积大小、共用率、更新频率从新划分咱们的包,使其尽量的利用浏览器缓存。
分析项目的插件,能够按几下分类插件
这些都是更新频率很是低,公用率高,说起大,因此单独抽取。只要这些包不更新,拆包的chunk
文件名就不会变。就一直缓存在浏览器
接下来咱们根据这个分类拆包,增长cacheGroups茶包的规则
antdui: {
priority: 2,
test: /[\\/]node_modules[\\/](antd)[\\/]/, //(module) => (/antd/.test(module.context)),
},
// 拆分基础插件
basic: {
priority: 3,
test: /[\\/]node_modules[\\/](moment|react|react-dom|react-router|react-router-dom|mobx|mobx-react|axios)[\\/]/,
}
复制代码
打包测试:
这里已经成功拆分出来antdui~app.js
,basic~app.js
。使用webpack-bundle-analyzer
分析能够精确的看到antd,moment|react|react-dom|react-router|react-router-dom|mobx|mobx-react|axios插件被拆分。
不知道同窗们有没有发现问题? 一、打包的hash
都是同样的,并且每次打包的hash还都不同。 解决方法, output
添加
chunkFilename: utils.assetsPath("js/[name].[chunkhash].js")
复制代码
new MiniCssExtractPlugin(options)
初始化参数
chunkFilename: utils.assetsPath('css/[id].[chunkhash].css'),
复制代码
二、venders~app.js
不见了 antdui~app.js
,basic~app.js
只是拆分了antdui|moment|react|react-dom|react-router|react-router-dom|mobx|mobx-react|axios
这些插件。还有其它的node_modules
的插件会被拆分到venders~app.js
。这里须要知道maxInitialRequests
这个属性的做用了。
maxInitialRequests
:最大的初始化加载请求次数,为了对请求数作限制,不至于拆分出来过多模块
由此发现,加载app.js
的时候有三个文件会同步加载app.js
,antdui~app.js
,basic~app.js
。
须要把maxInitialRequests
的值修改为更大,修改为5
maxInitialRequests: 5,
复制代码
打包测试以下图:
查看结果venders~app.js
从app.js
中拆分出来啦。
源码以下:
react+webpack4+react-router5+react-loadable+mobx
系列文章
一、react+webpack4搭建前端项目(一)基础项目搭建