构建工具的作用:
- 转换ES6语法、JSX
- CSS前缀补全、预处理器
- 压缩混淆
- 图片压缩
构建的演变:
ant+YUI Tool -> grunt -> fis3/gulp -> rollup/webpack/parcel
webpack核心概念
entry
entry用来指定webpack的打包入口。webpack会根据模块依赖关系构建依赖图,entry是图的唯一入口。entry是一个字符串(单入口)或对象(多入口)。
module.exports = { |
多入口
利用glob.sync动态获取entry和设置html-webpack-plugin数量。
const glob = require("glob"); |
output
output将编译后的文件输出到指定目录。[name]是占位符,对应多入口的entry键值对的key。
module.exports = { |
output占位符
占位符名称 | 含义 |
---|---|
[ext] | |
[name] | 文件名 |
[path] | 文件的相对路径 |
[folder] | 文件所在的文件夹 |
[contenthash] | 文件的内容hash,默认是md5生成 |
[hash] | 文件内容的hash,默认是md5生成 |
[emoji] | 一个随机的指代文件内容的emoji |
module.exports = { |
loaders
webpack开箱即用只支持JS和JSON两种文件类型,通过loaders去支持其他文件类型并把它们转化成有效的模块,并添加到依赖图中。loader本身是一个函数,接受源文件作为参数,返回转换的结果。
常见的loaders
名称 | 描述 |
---|---|
babel-loader | 转换ES6、ES7等JS新特性语法 |
css-loader | 支持.css文件的加载和解析 |
less-loader | 将less文件转换成css |
ts-loader | 将TS转换成JS |
file-loader | 进行图片、字体等的打包 |
raw-loader | 将文件以字符串的形式导入 |
thread-loader | 多进程打包JS和CSS |
loaders的用法
module字段的rules字符来制定,其是一个对象数组,每个对象有test和use两个属性,test指定匹配规则,use指定使用的loader名称。
module.exports = { |
babel-loader
babel的配置文件是.babelrc。presets和plugins增加ES6的babel preset配置。
//webpack.config.js |
css-loader
css-loader加载.css文件,并且转换成CommonJS对象,style-loader将样式通过<style>
标签插入到head中。loader调用是链式调用的,且调用顺序是从右到左的,所以下方先执行css-loader,再执行style-loader。
//webpack.config.js |
file-loader
file-loader解析图片、字体等文件。
module.exports = { |
url-loader
除了file-loader之外,url-loader也可以处理图片和字体,可以设置较小资源自动base64。url-loader内部也是使用了file-loader。不超过options的limit字节大小的图片会直接base64合入所在js文件。
module.exports = { |
postcss-loader
postcss-loader配合autoprefixer可以针对不同的浏览器版本为CSS加上相应的前缀。
module.exports = { |
px2rem-loader
CSS媒体查询实现响应式布局的缺点是需要写多套适配样式代码,rem就可以实现自适应CSS的开发。使用px2rem-loader会在页面渲染时计算根元素的font-size值。
module.exports = { |
raw-loader
raw-loader可以内联html、js等资源,资源内联可以对页面框架作初始化、上报相关打点。CSS内联避免页面闪动。可以减少HTTP网络请求数:比如小图片或字体。
|
plugins
plugins用于bundle文件的优化,资源管理和环境变量注入,作用于整个构建过程。
常见的plugins
名称 | 描述 |
---|---|
CommonsChunkPlugin | 将chunks相同的模块代码提取成公共js |
CleanWebpackPlugin | 清理构建目录 |
ExtractTextWebpackPlugin | 将CSS从bundle文件里提取成一个独立的CSS文件 |
CopyWebpackPlugin | 将文件或者文件夹拷贝到构建的输出目录 |
HtmlWebpackPlugin | 创建HTML文件去承载输出的bundle |
UglifyjsWebpackPlugin | 压缩JS |
ZipWebpackPlugin | 将打包出的资源生成一个zip包 |
MiniCssExtractPlugin
MiniCssExtractPlugin可以提取css为一个单独的文件。其余style-loader的功能互斥。用法如下。
; |
SplitChunksPlugin
webpack4内置的,替代CommonsChunkPlugin插件。
chunks参数有三种:
- async:异步引入的库进行分离(默认)
- initial:同步引入的库进行分离
- all:所有引入的库进行分离
module.exports = { |
mode
mode用来指定当前的构建环境:production、development和none。设置mode可以使用webpack内置的函数,默认是production。
选项 | 描述 |
---|---|
development | 设置process.env.NODE_ENV的值为development。开启NamedChunksPlugin和NamedModulesPlugin |
production | 设置process.env.NODE_ENV的值为production。开启FlagDependencyUsagePlugin等 |
none | 不开启任何优化选项 |
webpack进阶
监听
文件监听是在发现源码发生变化时,自动重新构建出新的输出文件。启动webpack时,带上--watch
参数或者在webpack.config.js
中设置watch: true
。该模式的缺陷是需手动刷新浏览器。
其监听的原理是轮询判断文件的最后编辑时间是否变化。某个文件发生了变化,并不会直接告诉监听者,而是先缓存起来,等aggregateTimeout。
module.exports = { |
文件指纹
打包后输出的文件名的后缀就是文件指纹,通常应用于版本管理,一般采用chunkhash。css文件常用contenthash。使用方法是设置output的filename,使用[chunkhash]来指定。
module.exports = { |
- hash,和整个项目的构建相关,只要项目文件有修改,整个项目构建的hash值就会改变
- chunkhash,和webpack打包的chunk有关,不同的entry会生成不同的chunkhash,不能与热更新一起使用
- contenthash,根据文件内容来定义hash,文件内容不变则contenthash不变
热更新
使用webpack-dev-server,其不刷新浏览器,不输出文件,而是放在内存中,使用HotModuleReplacementPlugin插件。Webpack Compiler将js编译成Bundle,HMR Server将热更新的文件输出给HMR Runtime,Bundle Server提供文件在浏览器的访问,HMR Runtime会被注入到浏览器,更新文件的变化,bundle.js即构建输出的文件。
使用webpack-dev-middleware,将webpack输出的文件传输给服务器,适用于灵活的定制场景。
代码压缩
包括HTML、CSS和JS文件的压缩。
JS:webpack内置了uglifyjs-webpack-plugin对JS进行了压缩。
CSS:使用optimize-css-assets-webpack-plugin以及cssnano(预处理器)对CSS文件进行压缩
module.exports = {
plugins: [
new OptimizeCSSAssetsWebpackPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano')
})
]
}HTML:修改html-webpack-plugin,设置压缩参数minify。
module.exports = {
plugins: [
new HTMLWebpackPlugin({
template: path.join(__dirname, 'src/search.html'), //模板所在位置
filename: 'search.html', //打包出来的文件名称
chunks: ['search'], //HTML文件使用的chunk
inject: true, //为true的话chunk中的js和css会自动注入到HTML中
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false
}
})
]
}
source map
通过source map可以定位到源代码,开发环境开启,线上环境关闭,线上排查问题的时候可以将sourcemap上传到错误监控系统。
关键字
eval:使用eval包裹模块代码
source map:产生.map文件
cheap:不包含列信息
inline:将.map作为DataUrl嵌入,不生成单独.map文件
module:包含loader的sourcemap
引入方法:
module.exports = { |
Tree Shaking
一个模块可能有多个方法,只要其中某个方法使用到了,则整个文件都会被打包到bundle中去,tree shaking就是只把用到的方法打入bundle,没用到的方法会在uglify阶段被擦除。必须是ES6的语法,CJS(require)的方式不支持。
使用:webpack默认支持,在.babelrc
里设置modules:false
即可。生产环境默认开启。
Dead Code Elimination的特点:
- 代码不会被执行,不可到达
- 代码执行的结果不会被用到
- 代码只会影响死变量(只写不读)
Scope Hoisting
构建后的代码存在大量函数闭包包裹代码,导致体积增大(模块越多越明显)。运行代码时创建的函数作用域变多,内存开销变大。
被webpack转换后的模块会带上一层包裹,import会被转换成__webpack_require
。打包出来的是一个IIFE,modules是一个数组,每一项是一个模块初始化函数,__webpack_require
用来加载模块,返回module.exports
,通过WEBPACK_REQUIRE_METHOD(0)
启动程序。
(function(modules) { |
Scope Hoisting原理
将所有模块代码按照引用顺序存放在一个函数作用域里,然后适当的重命名一些变量以防止命名冲突。通过scope hoisting可以减少函数声明代码和内存开销。production默认开启(ModuleConcatenationPlugin插件),必须使用ES6语法。