1.Webpack中的module指的是什么?
What is a webpack Module
In contrast to Node.js modules, webpack modules can express their dependencies in a variety of ways. A few examples are:
An ES2015
importstatementA CommonJS
require()statementAn AMD
defineandrequirestatementAn
@importstatement inside of a css/sass/less file.An image url in a stylesheet
url(...)or HTML<img src=...>file
其实Webpack中提到的module概念,和平时前端开发的module概念是一样的.
Webpack 支持ESModule, CommonJS, AMD, Assets等.
简单说几个模块的引入和导出方式
1.1.ESM
关键字export允许将ESM中的内容暴露给其他模块;
关键字import允许从其他模块获取引用到的ESM中
import { getInfo } from './user.js'
export { user }
可以设置package.json中的属性显示设置文件模块类型
在package.json中
设置‘’type‘’: ‘’module“ 会强制package.json下的所有文件使用ECMASript模块
设置“type”: "commonjs" 会强制使用CommonJS模块
1.2 CommonJS
module.export允许将CommonJS中的内容暴露给其他模块;
require允许从其他模块获取引用到CommonJS中
const path = require('path')
module.exports = {
user,
admin
}
所以webpack modules如何表达自己的各种依赖关系?
ESM import 语句
CommonJS require()语句
AMD define 和 require语句
css/sass/less文件中的@import语句
stylesheet url(...)或者HTML文件中的图片和链接
那么问题又来了,我们常说的chunk和bundle的区别是什么?
1. Chunk
Chunk: This webpack-specific term is used internally to manage the bundling process. Bundles are composed out of chunks, of which there are several types (e.g. entry and child). Typically,chunks directly correspond with the output bundles however, there are some configurations that don't yield a one-to-one relationship.
Chunk是webpack打包过程中modules的集合,是打包过程中的概念
webpack的打包是从一个入口模块开始的,入口模块引用其他模块,模块再引用模块
webpack通过引用关系逐个打包模块,这些module就形成了一个chunk
当然,如果有多个入口模块,可能会产出多条打包路径,每条路径都会形成一个chunk
2.Bundle
Bundle: Produced from a number of distinct modules, bundles contain the final versions of source files that have already undergone the loading and compilation process
Bundle是最终输出的一个或者多个打包好的文件
3. Chunk 和 Bundle的关系
大多数情况下,一个chunk会产生一个bundle
例子:webpack.config.js
module.exports = {
mode: "porduction",
entry: {
main: ".src/index.js"
},
output: {
filename: "[name].js"
}
}
// Result
// Asset Size Chunks Chunk Names
// main.js 72.5 KiB 0 [emitted] main
// Entrypoint main = main.js
但是当我们开启source-map后,chunk和bundle就不是一对一的关系了
module.exports = {
mode: "porduction",
entry: {
main: ".src/index.js"
},
output: {
filename: "[name].js"
},
devtool: "source-map"
}
// Result
// Asset Size Chunks Chunk Names
// main.js 72.6 KiB 0 [emitted] main
// main.js.map 682 KiB 0 [emitted] [dev] main
// Entrypoint main = main.js main.js.map
可以看一下webpack的输出,chunknames只有一个mian,而输出了两个bundle, main.js, main.js.map
所以,总结:
Chunk是过程中的代码块,bundle是打包结果输出的代码块,chunk在构建完成就呈现为bundle
4. 生成chunk的几种方式
entry配置一个key,value为数组
module.exports = { mode: "production", entry: { main: ["./src/index.js", "./src/add.js"] },
output: { filename: "[name].js"
},
}
// Result
// Asset Size Chunks Chunk Names
// main.js 72.8 KiB 0 [emitted] main
// Entrypoint main = main.js
// 可以看到这种情况,也只会产生一个chunk
entry配置多个key
module.exports = {
mode: "production",
entry: {
main: ["./src/index.js", "./src/add.js"]
},
output: {
filename: "[name].js"
},
}
// Result
// Asset Size Chunks Chunk Names
// common.js 1.03 KiB 0 [emitted] common
// main.js 72.8 KiB 1, 0 [emitted] main
// Entrypoint main = main.js
// Entrypoint common = common.js// Entrypoint main = main.js
// 可以看到这种情况,产生了common和index两个chunk,配置的key也就会被用为chunkName
// 而output中filename字段,将被用作bundle的名称
split chunk
add.js和multiply.js都引用common.js
// add.js
import CommonFn from './common.js';export default function add(a, b) { return CommonFn(a + b);}
// multiply.js
import CommonFn from './common.js';export default function multiply(a, b) { return CommonFn(a * b);}
安装lodash
npm i lodash
修改index.js
// index.js
import { once} from 'lodash';import Add from './add.js';import Multiply from './multiply.js';const onceAdd = once(Add);const addRes = onceAdd(1, 3);const mulRes = Multiply(2, 4);console.log(addRes);console.log(mulRes);
修改webpack.config.js
module.exports = {
mode: "production",
entry: {
main: "./src/index.js",
other: "./src/multiply.js"
},
output: {
filename: "[name].js"
},
optimization: {
runtimeChunk: "single",
splitChunks:{
cacheGroups: {
commons: {
chunks: "initial",
minChunks: 2,
minSize: 0 // 默认是20000,这里为了演示生成commonChunks,对最小体积不做限制
},
vendor: {
test: /node_modules/,
chunks: "initial",
name: "vendor",
enforce: true
}
},
}
}
}
// Result
// Asset Size Chunks Chunk Names
// commons~main~other.js 322 bytes 0 [emitted] commons~main~other
// main.js 397 bytes 2 [emitted] main
// other.js 71 bytes 3 [emitted] other
// runtime.js 1.46 KiB 1 [emitted] runtime
// vendor.js 71.5 KiB 4 [emitted] vendor
// Entrypoint main = runtime.js commons~main~other.js vendor.js main.js
// Entrypoint other = runtime.js commons~main~other.js other.js
一共产生了5个chunk
entry main
entry other
runtimeChunk: single
splitChunks commons
splitChunks vendor
配置介绍: webpack.docschina.org/configurati… #optimizationruntimechunk 官⽅方runtime解释:www.webpackjs.com/concepts/ma…
The runtime, along with the manifest data, is basically all the code webpack needs to connect your modularized application while it's running in the browser. It contains the loading and resolving logic needed to connect your modules as they interact. This includes connecting modules that have already been loaded into the browser as well as logic to lazy-load the ones that haven't.
2.比较重要的一些概念
1.Compiler 对象包含了webpack环境所有的配置信息,包含options,loaders,plugins这些信息,这个对象在webpack启动时候被实例化,它是全局唯一的,可以简单的把它理解为webpack实例
2.Compilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。当webpack以开发模式运行时,每当检测到一个文件变化,一次新的Compilation将被创建。Compilation对象也提供了很多事件回调插件做扩展。通过Compilation也能读取都Compiler对象
plugin和loader分别是做什么的?怎么工作的?
1.loader
一句话描述:模块转换器,将非js模块转换为webpack能识别的js模块
loader让webpack能够去处理那些非JavaScript的文件
loader可以将所有类型的文件转换为webpack能够处理的有效模块,然后就可以利用webpack的打包能力,对它们进行处理
本质上,webpack loader将所有类型的文件,转换为应用程序的依赖图(和最终的bundle)可以直接引用的模块
2plugin
一句话描述:扩展插件,在webpack运行的各个阶段,都会广播出去相对应的事件,插件可以监听到这些事情的发生,在特定的时机做相对应的事情
loader被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。
插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务
在webpack运行的生命周期中会广播出各种事件,plugin就可以监听这些事件,在触发时通过webpack提供的api改变输出结果
在插件中,可以拿到compiler和compilation的引用对象,使用它们的广播事件,这些事件可以被其他插件监听到,或者对它们做出一定修改,其他插件拿到也是变化的对象
3.能简单描述一下webpack的打包流程吗
1.初始化参数:从配置文件和shell语句中读取与合并参数,得出最终的参数
2.开始编译:用上一步得到的初始化compiler对象,加载所有配置的插件,执行对象的run方法开始执行编译
3. 确定入门:根据配置中的entry找出所有的入口文件
4. 遍历模块: 从入口文件出发,调用所有配置的loader,对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
5. 完成模块编译:在经过第 4 步使⽤用 Loader 翻译完所有模块后,得到了了每个模块被翻译后的最终内容以及它们之间的依赖关系。