想要理解为什么要使用 webpack,我们先回顾下历史,在打包工具出现之前,我们是如何在 web 中使用 JavaScript 的,然后就能窥探webpack要解决的问题是什么,可以从两方面来看
传统的方式有什么问题
一是浏览器中运行 JavaScript 的方式的效率问题
我们知道有两种方式在浏览器上执行js
第一种方式,引用一些脚本来存放每个功能;此解决方案有个问题,如果文件比较多,比如有10个文件则需要发送十次http请求来加载,因为文件之中有函数的相互引用,则如果因为网络原因其中一个文件未加载成功,则页面功能就无法顺利执行。
第二种方式,使用一个包含所有项目代码的大型 .js 文件,但是这会导致作用域、文件大小、可读性和可维护性及文件加载时长性能方面的问题。
二是浏览器对新技术的支持不足
现代JavaScript应用程序的最大特点可能就是模块化了,这里面涉及很多新技术,比如ES6、TS的使用越来越普及,再比如Vue框架,包含了特定的语法:指令。
但是出现的问题是:这些新兴的技术并不是在所有的浏览器上都适用(浏览器不支持模块化,它不能像node.js那样快速地去本地加载一个个模块文件),都需要将源代码转化为可以直接在浏览器上运行的代码。
webpack是怎样做的
传统的任务构建工具基于 Google 的 Closure 编译器都要求你手动在顶部声明所有的依赖。然而像 webpack 一类的打包工具自动构建并基于你所引用或导出的内容推断出依赖的图谱。依赖自动收集这个特性与其它的如插件 and 加载器一道让开发者的体验更好。
模块
项目中使用的每个文件都是一个 模块
# ./index.js
import app from './app.js';
# ./app.js
export default 'the app';
通过互相引用,这些模块会形成一个图(ModuleGraph)数据结构。
在打包过程中,模块会被合并成 chunk。 chunk 合并成 chunk 组,并形成一个通过模块互相连接的图(ModuleGraph)。 那么如何通过以上来描述一个入口起点:在其内部,会创建一个只有一个 chunk 的 chunk 组。
# ./webpack.config.js
module.exports = {
entry: {
home: './home.js',
about: './about.js',
},
};
这会创建出两个名为 home 和 about 的 chunk 组。 每个 chunk 组都有一个包含一个模块的 chunk:./home.js 对应 home, ./about.js 对应 about
chunk的形式
chunk 有两种形式:
- initial(初始化) 是入口起点的 main chunk。此 chunk 包含为入口起点指定的所有模块及其依赖项。
- non-initial 是可以延迟加载的块。可能会出现在使用 动态导入(dynamic imports) 或者 plitChunksPlugin 时
每个 chunk 都有对应的 asset(资源)。资源,是指输出文件(即打包结果)。
# webpack.config.js
module.exports = {
entry: './src/index.jsx',
};
# ./src/index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import('./app.jsx').then((App) => {
ReactDOM.render(<App />, root);
});
这会创建出一个名为 main 的 initial chunk。其中包含:
- ./src/index.jsx
- react
- react-dom
以及除 ./app.jsx 外的所有依赖
然后会为 ./app.jsx 创建 non-initial chunk,这是因为 ./app.jsx 是动态导入的。
Output:
- /dist/main.js - 一个 initial chunk
- /dist/app.js - non-initial chunk
output(输出)
输出文件的名称会受配置中的两个字段的影响:
- output.filename - 用于 initial chunk 文件
- output.chunkFilename - 用于 non-initial chunk 文件
- 在某些情况下,使用 initial 和 non-initial 的 chunk 时,可以使用 output.filename
这些字段中会有一些 占位符。常用的占位符如下:
- [id] - chunk id(例如 [id].js -> 485.js)
- [name] - chunk name(例如 [name].js -> app.js)。如果 chunk 没有名称,则会使用其 id 作为名称
- [contenthash] - 输出文件内容的 md4-hash(例如 [contenthash].js -> 4ea6ff1de66c537eb9b2.js)