css文件独立打包
在当前项目中,我们是把所有的css、js及图片都打包到index.js里面的,这样会导致index.js会比较大,其实并不是css文件比较大,而是为了让css代码运行起来,需要加入css-loader 和 style-loader,这两个loader会添加大量的相关代码。这部分的代码量是非常大的,这样会使我们的index.js会变得很大。所以需要单独把css文件单独提取出来。
- 安装mini-css-extract-plugin
npm install mini-css-extract-plugin -D
- 在webpack.config.js中添加相关配置
// 修改loader
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
// 修改plugins
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].chunk.css'
})
代码中用MiniCssExtractPlugin.loader替换了style-loader,它的作用是把css-loader写到index.js里面的css代码进行剥离。我们知道对源码继续处理是loader的事情,但是剥离文件,生成文件等是在plugin里面做的,所以还需要在plugin中进行配置。
最后生成的index.css 和 login.css是该页面所有css文件的合并,这样减少请求。
js和css文件压缩
- 安装css-minimizer-webpack-plugin,terser-webpack-plugin
npm install css-minimizer-webpack-plugin -D
npm install terser-webpack-plugin -D
- 在webpack.config.js中添加相应配置
const TerserPlugin = require('terser-webpack-plugin')
const CssMiniPlugin = require('css-minimizer-webpack-plugin')
// 添加optimization属性
optimization: {
// 开启压缩,当处于开发模式下,开启这个配置也能压缩
minimize: true,
minimizer: [
new TerserPlugin({
extractComments: false
}),
new CssMiniPlugin({})
]
}
执行npm run build命令重新打包,可以看到css和js文件代码都进行了压缩。
tips: 在生成模式会自动进行压缩
tree-shaking
Tree-Shaking 是一个前端术语,本意为摇树的意思,通常用于描述移除 JS 中没用的代码,这样可以有效地缩减打包体积。关于Tree-Shaking的详细介绍可以参考这篇文章Tree-Shaking的使用。
在 Webpack 中满足什么条件, tree-shaking才会生效呢?需要满足一下条件:
打包模式mode必须是production
因为只有在生产模式下,webpack 才会利用terser-webpack-plugin去删除无用的代码。
通过解构的方式引用
// 触发 tree-shaking
import { get } from 'lodash-es'
console.log(get({ a: 1 }, 'a'))
// 不会触发 tree-shaking
import _ from 'lodash-es'
console.log(_.get({ a: 1 }, 'a'))
在我们自己写npm包的时候,最好通过 export 导出,比如:
// utils.js
export function a() {}
export function b() {}
// index.js
import { a } from './utils.js'
像上面这种写法是可以出发tree-shaking的,如果通过export default 导出,则不会触发tree-shaking,比如:
// utils.js
export default {
a() {},
b() {}
}
// index.js
import utils from './utils.js'
utils.a()
通过export default导出,引用的时候不能通过解构的方式引用,export default它是整体输出一个对象,但是你无法分清对象里面的函数a,b是否被调用,只要有一个方法被调用,那么这个对象必须存在,所以对象是没有办法做tree-shaking。
调用npm包必须使用ESM规范 lodash工具库是使用CJS模块规范的,当你即使通过解构的方式引用,也不会触发tree-shaking。所以,后来就出现了lodash-es,它是使用了ESM规范。所以,我们在引入第三方模块的时候尽量使用ESM规范的库。
// 不会触发tree-shaking
import { get } from 'lodash'
代码分割
我们来看看首页入口文件src/index.js引入了那些库:
import './css/public.css'
import './css/index.css'
import 'jquery'
import './js/public'
import './js/nav'
// 这部分代码只是为了演示如何进行代码分割
import { get } from 'lodash-es'
console.log(get({ a: 1 }, 'a'))
webpack.config.js配置
// 分析打包后的文件
const BundleAnalyzerPlugin =
require('webpack-bundle-analyzer').BundleAnalyzerPlugin
// optimization
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
extractComments: false
}),
new CssMiniPlugin({})
],
splitChunks: {
chunks: 'all',
minSize: 300 * 1024,
name: 'common'
}
}
// plugins
plugins: [
new BundleAnalyzerPlugin()
]
上面对splitChunks的配置的意思是,如果入口文件引入的第三方库总体积大小超过了300kb,那么就个拆分出来,文件名称为common.js,该文件包含了jquery, lodash-es及flexslider。如下如所示:
如果把 minSize 设置为 400 * 1024,代码就不会分割,第三方库会打到入口文件中,如下图:
现在如果我想把jquery单独拆分出来怎么办呢?配置如下:
// optimization
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
extractComments: false
}),
new CssMiniPlugin({})
],
splitChunks: {
chunks: 'all',
minSize: 280 * 1024,
name: 'common',
cacheGroups: {
jquery: {
name: 'jquery',
test: /jquery\.js/,
chunks: 'all'
}
}
}
}
因为jquery库的体积是280kb,所以需要把minSize调成小于280kb,执行打包后jquery会被单独抽离出来,其他的库如lodash和flexslider的体积不满足要求,所以不会被拆分,而是被打入到入口文件index.js中,如下图:
如果我们想把lodash-es也拆分出来呢?只需要把minSize调低到满足lodash-es大小即可:
splitChunks: {
chunks: 'all',
minSize: 30 * 1024,
name: 'common',
cacheGroups: {
jquery: {
name: 'jquery',
test: /jquery\.js/,
chunks: 'all'
},
'lodash-es': {
name: 'lodash-es',
test: /lodash-es/,
chunks: 'all'
}
}
}
可以看到,jquery,lodash-es及flexslider都被拆分了,因为这三个库都满足所设置的条件。
注意:如果没有设置cacheGroups,那么minSize就是针对的整体包的体积大小,而不是单个库的体积大小。如果设置了cacheGroups,minSize就是针对单个库的大小进行拆分。
ejs组件抽离
- 安装 ejs-loader
npm install ejs-loader -D
- 修改webpack.config.js配置
{
test: /\.ejs$/,
use: {
loader: 'ejs-loader',
options: {
esModule: false
}
}
}
- 提取模板,新建ejs/footer.ejs、ejs/header.ejs文件
// header.ejs 把原来index.html中的header部分直接移植过来即可
<div class="head">
<div class="wrapper clearfix">
......
</div>
</div>
- 修改index.html
// 引入头部组件
<%=require('./ejs/header.ejs')() %>
- require里面可以传入参数,实现组件动态化
// index.html
<%=require('./ejs/header.ejs')({title: '首页'}) %>
// header.ejs 接受页面传递过来的值
<li><a href="index.html"><%=title%></a></li>