webpack4.X 实战(四):企业SPA 24点总结(下)node
本文纲要:项目打包配置、优化jquery
webpack 配置文件输出路径时,经常使用三种配置webpack
配置一:出口 entry.path
,配置全部文件的输出路径git
// webpack 配置
const path = require('path');
const rootPath = path.resolve(__dirname, '');
module.exports = {
output: {
path: path.resolve(rootPath, './dist')
}
}
// 打包后的文件,全部资源 统一输出到 项目根目录下的 dist 文件夹下
复制代码
配置二:loader
的 options.outputPath
,配置 当前 loader
匹配到文件 的输出路径github
这项配置的文件输出 以
entry.path
路径为标准web
// webpack 配置
module.exports = {
output: {
path: path.resolve(rootPath, './dist')
},
module: {
rules: [{
test: /\.(png|jpg|gif|ico)$/,
use: [{
loader: 'url-loader',
options: {
limit: 8192,
name: '[name].[ext]',
outputPath: 'static/assets/'
}
}]
}]
}
};
// 打包后的文件,图片资源 统一输出到 项目根目录下的 `dist/static/assets` 文件夹下
复制代码
配置三:name
选项的值,可改变当前包的输出路径vue-router
出口
entry.chunkFilename
,改变全部chunk
包的输出位置(runtimeChunk
除外)
// webpack 配置
module.exports = {
output: {
path: path.resolve(rootPath, './dist'),
chunkFilename: 'static/js/[name].js'
}
};
// 打包后的 chunk 包,统一输出到 dist/static/js 文件夹下
复制代码
optimization.splitChunks
的name
值,以output.chunkFilename
路径为标准
// webpack 配置
module.exports = {
output: {
path: path.resolve(rootPath, './dist'),
chunkFilename: 'static/js/[name].js'
},
optimization: {
splitChunks: {
cacheGroups: {
libs: {
test: /[\\/]node_modules[\\/]/,
priority: 20,
name: '../libs/index',
chunks: 'all'
}
}
}
}
};
// 打包后的 splitChunks 包,统一输出到 dist/static/js 文件夹下
复制代码
【特殊】
optimization.runtimeChunk
的name
值,以output.path
路径为标准
// webpack 配置
module.exports = {
output: {
path: path.resolve(rootPath, './dist'),
chunkFilename: 'static/js/[name].js'
},
optimization: {
runtimeChunk: {
name: 'static/runtime/index'
}
}
};
// 打包后的 runtime 包,统一输出到 dist/static/runtime 文件夹下
复制代码
做用:配置 发布路径,让页面请求 CDN 上的静态资源,速度更快
配置 公共发布路径: output.publicPath
// webpack 配置
module.exports = {
output : {
publicPath : 'http://aaa.com/'
}
};
复制代码
单独配置静态资源: loader options
module.exports = {
module: {
rules: [{
test: /\.(png|jpg|gif|ico)$/,
use: [{
loader: 'url-loader',
options: {
limit: 8192,
name: '[name].[hash:8].[ext]',
outputPath: 'static/assets/',
publicPath: 'http://aaa.com/assets/'
}
}]
}]
}
};
复制代码
webpack 打包后的模块 默认命名规则:
webpack 默认为给各个模块分配一个 id,做为模块的名称
默认 id 是根据模块引入的顺序,赋予一个整数(0、一、二、3……)
默认 id 用来处理模块之间的依赖关系
经过配置 不一样的 hash
,缓存文件
webpack 内置了多种可以使用 hash,官网解释分别以下:
hash
: the hash of the module identifier
chunkHash
: the hash of the chunk content
contentHash
: the hash of extracted content
设置 哈希 的长度
// 全局设置 hash 长度
output.hashDigestLength
复制代码
// 局部设置 hash 长度
[hash:16] 等方式
复制代码
配置 什么时候生成 hash
// output.hashDigest
复制代码
如何选择正确的 hash
?
JS、分离后的CSS、资源文件(图片、字体图标)都使用 hash
每次打包 JS、分离后的CSS 文件 哈希值 都同样
项目代码没有变化,再次打包;全部文件哈希值不变
只要JS、CSS 有一处变化,全部 JS、分离后的CSS 文件 哈希值 都变化
资源文件(图片、字体图标)没更新,哈希值不变;使用 hash
,是个不错的选择
JS、分离后的CSS、资源文件(图片、字体图标)都使用 chunkHash
每次打包 同一个页面的 JS、分离后的CSS 哈希值同样;不一样页面 JS、CSS文件 哈希值不同
项目代码没有变化,再次打包;全部文件哈希值不变
项目代码有变化,只是有更改的JS、对应的CSS文件 哈希值变化;其余的文件 哈希值不变
资源文件(图片、字体图标)不能使用 chunkHash
,会报错
最佳实践:
JS 文件:[name].[chunkHash:8].js
分离后的CSS 文件:[name].[contentHash:8].css
不会 随着JS的改变,而更改
hash
资源文件(图片、字体图标):[name].[hash:8].[ext]
使用 clean-webpack-plugin
插件,在打包时先清空上一次打包的文件
若是不清空,打包后的文件体积会愈来愈大
安装
npm i clean-webpack-plugin@1.0.0 -D
复制代码
配置
// webpack 中配置
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
plugins: [
new CleanWebpackPlugin(['dist']) // 值为根目录下 清空的文件夹
]
}
复制代码
webpack 提供内置插件:ContextReplacementPlugin
// 以下:只打包 moment 的中文包
...
plugins: [
new webpack.ContextReplacementPlugin(
/moment[/\\]locale$/,
/zh-cn/,
),
]
...
复制代码
新旧版本 API更新
webpack3 使用 内置插件 optimize.CommonsChunkPlugin
来分割代码
webpack4 移除内置插件,新增 optimization.splitChunks
、optimization.runtimeChunk
来分割代码
Webpack 4 的 Code Splitting 最大的特色就是配置简单(0配置起步),和基于内置规则自动拆分
为何要分割代码?
编译:减小编译的总体大小,以提升构建性能
运行:减少文件体积,提升加载速度
这是相对的,在代码分割减少文件体积的同时,也应该考虑 合理的HTTP请求次数
一般分割哪些代码?
提取公有代码
提取经常使用库代码
提取 webpack 的 runtime (运行时) 代码
webpack4默认的分割代码机制,知足以下条件的默认都会被分割
新 bundle 被两个及以上模块引用,或者来自 node_modules
新 bundle 大于 30kb (压缩以前)
异步加载并发加载的 bundle 数不能大于 5 个
初始加载的 bundle 数不能大于 3 个
默认配置以下:
// 默认配置以下:
module.exports = {
// ...
optimization: {
splitChunks: {
chunks: 'async', //默认只做用于异步模块,为`all`时对全部模块生效,`initial`对同步模块有效
minSize: 30000, //合并前模块文件的体积
maxSize: 0,
minChunks: 1, //最少被引用次数
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~', //自动命名链接符
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10 // 同时知足条件的,优先级更高的生效
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
};
复制代码
自定义代码分割 optimization.splitChunks.cacheGroups
optimization.splitChunks.cacheGroups
下每个chunk
的配置 选项 同optimization.splitChunks
的配置选项
若是设置了将 覆盖
optimization.splitChunks
下的配置选项;若是没设置 就继承
// 将代码库 单独打包
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
libs: {
test: /[\\/]node_modules[\\/]/,
priority: 20,
name: '../libs/index',
chunks: 'all'
}
}
}
}
};
复制代码
将 webpack runtime
代码单独打包
// 将代码库 单独打包
module.exports = {
optimization: {
runtimeChunk: {
name: 'static/runtime/index'
}
}
};
复制代码
特殊场景下,须要咱们 手动代码分割,可参考以下案例
默认状况下,webpack 单线程处理任务
因为运行在 Node.js 之上的 Webpack 是单线程模型的
因此Webpack 须要处理的事情须要一件一件的作,不能多件事一块儿作
HappyPack 可让 webpack 同时处理多个任务
它将任务分解给多个子进程去并发执行,子进程处理完成后再将结果发送给主进程中
从而减小总的构建时间,提高构建效率
HappyPack 处理 JS,配置以下
// 安装
npm i happypack@5.0.1 -D
复制代码
// webpack 配置以下
const os = require('os');
const HappyPack = require('happypack');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module.exports = {
module: {
rules: [{
test: /\.js$/,
// 把对.js 的文件处理交给id为 happyBabel 的 HappyPack 的实例执行
loader: 'happypack/loader?id=happyBabel'
}]
},
plugins: [
new HappyPack({
id: 'happyBabel', // 用id来标识 happypack处理那里类文件
// 如何处理 用法和loader 的配置同样
loaders: [{
loader: 'babel-loader?cacheDirectory=true'
}],
threadPool: happyThreadPool, // 共享进程池
verbose: true // 容许 HappyPack 输出日志
})
]
};
复制代码
说明 module.rules.loader
在 Loader 配置中,全部文件的处理都交给了 happypack/loader
去处理
使用紧跟其后的 ?id=happyBabel
去告诉 happypack/loader
去选择哪一个 HappyPack 实例去处理文件
说明 HappyPack 参数
id
: String 用惟一的标识符 id 来表明当前的 HappyPack 是用来处理一类特定的文件
loaders
: Array 用法和 webpack Loader 配置中同样
threads
: Number 表明开启几个子进程去处理这一类型的文件,默认是3个,类型必须是整数
verbose
: Boolean 是否容许 HappyPack 输出日志,默认是 true
threadPool
: HappyThreadPool 表明共享进程池(即多个 HappyPack 实例使用同一个共享进程池中的子进程去处理任务,以防止资源占用过多)
verboseWhenProfiling
: Boolean 开启 webpack --profile ,仍然但愿HappyPack产生输出
debug
: Boolean 启用debug 用于故障排查;默认 false
注意:
HappyPack 在处理 CSS / SCSS 时,须要单首创建一个 postcss.config.js
文件;不然会报错
HappyPack 对 url-loader 和 file-loader 的支持度有限
存疑
问题:项目中只配置了子线程编译 JS,编译速度却更慢了(项目没有分离CSS;项目代码很少,生产环境打包后体积 1.13M)
缘由猜想:HappyPack 多线程的原理是,先开启子线程处理,完成后再讲结果传递给主线程,这个时间 超过了 多线程打包节省的时间(因为须要编译的JS很少)
认识 DLL
能够包含给其余模块调用的函数和数据
用过 Windows 系统的人应该会常常看到以 .dll 为后缀的文件
web 项目构建接入动态连接库,须要完成如下事情:
把网页依赖的基础模块抽离出来,打包到一个个单独的动态连接库中(一个动态连接库中能够包含多个模块)
当须要导入的模块存在于某个动态连接库中时,这个模块不能被再次被打包,而是去动态连接库中获取
页面依赖的全部动态连接库须要被加载
web 项目构建接入动态连接库,好处:提高构建速度(每次构建 不用再重复打包)
webpack 已经内置了对动态连接库的支持
DllPlugin 插件:打包出一个个单独的动态连接库文件
DllReferencePlugin 插件:在主要配置文件中去引入 DllPlugin 插件打包好的动态连接库文件
实践 DLL
// webpack/dll.config.js 配置文件
const path = require("path");
const webpack = require("webpack");
const libs = require('../configs/dll.config');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
entry: {
// 把 vue 相关模块的放到一个单独的动态连接库
vue: ['vue', 'vue-router'],
// 把项目须要全部的 lib 放到一个单独的动态连接库
lib: ['jquery', 'moment'],
},
output: {
path: path.resolve(__dirname, './dll'),
filename: "[name].js",
library: "_dll_[name]"
},
plugins: [
new webpack.DllPlugin({
name: "_dll_[name]",
path: path.join(__dirname, 'dll', 'manifest.json'),
}),
// 清空 dll 文件夹
new CleanWebpackPlugin(['dll']),
]
}
复制代码
配置 npm script
{
"scripts": {
"dll": "webpack --config webpack/dll.config.js",
}
}
复制代码
安装 依赖包
npm i clean-webpack-plugin@1.0.0 -D
复制代码
// webpack 配置文件
const webpack = require('webpack');
module.exports = {
plugins: [
// 启用 dll
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./dll/manifest.json')
})
]
};
复制代码
// 新dowload下来的项目,须要先打 DLL 包,再运行项目
复制代码
webpack 内置插件,支持全局引入第三方库
// webapck.config.js 文件中配置以下:
const webpack = require('webpack');
plugins:[
new webpack.ProvidePlugin({
$:"jquery"
})
],
复制代码
对比:
全局引入:
全局引入后,项目代码中直接使用便可
若是没有地方用,第三方库将不会被打包
局部引入:
每一个文件中须要的话,都单独引入
只要引入了,即便没用,第三方库也会打包
插件:webpack-bundle-analyzer
生成的报告中有三种尺寸大小:
stat
: 压缩等转换以前的输入文件大小(从webpack的stat对象里获得的
parsed
: webpack 打包压缩后 输出的JS文件大小(不含资源文件、分离后的CSS等)
gzip
: 通过 gzip 压缩后的大小
使用
// 安装
npm i webpack-bundle-analyzer@3.0.3 -D
复制代码
// webpack 配置
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
};
// 打包完成后 自动在浏览器中显示分析结果
复制代码
插件:webpack-chart
插件:webpack-analyse
部署打包 配置
// webpack 配置文件
module.exports = {
// 精简 终端输出(打包部署)
stats: {
modules: false,
children: false,
chunks: false,
chunkModules: false
}
};
复制代码
本地运行 配置 webpack-dev-server
// webpack 配置文件
module.exports = {
devServer: {
// 精简 终端输出(本地运行)
stats: {
modules: false,
children: false,
chunks: false,
chunkModules: false
}
}
};
复制代码
以下插件,可显示打包进度,具体配置可查阅 GitHub
progress-bar-webpack-plugin
nyan-progress-webpack-plugin
progress-bar-webpack-plugin
progress-webpack-plugin