每次看一些webpack相关的学习视频总是看了之后记不住,也不知道怎么应用,看了眼官方文档感觉比视频讲的好很多,算是一个记录学习的文档吧
基础知识
Webpack定义
是一个静态的模块化打包工具,内部会从一个/多个入口点构建一个依赖图,然后将项目中所需要的每一个模块组合为一个或多个bundles
什么是静态资源?就是在开发阶段可以直接被webpack引用的资源
入口
module.exports = {
entry: "xxxxx"
}
入口可以配置多个
module.exports = {
entry: {
app: './src/app.js',
adminApp: './src/adminApp.js',
},
};
此外还有描述入口的对象,有以下的这几种属性
- dependOn: 当前入口所依赖的入口,必须在该入口被加载前被加载,不能是循环引用的
- filename: 指定要输出的文件名称
- import: 启动的时候需要加载的模块
- runtime: 运行时chunk的名字,不能指向已经存在的入口名称,否则会抛出错误
module.exports = {
entry: {
a2: 'dependingfile.js',
b2: {
dependOn: 'x2',
import: './src/app.js',
},
},
};
通常多入口会分离app和vendor(第三方库)入口
出口
注意下,出口必须是绝对路径
const path = require("path");
module.exports = {
entry: "xxxxx",
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'xxx'
}
}
如果出口要有多个,应该使用占位符来确保每个文件有唯一的名称
loader
为啥要用loader呢? 因为webpack自己只支持处理js和json格式的文件,也就是说他连css格式的都处理不了,那就得引入东西去进行转化呗,这个东西就是loader
作用:用于转换某些类型的模块
loader执行顺序:从右到左/从下到上
const path = require("path");
module.exports = {
entry: "xxxxx",
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'xxx'
},
module: {
rules: [{test: /\.jsx?$/i, use: 'raw-loader'}]
}
}
注意 用正则表达式的时候不要加引号
举例:配置sass-loader
module.exports = {
module: {
rules: [
{
test: /.sass$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true,
},
},
{ loader: 'sass-loader' },
],
},
],
},
};
plugin
可以用于执行范围更广的任务,如:打包优化,资源管理,注入环境变量,相当于是插件,可以执行很多loader无法实现的事情
在进行plugin的时候,向plugins属性传入一个new实例
模块热替换
HMR,这个功能会在应用程序运行过程中,替换、添加/删除模块,而无需重新加载整个页面
常见的loader
- 加载css文件
module.exports = {
module: {
rules: [
{test: /\.css/, use: ['style-loader', 'css-loader']}
]
}
}
- 加载image文件和字体
module.exports = {
module: {
rules: [
{test: /\.(png|svg|jpg|jpeg|gif)/i, type: "asset/resource"},
{test: /\.(woff|ttf|otf)/i, type: "asset/resource"}
]
}
}
- 加载某些数据,比如csv、tsv、xml
module.exports = {
module: {
rules: [
{test: /\.(csv|tsv)/i, use:['csv-loader']},
]
}
}
module.exports = {
module: {
rules: [
{test: /\.(xml)/i, use:['xml-loader']},
]
}
}
sourcemap
为什么需要sourcemap?当你写的代码过多,被分离成不同文件的时候,如果代码出错了,在浏览器打包后,只知道是文件出错了,但是不知道出错的地方在哪里,哪个文件出错了
因此产生了sourcemap sourcemap配置的时候是这样的
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
+ devtool: 'inline-source-map',
plugins: [
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
watch mode
watch的话可以在一个文件被更新的时候,代码被重新编译,不必再去重写手动运行创建 在package.json里的scripts这样
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
+ "watch": "webpack --watch",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"html-webpack-plugin": "^4.5.0",
"webpack": "^5.4.0",
"webpack-cli": "^4.2.0"
},
"dependencies": {
"lodash": "^4.17.20"
}
}
这样还有个缺点,为了查看修改后的实际效果,需要刷新下浏览器,如果能有类似于liveserver的效果就好了,而很幸运,webpack也有这个功能,他叫devServer
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
devtool: 'inline-source-map',
+ devServer: {
+ static: './dist',
+ },
plugins: [
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
+ optimization: {
+ runtimeChunk: 'single',
+ },
};
代码分离
什么叫代码分离,就是把代码分离到不同的bundle中,然后可以按需加载/并行加载这些文件,代码分离可以获取更小的bundle,并且控制资源加载的优先级,这样的话就可以大大提高首页渲染的速度了
SplitChunksPlugin
可以消除重复的模块
const path = require('path');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
another: './src/another-module.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
+ optimization: {
+ splitChunks: {
+ chunks: 'all',
+ },
+ },
};
预获取 预加载
prefetch/preload
prefetch: 将来某些导航下可能需要的资源
preload:当前导航下可能需要的资源
- preload在父chunk加载时,以并行的方式开始加载,prefetch会在父加载结束后开始加载;
- prefetch在浏览器闲置的时候下载,preload有中等优先级,并立即下载
- prefetch用于未来的某个时刻,preload会在父chunk中立即请求,用于当下时刻
写webpack魔法注释即可
import(/* webpackPrefetch: true */ './path/to/LoginModal.js');
TreeShaking
只用于ES Modules
怎么标除文件没有副作用
在package.json的sideEffects属性可以标记,这样的话可以安全的删除没有用到的export
用于移除JS上下文的未引用代码,webpack会自动识别出没有引用的代码,然后怎么去删除?
-
依赖收集:webpack在打包时,会将每个模块中的导出语句格式化后保存在一个数组中,并最终存储在
ModuleGraph
,它记录了模块间的依赖关系 -
标记:遍历所有模块,如果用到了收集来的导出的变量,将会打上已使用的标记
-
删除:通过默认的压缩工具 taser 的 taser-webpack-plugin,对于没有打标记的导出,就会被删除掉
module.exports = {
mode: 'development',
devtool: 'source-map',
optimization: {
minimize: true, // 增加了这一行
usedExports: true
}
}