安装一个将被打包到生产环境 bundle 的包时,应该使用 npm install --save;而安装一个用于开发环境的包时(例如代码检查工具、测试库等),应该使用 npm install --save-dev。
考虑到使用 CLI 这种方式运行本地 webpack 副本不是特别方便,可以在 package.json 文件中添加 npm script 以设置一个快捷方式:现在可以使用 npm run build 命令替代之前使用的 npx 命令。注意,使用 npm scripts 便可以像使用 npx 那样通过模块名引用本地安装的 npm 包。
module loader 可以链式调用。链中的每个 loader 都将对资源进行转换,不过链会逆序执行。第一个 loader 将其结果(被转换后的资源)传递给下一个 loader,依此类推。最后,webpack 期望链中的最后的 loader 返回 JavaScript。
请检查页面(不要查看页面源代码,它不会显示结果,因为 <style> 标签是由 JavaScript 动态创建的)并查看页面的 head 标签以观察 webpack 做了什么。可以发现,head 标签包含了原本不存在的 style 块元素,也就是在 index.js 中导入的 CSS 文件中的样式。
import MyImage from './my-image.png' 将会处理图像,将其添加到 output 目录,并且 MyImage 变量将包含该图像在处理后的最终的 url。如前所示,在使用 css-loader 时,处理 CSS 中的 url('./my-image.png') 也会发生类似过程。loader 会识别这是一个本地文件,并将 './my-image.png' 路径替换为 output 目录中图像的最终路径。而 html-loader 也以相同方式处理 <img src="./my-image.png" />。
通常比较推荐的做法是在每次构建前清理 /dist 文件夹,那么构建后就只会存在将要用到的文件。可以使用 output.clean 配置选项实现这个需求。
当 webpack 打包源代码时,可能会很难追踪到错误和警告在源代码中的原始位置。例如,如果将三个源文件(a.js,b.js 和 c.js)打包到一个 bundle(bundle.js)中,而其中一个源文件包含错误,那么堆栈跟踪就会直接指向到 bundle.js,却无法准确知道错误来自于哪个源文件,所以这种提示通常无法提供太多帮助。
为了更容易地追踪错误与警告在源代码中的原始位置,JavaScript 提供了 source map 功能,可以帮助将编译后的代码映射回原始源代码。source map 会直接告诉开发者错误来源于哪一个源代码
webpack-dev-server 会将在 output.path 中定义的目录中的 bundle 文件作为可访问资源部署在 server 中,即文件可以通过 http://[devServer.host]:[devServer.port]/[output.publicPath]/[output.filename] 进行访问。
在配置文件中配置 dependOn 选项,以在多个 chunk 之间共享模块.如果想要在一个 HTML 页面上使用多个入口起点,还需设置 optimization.runtimeChunk: 'single'
使用 optimization.splitChunks 配置选项后构建,将会发现 index.bundle.js 和 another.bundle.js 已经移除了重复的依赖模块。从插件将 lodash 分离到单独的 chunk,并且将其从 main bundle 中移除,减轻了 bundle 大小。
webpack 会在打包后生成可部署的 /dist 目录,并将打包后的内容放在此目录。一旦 /dist 目录中的内容部署到服务器上,客户端(通常是浏览器)就能够访问此服务器以获取站点及其资源。由于获取服务器资源是比较耗费时间的操作,因此浏览器使用了一种名为 缓存 的技术。命中缓存可以降低网络流量,使网站加载速度更快。然而,如果在部署资源的最新版本时没有更改资源的文件名,浏览器可能会认为它没有被更新,从而使用它的缓存版本。由于缓存的存在,当需要获取新的代码时,就会显得很棘手.
更改 output.filename 中的 substitutions 以定义输出文件的名称。webpack 提供了一种称为 可替换模板字符串(substitution) 的方式,通过带括号字符串来模板化文件名。其中,[contenthash] 将根据资源内容创建唯一哈希值。当资源内容发生变化时,[contenthash] 也会发生变化。
由于像 lodash 或 react 这样的第三方库很少像本地源代码一样频繁修改,因此通常推荐将第三方库提取到单独的 vendor chunk 中。这一步将减少客户端对服务器的请求,同时保证自身代码与服务器一致。
在 Vue 项目中,像 lodash 或 react 这样的第三方库通常不会频繁修改,而且它们的文件体积较大。为了优化客户端的加载速度,通常会将这些第三方库提取到单独的 vendor chunk 中。
将第三方库提取到 vendor chunk 中的目的是减少客户端对服务器的请求次数。当客户端访问服务器时,服务器会返回一个或多个打包后的文件。如果将第三方库打包到与自身代码混合的文件中,那么每次更新自身代码时,客户端需要重新下载整个文件,包括第三方库部分,即使这部分代码并没有变化。这会浪费带宽和增加客户端的加载时间。
通过将第三方库提取到单独的 vendor chunk 中,我们可以将其单独打包为一个文件。这个文件在客户端首次加载时会被缓存,后续访问时只需要加载自身代码文件即可。当服务器端更新了自身代码时,客户端只需要重新加载自身代码文件,而不需要重新下载第三方库文件,从而减少了请求次数和加载时间。
这样的做法可以保证客户端的自身代码与服务器保持一致,同时提高了客户端的加载速度。这在构建大型项目时特别有用,因为第三方库的文件体积较大,而且更新频率较低。
optimization: {
runtimeChunk: 'single',
+ splitChunks: {
+ cacheGroups: {
+ vendor: {
+ test: /[\/]node_modules[\/]/,
+ name: 'vendors',
+ chunks: 'all',
+ },
+ },
+ },
},
vendor 的哈希值发生变化是我们要修复的。试试将 optimization.moduleIds 设置为 'deterministic'
更新 output.library 配置项,将其 type 设置为 'umd':
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'webpack-numbers.js',
- library: 'webpackNumbers',
+ library: {
+ name: 'webpackNumbers',
+ type: 'umd',
+ },
},
};
现在 webpack 将打包它,使其可以通过 CommonJS、AMD 模块以及脚本标签使用。
Tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的死代码。它依赖于 ES2015 模块语法的 静态结构 特性,例如 import 和 export。
提示
译注:死代码(dead code)是指程序中一段已经不会被执行的代码,通常是因为重构、优化或者逻辑错误导致的。这些代码可能是之前版本的遗留物,或者某些条件下永远不会被执行的代码。 在一个纯粹的 ES 模块世界中,很容易识别出哪些文件有副作用。然而,我们的项目无法达到这种纯度,所以,此时有必要提示 webpack 编译器哪些代码是纯粹的。
通过 package.json 的 "sideEffects" 属性即可实现此目的。
{
"name": "your-project",
"sideEffects": false
}
如果所有代码都不包含副作用,我们就可以简单地将该属性标记为 false 以告知 webpack 可以安全地删除未使用的导出内容。
提示
副作用(effect 或者 side effect)指在导入时会执行特殊行为的代码,而不是仅仅暴露一个或多个导出内容。polyfill 就是一个例子,尽管其通常不提供导出,但是会影响全局作用域,因此 polyfill 将被视为一个副作用。
懒加载或者按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。
button.onclick = e => import(/* webpackChunkName: "print" */ './print').then(module => {
+ const print = module.default;
+
+ print();
+ });
ECMAScript 模块(ESM)是在 Web 中使用模块的规范。 所有现代浏览器均支持此功能,同时也是在 Web 中编写模块化代码的推荐方式。
webpack 支持处理 ECMAScript 模块以优化它们。
默认情况下,webpack 将自动检测文件是 ESM 还是其他模块系统。
Node.js 通过设置 package.json 中的属性来显式设置文件模块类型。 在 package.json 中设置 "type": "module" 会强制 package.json 下的所有文件使用 ECMAScript 模块。 设置 "type": "commonjs" 将会强制使用 CommonJS 模块。
{
"type": "module"
}
除此之外,文件还可以通过使用 .mjs 或 .cjs 扩展名来设置模块类型。 .mjs 将它们强制置为 ESM,.cjs 将它们强制置为 CommonJs。
导入模块在 ESM 中更为严格,导入相对路径的模块必须包含文件名和文件扩展名(例如 *.js 或者 *.mjs),除非你设置了 fullySpecified=false。
提示
依旧支持导入包,例如 import "lodash" .
publicPath 配置选项在各种场景中都非常有用。你可以通过它来指定应用程序中所有资源的基础路径。实质上,发送到 output.path 目录的每个文件,都将从 output.publicPath 位置引用。
loader 用于对模块的源代码进行转换。loader 可以使你在 import 或 "load(加载)" 模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的得力方式。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript 或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS 文件!
loader 从右到左(或从下到上)地取值(evaluate)/执行(execute)。在下面的示例中,从 sass-loader 开始执行,然后继续执行 css-loader,最后以 style-loader 为结束。