11 月 19 - 20 日 Apache Pulsar 社区年度盛会来啦,立即报名! 了解详情
写点什么

Webpack 5.0 正式发布:更好的持久化缓存算法、提高 Web 平台的兼容性、带来 Node 生态新功能

  • 2020-11-30
  • 本文字数:19019 字

    阅读完需:约 62 分钟

Webpack 5.0正式发布:更好的持久化缓存算法、提高Web平台的兼容性、带来Node生态新功能

时隔两年,webpack 5 正式发布,快来看看有哪些新特性吧!(建议收藏)


webpack 4 于 2018 年 2 月发布,之后到现在虽然更新了很多功能,但没有重大更改。我们知道开发者不喜欢带很多重大更改的大版本。尤其是使用 webpack 时,开发者通常每年只“碰”它两次,而剩下的时间 webpack “就在那儿工作”。但是交付功能时不做重大更改是需要付出代价的:我们无法进行主要的 API 或架构改进。


该来的总是要来的,为了避免混乱,我们被迫进行重大更改。而现在是时候推出新的主要版本了。因此 webpack 5 包含了一众架构改进和得益于新架构的特性。


这个新的大版本还让我们有机会修改一些默认设置,并与近期新提出的提案和规范保持一致。


所以 webpack 5.0.0 发布了,但这并不意味着它已经完成、没有错误,甚至也不等于功能都完善好了。与 webpack 4 一样,我们会继续开发下去,不断修复问题和添加功能。在接下来的几天中可能会有很多错误修正。还有一些功能将稍后发布。


常见问题


新版正式发布意味着什么?


这意味着我们完成了重大更改。我们进行了许多重构,以升级架构并为将来的功能(和当前功能)奠定良好的基础。


那么什么时候升级呢?


看具体情况。升级很有可能失败,你可能要试两三次。如果你愿意这样做,请立即尝试升级并向 webpack、插件和加载程序提供反馈。我们希望尽快解决这些问题。总有人要迈出第一步,你将是最早从中受益的人之一。


总体方向


此版本重点关注以下内容:


  • 通过持久缓存提高构建性能。

  • 使用更好的算法和默认值来改善长期缓存。

  • 通过更好的 Tree Shacking 和代码生成来改善包大小。

  • 改善与 web 平台的兼容性。

  • 在 v4 中实施的一些功能没有引入重大更改,导致了一些怪异的内部结构,现在要清理掉。

  • 现在就引入一些重大更改为将来的功能做准备,使我们能够尽可能长时间地停留在 v5 上。


迁移指南


详见《迁移指南》


重大更改:移除


移除已弃用的项目


v4 中弃用的所有项目均已移除。


迁移:确保你的项目不包含 webpack 4 中已经 Warning 的功能。


以下是一些已移除但在 v4 中没有弃用警告的内容:


  • 现在必须只传递一个参数(可以是对象、字符串或函数)给 IgnorePlugin 和 BannerPlugin。


弃用代码


新的弃用包括弃用代码,因此更易于引用。


已弃用语法


require.include 已弃用,使用时默认会发出警告。


可以使用 Rule.parser.requireInclude 将行为更改为 allowed, deprecated 或 disabled。


移除自动化 Node.js Polyfills


早期,webpack 的目标是允许在浏览器中运行大多数 Node.js 模块,但是模块格局已经发生了很大变化,现在许多模块主要是出于前端目的编写的。webpack<=4 附带了许多 Node.js 核心模块的 polyfills,一旦一个模块使用任何核心模块(如 crypto 模块),这些 polyfills 就会自动应用。


虽然这让为 Node.js 编写的模块用起来更方便,但它会将这些巨大的 polyfill 添加到包中。在许多情况下,这些 polyfills 是不必要的。


webpack 5 会停止自动 polyfilling 这些核心模块,并专注于与前端兼容的模块。我们的目标是提高与 web 平台的兼容性,而在 web 平台上 Node.js 核心模块不可用。


迁移:


  • 尽可能尝试使用与前端兼容的模块。

  • 可以为 Node.js 核心模块手动添加 polyfill。错误消息将提示实现方法。

  • 包作者:使用 package.json 中的 browser 字段使包与前端兼容。提供浏览器的替代实现 / 依赖。


主要更改:长期缓存


确定性块、模块 ID 和导出名称


新版添加了用于长期缓存的一些新算法,在生产模式下默认启用。


chunkIds: "deterministic" moduleIds: "deterministic" mangleExports: "deterministic"


该算法以确定性方式为模块和块分配短(3 或 5 位数字)数字 ID,并为导出分配短(2 个字符)名称。这是包大小和长期缓存之间的折衷方案。


moduleIds/chunkIds/mangleExports: false 禁用默认行为,并且可以通过插件提供自定义算法。请注意,在 webpack 4 中,moduleIds/chunkIds: false(不带自定义插件)会正常构建,而在 webpack 5 中你必须提供自定义插件。


迁移:最好使用 chunkIds、moduleIds 和 mangleExports 的默认值。你还可以选择使用旧的默认 chunkIds: "size", moduleIds: "size", mangleExports: "size",这将生成较小的包,但会为了缓存而更频繁使它们无效化。


注意:在 webpack 4 中,哈希过的模块 ID 导致 gzip 性能降低。这与更改的模块顺序有关,并且已得到修复。


注意:在 webpack 5 中,默认情况下在生产模式下启用 deterministic ID。


真实内容哈希


现在,当使用 [contenthash] 时,webpack 5 将使用文件内容的真实哈希。在此之前“仅”使用内部结构的哈希。当仅更改注释或重命名变量时,这可能对长期缓存产生积极影响。包缩小后,这些更改不可见。


重大更改:开发支持


命名块 ID


在开发模式下默认启用的新命名块 ID(named chunk id)算法为块(和文件名)提供了易于理解的名称。模块 ID 由其相对于 context 的路径确定。块 ID 由块的内容确定。


因此,你不再需要使用 import(/* webpackChunkName: "name" */ "module") 进行调试。但如果要控制生产环境的文件名,这还是有用的。


可以在生产环境中使用 chunkIds:"named",但请确保不要意外地泄露有关模块名称的敏感信息。


迁移:如果你不喜欢在开发中更改文件名,则可以传递 chunkIds:"natural"以使用旧的数字模式。


模块联盟


webpack 5 添加了一个称为“模块联盟”(Module Federation)的新功能,允许多个 webpack 构建协同工作。从运行时的角度来看,来自多个构建的模块的行为就像一个巨大的互联模块图。从开发人员的角度来看,可以从指定的远程构建中导入模块,并以最少的限制使用它们。


更多详细信息请见该指南


重大更改:新的 Web 平台特性


JSON 模块


JSON 模块现在与提案保持一致,并在使用非默认导出时发出警告。从严格的 ECMAScript 模块导入时,JSON 模块不再有命名导出。


迁移:使用默认导出。


即使在使用默认导出时,optimization.usedExports 优化也会移除未使用的属性,而 optimization.mangleExports 优化会破坏属性。


可以在 Rule.parser.parse 中指定自定义 JSON 解析器,以导入类似 JSON 的文件(例如 toml、yaml、json5 等)。


import.meta


  • import.meta.webpackHot 是 module.hot 的别名,在严格的 ESM 中也可用

  • import.meta.webpack 是 webpack 的主要版本,数字表示

  • import.meta.url 是当前文件的 file: url(类似 __filename,但作为文件 url)


资产模块


webpack 5 现在对表示资产的模块提供原生支持。这些模块会将文件发射到输出文件夹中,或者将 DataURI 注入到 JavaScript 包中。无论哪种方式,它们都提供了可使用的 URL。


它们可以通过多种方式使用:


  • 匹配此类导入时,import url from "./image.png",并在 module.rules 中设置 type: "asset"。(旧方法)

  • new URL("./image.png", import.meta.url)(新方法)


新方法也允许运行没有打包程序的代码。此语法在浏览器的原生 ECMAScript 模块中也可用。


原生 Worker 支持


将资产的 new URL 与 new Worker/newSharedWorker/navigator.serviceWorker.register 结合使用时,webpack 会自动为 webWorker 创建一个新的入口点。


new Worker(new URL("./worker.js", import.meta.url))


选择该语法以允许运行代码时无需打包程序。此语法在浏览器的原生 ECMAScript 模块中也可用。


URIs


webpack 5 支持处理请求中的协议。


  • 支持 data: 。支持 Base64 或原始编码。Mimetype 可以映射到 module.rules 中的加载程序和模块类型。示例:import x from "data:text/javascript,export default 42"

  • 支持 file: 。

  • 支持 http(s): ,但需要通过 new webpack.experiments.schemesHttp(s)UriPlugin() 启用

  • 默认情况下,定位“web”时,这些 URI 导致对外部资源的请求(它们是外部资源)


支持请求中的 fragment,示例:./file.js#fragment


Async 模块


webpack 5 支持所谓的“async 模块”。这些模块不会同步执行,而是异步和基于 Promise 的模块。


通过 import 可以自动导入它们,并且不需要其他语法,和之前几乎没有差异。


通过 require() 导入它们将返回一个解析为导出的 Promise。


在 webpack 中,有多种方式来拥有 async 模块:


  • 异步 externals

  • 新规范中的 WebAssembly 模块

  • 使用顶级 Await 的 ECMAScript 模块


externals


webpack 5 添加了更多外部类型以覆盖更多应用程序:


promise:表示为 Promise 的表达式。该外部模块是 async 模块,解析值用作模块导出。


import:原生 import() 用于加载指定的请求。该外部模块是 async 模块。


module:尚未实现,但计划通过 import x from "..."加载模块。


script:通过<script>标签来加载 url,并从全局变量(及其可选属性)获取导出。该外部模块是 async 模块。


重大更改:Node.js 生态系统的新功能


解析


现在支持 package.json 中的 exports 和 imports 字段。


原生支持 Yarn PnP。


更多信息可点击此链接


主要更改:开发体验


改进的目标


webpack 5 允许传递目标列表,并且还支持目标版本。


示例:target: "node14" target: ["web", "es2020"]


这是提供 webpack 确定以下内容所需所有信息的简单方法:


  • 块加载机制;

  • 支持的语法,例如箭头函数。


Stats


Stats 测试格式改进了可读性和简洁性。改进了默认值来简化内容,也适用于大型构建。


  • 块关系被默认隐藏。可以使用 stats.chunkRelations 切换。

  • Stats 现在可以区分 files 和 auxiliary Files。

  • Stats 现在默认隐藏模块和块 ID。可以使用 stats.ids 切换。

  • 现在,所有模块的列表按到入口点的距离排序。可以使用 stats.modulesSort 更改。

  • 块模块列表现在按模块名称排序。可以使用 stats.chunkModulesSort 更改。

  • 串联模块中的嵌套模块列表现在按拓扑排序。可以使用 stats.nestedModulesSort 更改。

  • 块和资产现在显示块 ID 提示。

  • 资产和模块将显示在树中,而不是列表 / 表中。

  • 一般信息显示在末尾的摘要中,包含 webpack 版本、配置名称和警告 / 错误计数。

  • 哈希默认隐藏。可以使用 stats.hash 更改。

  • 默认不再显示构建时间戳。可以使用 stats.builtAt 启用它。它将在摘要中显示时间戳。

  • 默认不再显示子编译。可以用 stats.children 显示。


Progress


对 CLI 用于 --progress 的 ProgressPlugin 进行了一些改进,但也可以手动用作插件。


之前它仅用于计数已处理的模块。现在,它可以计数 entries、dependencies 和 modules。现在这些都默认显示。


之前它用于显示当前处理的模块。这导致很多 stderr 输出,并在某些控制台上产生了性能问题。现在默认禁用此功能(activeModules 选项)。这也减少了控制台上的垃圾信息数量。现在,在构建模块期间写入 stderr 的速度限制为 500ms。


profiling 模式也得到了升级,将显示嵌套进度消息的计时。这样在插件引起性能问题时更容易找出原因。


新添加的 percentBy 选项告诉 ProgressPlugin 如何计算进度百分比。


new webpack.ProgressPlugin({ percentBy: 'entries' });
复制代码


为了让进度百分比更加准确,ProgressPlugin 缓存了最后一个已知的总模块数,并在下一个构建中重用此值。第一个构建将预热缓存,随后的构建将使用并更新此值。


自动唯一命名


在 webpack 4 中,多个 webpack 运行时可能在同一 HTML 页面上发生冲突,因为它们使用相同的全局变量进行块加载。为了解决这个问题,需要为 output.jsonpFunction 配置提供自定义名称。


webpack 5 会从 package.json 名称自动推断出构建的唯一名称,并将其用作 output.uniqueName 的默认名称。


此值用来使所有可能出现潜在冲突的全局变量唯一化。


迁移:移除 output.jsonpFunction,以便在 package.json 中使用唯一名称。


自动公共路径


webpack 5 将在可能的情况下自动确定 output.publicPath。


Typescript 类型


webpack 5 从源代码生成 typescript 类型,并通过 npm 包公开它们。


迁移:移除 @types/webpack。名称不同时会更新引用。


主要更改:优化


嵌套摇树


webpack 现在能够跟踪对导出的嵌套属性的访问。重新导出名称空间对象时,这可以改善摇树优化(清除未使用的导出和导出缩小)。


// inner.jsexport const a = 1;export const b = 2;// module.jsexport * as inner from './inner';// or import * as inner from './inner'; export { inner };// user.jsimport * as module from './module';console.log(module.inner.a);
复制代码


在此示例中,可以在生产模式下移除导出 b。


内部模块摇树


webpack 4 并未分析模块导出和导入之间的依赖关系。webpack 5 有一个新的选项 optimization.innerGraph,它在生产模式下默认启用,对模块中的符号进行分析以找出从导出到导入的依赖性。


在这样的模块中:


import { something } from './something';function usingSomething() {  return something;}export function test() {  return usingSomething();}
复制代码


内部图算法将确定仅在使用 test 导出时才使用 something。这允许将更多导出标志为未使用,并从包中省略更多代码。如果设置了"sideEffects": false,则可以省略更多模块。在此示例中,当未使用 test 导出时,./something 将被省略。


要获取有关未使用的导出的信息,optimization.unusedExports 是必需的。要移除无副作用的模块,需要 optimization.sideEffects。


可以分析以下符号:


  • 函数声明

  • 类声明

  • 带有以下内容的 export default 或变量声明

  • 函数表达式

  • 类表达式

  • 序列表达式

  • /#PURE/ 表达式

  • 局部变量

  • 导入绑定


反馈:如果你发现此分析中缺少某些内容,请报告问题,我们考虑将其添加进去。


使用 eval() 可以对模块 bail-out 这种优化,因为 evaled 的代码可以引用作用域中的任何符号。


此优化也称为深度作用域分析。


CommonJs 摇树


webpack 以前会对 CommonJs 导出和 require() 调用退出已使用导出分析。


webpack 5 增加了对某些 CommonJs 构造的支持,允许消除未使用的 CommonJs 导出,并从 require() 调用中跟踪引用的导出名称。


支持以下构造:


  • exports|this|module.exports.xxx = ...

  • exports|this|module.exports = require("...") (reexport)

  • exports|this|module.exports.xxx = require("...").xxx (reexport)

  • Object.defineProperty(exports|this|module.exports, "xxx", ...)

  • require("abc").xxx

  • require("abc").xxx()

  • importing from ESM

  • require() a ESM

  • flagged exportType (special handling for non-strict ESM import):

  • Object.defineProperty(exports|this|module.exports, "__esModule", { value: true|!0 })

  • exports|this|module.exports.__esModule = true|!0

  • 计划将来支持更多构造


当检测到无法分析的代码时,webpack 会 bail out,并且完全不会跟踪这些模块的导出信息(出于性能原因)。


副作用分析


package.json 中的“sideEffects”标志允许将模块手动标记为无副作用,从而在不使用它们时将其移除。


webpack 5 还可以根据对源代码的静态分析,自动将模块标记为无副作用。


按运行时的优化


webpack 5 现在能够(并且默认)对每个运行时分析和优化模块(运行时通常等于入口点)。这就可以只在真正需要它们的这些入口点中导出。入口点不会互相影响(只要每个入口点使用一个运行时)


模块串联


模块串联(Module Concatenation)还可以对每个运行时使用,以允许每个运行时进行不同的串联。


模块串联已成为一等公民,现在允许任何模块和依赖项来实现它。webpack 5 已经添加了对 ExternalModules 和 json 模块的支持,可能很快就会发布更多支持。


常规摇树改进


export * 已改进,可以跟踪更多信息,并且不再将 default 导出标志为已使用。


当 webpack 确定存在冲突的导出时,export * 现在将显示警告。


import() 允许通过 /* webpackExports: ["abc", "default"] */ 魔术注释来手动摇树模块。


开发 / 生产相似度


我们试图提高两种模式之间的相似性,在开发模式下的构建性能与避免仅在生产环境中出现的问题之间找到一个良好的平衡。


webpack 5 默认在两种模式下都启用 sideEffects 优化。在 webpack 4 中,由于 package.json 中的“sideEffects”标志不正确,此优化导致了一些仅出现在生产的错误。在开发中启用此优化可以更快、更轻松地发现这些问题。


在许多情况下,开发和生产都在具有不同文件大小写敏感性的 OS 上进行,因此当出现奇怪的情况时,webpack 5 会添加更多警告 / 错误。


改进的代码生成


webpack 会检测何时发生 ASI,并且在未插入分号时会生成较短的代码。Object(...) -> (0, ...)


webpack 将多个导出 getter 合并为一个运行时函数调用:r.d(x, "a", () => a); r.d(x, "b", () => b); -> r.d(x, {a: () => a, b: () => b});


现在在 output.environment 中还有额外选项,允许指定哪些 ECMAScript 特性可用于 webpack 生成的运行时代码。


通常不直接指定此选项,而是使用 target 选项。


webpack 4 只会发出 ES5 代码。webpack 5 现在可以生成 ES5 和 ES6/ES2015 代码。


仅支持现代浏览器将使用箭头函数生成较短的代码,并使用带有 TDZ 的 const 声明(export default)生成更多符合规范的代码。


改进的 target 选项


在 webpack 4 中,target 只有“web”和“node”(以及其他一些)粗略选项。webpack 5 在这里为你提供更多选择。


现在,target 选项对生成代码的影响比以前更多:


  • 块加载方法

  • 块的格式

  • wasm 装载方法

  • worker 中的块和 wasm 加载方法

  • 使用的全局对象

  • 是否应该自动确定 publicPath

  • 生成的代码中使用的 ECMAScript 特性 / 语法

  • 默认启用 externals

  • 一些 Node.js 兼容层的行为(global、filename、dirname)

  • 模块解析(browser 字段、exports 和 imports 条件)

  • 一些加载程序可能会基于此更改行为


对于某些内容来说,"web"和"node"这俩选项太粗略了,我们需要更多信息。因此,我们允许指定最低版本,例如“node10.13”,并推断有关目标环境的更多属性。


现在还可以将多个目标组合为一个数组,webpack 将确定所有目标的最小属性。当使用无法提供完整信息,例如“web”或“node”(无版本号)的目标时,数组也很有用。例如 [“web”, “es2020”] 结合了这两个部分目标。


有一个目标"browserlist",它将使用浏览器列表数据来确定环境的属性。当项目中有可用的 browserslist 配置时,默认也会使用此目标。如果没有此类配置可用,则默认将使用“web”目标。


某些组合和特性尚未实现,将导致错误。它们是将来特性的准备。例如:


  • ["web", "node"] 将引向一种通用的块加载方法,该方法尚未实现

  • ["web", "node"] + output.module: true 将导致一种模块块加载方法,该方法尚未实现

  • "web"将导致 http(s): 导入被视为尚未实现的 module externals(解决方案:externalsPresets: { web: false, webAsync: true },使用 import() 代替。)


SplitChunk 和模块大小


现在模块大小不再只用一个数字来表示了。现在有了不同类型的大小表示。


现在 SplitChunksPlugin 知道如何处理这些格式,并将它们用于 minSize 和 maxSize。默认情况下仅处理 javascript 格式,但是你现在可以传递多个值来管理它们:


module.exports = {  optimization: {    splitChunks: {      minSize: {        javascript: 30000,        webassembly: 50000,      }    }  }};
复制代码


你仍然可以使用单个数字表示大小。在这种情况下,webpack 将自动使用默认大小类型。mini-css-extract-plugin 使用 css/mini-extra 作为大小类型,并自动将此大小类型添加到默认类型。


主要更改:性能


持久缓存


现在有一个文件系统缓存。它是可选的,可以通过以下配置启用:


module.exports = {  cache: {    // 1. Set cache type to filesystem    type: 'filesystem',    buildDependencies: {      // 2. Add your config as buildDependency to get cache invalidation on config change      config: [__filename]      // 3. If you have other things the build depends on you can add them here      // Note that webpack, loaders and all modules referenced from your config are automatically added    }  }};
复制代码


重要提示:


默认情况下,webpack 假定 webpack 所在的 node_modules 目录 仅由 包管理器修改。对于 node_modules,将跳过哈希和时间戳。相反,出于性能原因仅使用包名称和版本。符号链接(即 npm/yarn 链接)只要 resolve.symlinks: false 未指定即可(但也尽量不要这么做)。除非你选择使用 snapshot.managedPaths: [] 退出此优化,否则请不要直接在 node_modules 中编辑文件。使用 Yarn PnP webpack 时,假定 yarn 缓存是不可变的(通常是不变的)。你可以通过 snapshot.immutablePaths: [] 退出此优化。


缓存将默认存储在 node_modules/.cache/webpack 中(使用 node_modules 时),或.yarn/.cache/webpack 中(使用 Yarn PnP 时)。当所有插件正确处理缓存时,你可能不必手动移除它。


许多内部插件也使用持久缓存。示例:SourceMapDevToolPlugin(用于缓存 SourceMap 生成)或 ProgressPlugin(用于缓存模块数量)


持久缓存将根据使用情况自动创建多个缓存文件,以优化对缓存的读写访问。


默认情况下,时间戳将在开发模式下用于快照,而在生产模式下用于文件哈希。文件哈希也允许在 CI 上使用持久缓存。


编译器空闲和关闭


现在需要在使用编译器后将其关闭。编译器现在进入和离开空闲状态,并具有这些状态的 hooks。插件可以使用这些 hooks 执行不重要的工作。(如,持久缓存慢慢缓存到磁盘)。编译器关闭时,所有剩余工作应尽快完成。一个回调会表示关闭已完成。


插件及其作者应该期望某些用户可能会忘记关闭编译器。因此,所有工作最终也应该在空闲时完成。当工作完成时,应防止进程退出。


传递回调时,webpack() 会自动调用 close。


迁移:使用 Node.js API 时,请确保在完成后调用 Compiler.close。


文件发射


webpack 过去总是在第一个构建期间发出所有输出文件,但是在增量(监视)构建期间跳过写入未更改的文件。这里假定在运行 webpack 时没有其他东西更改输出文件。


加入持久缓存后,即使重新启动 webpack 进程也应获得类似监视的体验,但是如果认为即使 webpack 不运行,也没有其他东西更改输出目录,这就会是一个太强的假设。


因此,webpack 现在将检查输出目录中的现有文件,并将其内容与内存中的输出文件对比。仅在更改文件后才写入文件。这仅在首次构建时完成。在运行中的 webpack 进程中生成新资产时,任何增量构建都将始终写入文件。


我们假设 webpack 和插件仅在内容已更改时才生成新资产。缓存应该用于确保输入相等时不会生成新资产。不遵循此建议将降低性能。


当存在具有相同名称的文件时,将永远不会写入标志为 [immutable](包括内容哈希)的文件。我们假设当文件内容更改时,内容哈希也将更改。一般而言这是正确的,但在 webpack 或插件开发期间可能并不总是如此。


重大更改:长期未解决的问题


单文件目标的代码分割


现在,仅允许启动单个文件的目标(如 node、webWorker、electron main)支持在运行时自动加载引导所需的依赖项。


这允许将 opimization.splitChunks 用于具有 chunks: "all"以及 optimization.runtimeChunk 的这些目标。


请注意,对于块加载异步的目标,这会使初始评估也异步。使用 output.library 时这可能是一个问题,因为导出的值现在是一个 Promise。


更新的解析器


Enhanced-Resolve 已更新至 v5,具有以下改进:


  • 跟踪更多的依赖项,例如丢失的文件;

  • aliasing 可能有多种选择;

  • 现在能 aliasing 到 false;

  • 支持 exports 和 imports 字段等特性;

  • 性能提升。


没有 JS 的块


不包含 JS 代码的块将不再生成 JS 文件。这允许生成仅包含 CSS 的块。


重大更改:未来


实验


并非所有特性从一开始就是稳定的。在 webpack 4 中,我们添加了一些实验特性,并在更改日志中指出它们是实验性的。但是从配置中并不总是能清楚表明这些功能是实验性的。


在 webpack 5 中有一个新的 experiments 配置选项,允许启用实验特性。这样可以清楚知道启用 / 使用了哪些特性。


尽管 webpack 遵循语义版本控制,但实验特性会是例外。实验特性可能包含次要 webpack 版本中的重大更改。发生这种情况时,我们将在更改日志中添加清晰的注释。这将使我们能够更快地迭代实验特性,同时还能让我们在主要版本上停留更长时间以获得稳定的特性。


以下实验特性将随 webpack 5 一起提供:


  • webpack 4(experiments.syncwebAssembly)中的旧 WebAssembly 支持

  • 根据更新规范(experiments.asyncwebAssembly)的新 WebAssembly 支持

  • 这会让 WebAssembly 模块成为异步模块

  • 顶级 await 阶段 3 提案(experiments.topLevelAwait)

  • 在顶级使用 await 会让模块成为异步模块

  • 将包作为模块发出(experiments.outputModule)

  • 这从包中移除了包装器 IIFE,强制执行严格模式,通过<script type="module">进行延迟加载,并在模块模式下将其最小化。


请注意,这还意味着现在默认禁用 WebAssembly 支持。


最低 Node.js 版本


支持的最低 Node.js 版本已从 6 增加到 10.13.0(LTS)。


迁移:升级到可用的最新 Node.js 版本。


配置更改


结构更改


  • entry: {}现在允许使用空对象(以允许使用插件添加 entry)

  • target 支持数组、版本和浏览器列表

  • cache: Object 已移除:不再可以设置为内存缓存对象

  • 添加了 cache.type:现在可以在"memory"和"filesystem"之间选择

  • 添加了 cache.type = "filesystem"的新配置选项:

  • cahce.cacheDirectory

  • cahce.name

  • cahce.version

  • cahce.store

  • cahce.hashAlgorithm

  • cahce.idleTimeout

  • cahce.idleTimeoutForInitialStore

  • cahce.buildDependencies

  • 添加了 snapshot.resolveBuildDependencies

  • 添加了 snapshot.resolve

  • 添加了 snapshot.module

  • 添加了 snapshot.managedPaths

  • 添加了 snapshot.immutablePaths

  • 添加了 resolve.cache:允许禁用 / 启用安全的解析缓存

  • resolve.concord 已移除

  • resolve.alias 值现在可以是数组或 false

  • 添加了 resolve.restrictions:允许限制潜在的解析结果

  • 添加了 resolve.fallback:允许为解析失败的请求添加别名

  • 添加了 resolve.preferRelative:允许解析模块请求也是相对请求

  • 用于原生 Node.js 模块的自动 polyfill 已移除

  • node.Buffer 已移除

  • node.console 已移除

  • node.process 已移除

  • node.*(Node.js 原生模块)已移除

  • 迁移:resolve.alias 和 ProvidePlugin。错误会提示。(v4 中使用的 polyfill 和 mock 的信息,请参阅:https://github.com/webpack/node-libs-browser)

  • output.filename 现在可以是一个函数

  • 添加了 output.assetModuleFilename

  • 将 output.jsonpScriptType 重命名为 output.scriptType

  • devtool 更严格

  • 格式:false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

  • 添加了 optimization.chunkIds: "deterministic"

  • 添加了 optimization.moduleIds: "deterministic"

  • optimization.moduleIds: "hashed"已弃用

  • optimization.moduleIds: "total-size"已移除

  • 移除了模块和块 ID 的弃用标志

  • optimization.hashedModuleIds 已移除

  • optimization.namedChunks 已移除(还有 NamedChunksPlugin)

  • optimization.namedModules 已移除(还有 NamedModulesPlugin)

  • optimization.occurrenceOrder 已移除

  • 迁移:使用 chunkIds 和 moduleIds

  • optimization.splitChunks test 不再匹配块名称

  • 迁移:使用测试函数 (module, { chunkGraph }) => chunkGraph.getModuleChunks(module).some(chunk => chunk.name === "name")

  • 添加了 optimization.splitChunks minRemainingSize

  • optimization.splitChunks filename 现在可以是一个函数

  • optimization.splitChunks 的大小现在可以是具有源类型的对象

  • minSize

  • minRemainingSize

  • maxSize

  • maxAsyncSize

  • maxInitialSize

  • maxSize 旁边添加了 optimization.splitChunks maxAsyncSize 和 maxInitialSize:允许为初始块和异步块指定不同的 max size

  • optimization.splitChunks name: true 移除:不再支持自动名称

  • 迁移:使用默认值。chunkIds: "named"将为你的文件提供有用的调试名称

  • 添加了 optimization.splitChunks.cacheGroups[].idHint:提示如何选择命名的块 ID

  • optimization.splitChunks automaticNamePrefix 已移除

  • 迁移:改用 idHint

  • optimization.splitChunks filename 不再限制为初始块

  • 添加了 optimization.splitChunks usedExports,以在对比模块时包括使用的导出

  • 添加了 optimization.splitChunks.defaultSizeTypes:在将数字用作大小时指定大小类型

  • 添加了 optimization.mangleExports

  • optimization.minimizer "..."可用于引用默认值

  • 添加了 optimization.usedExports "global"值,以允许禁用按每个运行时分析,改为全局进行分析(性能更高)

  • optimization.noEmitOnErrors 重命名为 optimization.emitOnErrors,逻辑倒置

  • 添加了 optimization.realContentHash

  • output.devtoolLineToLine 已移除

  • 迁移:无替代品

  • output.chunkFilename: Function 现在被允许

  • output.hotUpdateChunkFilename: Function 现在禁止使用:一直都没法用。

  • output.hotUpdateMainFilename: Function 现在禁止使用:一直都没法用。

  • output.importFunctionName: string 指定用于替换 import() 的名称,以允许在非支持环境中 polyfilling。

  • 添加了 output.charset:将其设置为 false 忽略脚本标签上的 charset 属性

  • 将 output.hotUpdateFunction 重命名为 output.hotUpdateGlobal

  • 将 output.jsonpFunction 重命名为 output.chunkLoadingGlobal

  • output.chunkCallbackFunction 重命名为 output.chunkLoadingGlobal

  • 添加了 output.chunkLoading

  • 添加了 output.enabledChunkLoadingTypes

  • 添加了 output.chunkFormat

  • module.rules resolve 和 parser 将以不同的方式合并(对象已深度合并,数组可能包含" ..."以引用上一个值)

  • 添加了 module.rules parser.worker:允许配置支持的 worker

  • module.rules query 和 loaders 已移除

  • 传递字符串的 module.rules option 已弃用

  • 迁移:传递一个选项对象,如果不支持,则在 loader 上开一个问题

  • 添加了 module.rules mimetype:允许匹配 DataURI 的 mimetype

  • 添加了 module.rules descriptionData:允许匹配 package.json 中的数据

  • module.defaultRules " ..."可用于引用默认值

  • 添加了 stats.chunkRootModules:显示块的根模块

  • 添加了 stats.orphanModules:显示未发出的模块

  • 添加了 stats.runtime:显示运行时模块

  • 添加了 stats.chunk Relations:显示父 / 子 / 兄弟块

  • 添加了 stats.errorStack:显示 webpack- internal 堆栈中的错误跟踪

  • 添加了 stats.preset:选择一个预设

  • 添加了 stats.relatedAssets:显示与其他资产相关的资产(例如 SourceMaps)

  • stats.warningsFilter 已弃用,改为 ignoreWarnings

  • BannerPlugin.banner 签名已更改

  • data.basename 已移除

  • data.query 已移除

  • 迁移:从 filename 中提取

  • SourceMapDevToolPlugin lineToLine 已移除

  • 迁移:无替代品

  • [hash] 作为完整编译的哈希已弃用

  • 迁移:改用 [fullhash] 或使用另一个哈希选项

  • [modulehash] 已弃用

  • 迁移:改用 [hash]

  • [moduleid] 已弃用

  • 迁移:改用 [id]

  • [filebase] 已移除

  • 迁移:改用 [base]

  • 基于文件的模板的新占位符(如 SourceMapDevToolPlugin)

  • [name]

  • [base]

  • [path]

  • [ext]

  • 传递函数时的 externals,现在有不同的签名 ({ context, request }, callback)

  • 迁移:更改签名

  • 添加了 externals Presets

  • 添加了 experiments(请参见上面的实验部分)

  • 添加了 watchOptions.followSymlinks

  • watchOptions.ignored 现在可以是一个 RegExp

  • webpack.util.serialization 现在公开。


* 默认值更改


  • 当浏览器列表配置可用时,target 现在默认是"browserlist"

  • 现在默认仅对 node_modules 启用 module.unsafeCache

  • optimization.moduleIds 在生产模式下默认为 deterministic,而不是 size

  • optimization.chunkIds 在生产模式下默认为 deterministic,而不是 total-size

  • optimization.nodeEnv 在 none 模式下默认为 false

  • optimization.splitChunks.minSize 在生产中默认为 20k

  • optimization.splitChunks.enforceSizeThreshold 在生产中默认为 50k

  • optimization.splitChunks minRemainingSize 默认为 minSize

  • 如果剩余部分太小,这将导致分割的块更少

  • optimization.splitChunks maxAsyncRequests 和 maxInitialRequests 的默认值已增加到 30

  • optimization.splitChunks.cacheGroups.vendors 已重命名为 optimization.splitChunks.cacheGroups.defaultVendors

  • optimization.splitChunks.cacheGroups.defaultVendors.reuseExistingChunk 现在默认为 true

  • terser 选项下,optimization.minimizer 目标默认使用 compress.passes: 2

  • 使用 cache 时 resolve(Loader).cache 默认为 true

  • resolve(Loader).cacheWithContext 默认为 false

  • resolveLoader.extensions 移除.json

  • node.global node.filename 和 node. dirname 在 node- target s 中默认为 false

  • stats.errorStack 默认为 false


loader 相关更改


this.getOptions


这个新的 API 应能简化 loader 中选项的用法。它允许传递 JSON 模式进行验证。详见:


https://github.com/webpack/webpack/pull/10017


this.exec


已从 loader 上下文中移除


迁移:这可以在 loader 本身中实现


this.getResolve


loaderAPI 中的 getResolve(options) 将以不同的方式合并选项,请参见 module.rules resolve。


由于 webpack 5 在不同的 issuing 依赖之间是不同的,因此可以将 dependencyType 作为选项传递(例如"esm"、"commonjs"或其他)。


内部主要更改


以下更改仅与插件作者相关:


新插件顺序


webpack5 中的插件将在默认配置应用前应用。这允许插件应用自己的默认值,或充当配置预设。


但这也是一个重大更改,因为插件在应用时不能依赖要设置的配置值。


迁移:仅在插件 hooks 中访问配置。或最好完全避免访问配置,并通过构造函数获取选项。


运行时模块


运行时代码的很大一部分已移至所谓的"运行时模块"中。这些特殊模块负责添加运行时代码。可以将它们添加到任何块中,但当前它们始终添加到运行时块中。"Runtime Requirements"控制将哪些运行时模块(或核心运行时部件)添加到包中。这样可以确保仅将用到的运行时代码添加到包中。将来,还可以将运行时模块添加到按需加载的块中,以在需要时加载运行时代码。


在大多数情况下,核心运行时允许内联 entry 模块,而不是使用 webpack_require 对其调用。如果包中没有其他模块,则根本不需要 webpack_require。这与模块串联(将多个模块串联成一个模块)结合得很好。


在理想情况下,根本不需要运行时代码。


迁移:如果要将运行时代码注入插件的 webpack 运行时中,请考虑改用 RuntimeModules。


序列化


添加了序列化机制以允许对 webpack 中的复杂对象序列化。它具有 opt-in 的语义,因此需要显式标记应序列化的类(及其序列化实现)。对于大多数模块、所有依赖项和某些错误,都已完成此操作。


迁移:使用自定义模块或依赖项时,建议使其可序列化以受益于持久缓存。


缓存插件


添加了具有插件接口的 Cache 类。此类可用于写入和读取缓存。根据配置,不同的插件可以向缓存添加功能。MemoryCachePlugin 添加了内存中缓存。FileCachePlugin 添加了持久(文件系统)缓存。


FileCachePlugin 使用序列化机制将持久化的项目持久化到磁盘或从磁盘还原。


hook 对象冻结


带有 hooks 的类会将其 hooks 对象冻结,因此不再可以通过这种方式添加自定义 hooks。


迁移:建议添加自定义 hooks 的方法是使用 WeakMap 和静态 getXXXHooks(XXX)(如 getCompilationHook(compilation))方法。内部类使用与自定义 hooks 相同的机制。


tapable 升级


webpack3 插件的 compat 层已移除。它在 webpack 4 中已弃用。


移除或弃用了一些较少使用的 tapableAPI。


迁移:使用新的 tapableAPI。


staged hooks


在封装过程的几个步骤中,有多个用于不同阶段的 hooks,如 optimizeDependencies Basic optimizeDependencies 和 optimizeDependenciesAdvanced。这些已被移除,改用带有 stage 选项的单个 hook。关于可用的 stage 值,参见 OptimizationStages。


迁移:改用剩下的 hook。你可以添加一个 stage 选项。


Main/Chunk/moduleTemplate 弃用


打包模板已重构。MainTemplate/ChunkTemplate/moduleTemplate 已弃用,而 JavascriptModulesPlugin 现在可以处理 JS 模板。


在这一重构之前,JS 输出由 Main/ChunkTemplate 处理,而另一个输出(如 WASM、CSS)由插件处理。看起来 JS 是一等,而另一个输出是二等。重构后,所有输出均由其插件处理。


仍然有可能 hook 进模板的各个部分。这些 hooks 现在位于 JavascriptModulesPlugin 中,而不是 Main/ChunkTemplate 中。(是的,插件也可以有 hooks。我称它们为附加 hooks。)


有一个兼容层,因此 Main/Chunk/moduleTemplate 仍然存在,但只是将 tap 调用委派给新的 hook 位置。


迁移:请遵循弃用消息中的建议。通常指向不同位置的 hooks。


入口点描述符


如果将对象作为入口点传递,则该值可能是字符串、字符串数组或描述符:


module.exports = {  entry: {    catalog: {      import: './catalog.js',    }  }};
复制代码


描述符语法可用于将其他选项传递给入口点。


入口点输出文件名


默认情况下,entry 块的输出文件名是从 output.filename 中提取的,但是你可以为特定 entry 指定自定义输出文件名:


module.exports = {  entry: {    about: { import: './about.js', filename: 'pages/[name][ext]' }  }};
复制代码


入口点依赖


默认情况下,每个 entry 块都存储它使用的所有模块。使用 dependOn-option,你可以将模块从一个 entry 块共享到另一个 entry 块:


module.exports = {  entry: {    app: { import: './app.js', dependOn: 'react-vendors' },    'react-vendors': ['react', 'react-dom', 'prop-types']  }};
复制代码


该应用块将不包含 react- vendors 拥有的模块。


入口点库


入口描述符允许为每个入口点传递不同的 library 选项。


module.exports = {  entry: {    commonjs: {      import: './lib.js',      library: {        type: 'commonjs-module'      }    },    amd: {      import: './lib.js',      library: {        type: 'amd'      }    }  }};
复制代码


入口点运行时


entry 描述符允许为每个 entry 指定运行时。指定后,将创建一个具有该名称的块,该块仅包含该 entry 的运行时代码。当多个 entry 指定相同的运行时时,该块将包含所有这些 entry 的公共运行时。这意味着它们可以在同一 HTML 页面上一起使用。


module.exports = {  entry: {    app: {      import: './app.js',      runtime: 'app-runtime'    }  }};
复制代码


入口点块加载


entry 描述符允许为每个 entry 指定一个 chunkLoading。该 entry 的运行时将使用它来加载块。


module.exports = {  entry: {    app: {      import: './app.js'    },    worker: {      import: './worker.js',      chunkLoading: 'importScripts'    }  }};
复制代码


顺序和 ID


之前 webpack 在编译阶段以特定方式对模块和块排序,从而按增量顺序分配 ID。现在情况变了。该顺序不再用于 ID 生成,而由插件完全控制 ID 生成。


优化模块和块顺序的 hooks 已移除。


迁移:你不能再依赖编译阶段中模块和块的顺序。


Array 到 Set


  • Compilation.modules 现在是一个 Set

  • Compilation.chunks 现在一个 Set

  • Chunk.files 现在是一个 Set


有一个兼容层,它会打印弃用警告。


迁移:使用 Set 方法而不是 Array 方法。


Compilation.fileSystemInfo


可以使用这个新类以缓存的方式访问有关文件系统的信息。当前,它允许询问文件和目录时间戳。如果可能,有关时间戳的信息将从监视程序传输,否则由文件系统访问确定。


将来,将添加请求文件内容哈希的功能,并且模块将能够使用文件内容而非文件哈希来检查有效性。


迁移:不要使用 file/contextTimestamps,而是使用 compiling.fileSystemInfoAPI。


现在可以为目录加上时间戳,来对 ContextModules 进行序列化。


已添加 Compiler.modifiedFiles(在 Compiler.removedFiles 旁边),便于引用更改后的文件。


文件系统


在 compiler.inputFileSystem 和 compiler.outputFileSystem 旁边,有一个新的 compiler.intermediateFileSystem,用于所有不视为输入或输出的文件系统动作,例如写入记录、缓存或分析输出。


文件系统现在具有 fs 接口,不再需要其他方法,例如 join 或 mkdirp。但是,如果它们具有诸如 join 或 dirname 之类的方法,则会使用它们。


热模块替换


HMR 运行时已重构为运行时模块。HotUpdateChunkTemplate 已合并到 ChunkTemplate。ChunkTemplates 和插件现在也应该处理 HotUpdateChunks。


HMR 运行时的 javascript 部分已与核心 HMR 运行时分离。现在,其他模块类型也可以用自己的方式处理 HMR。将来,这将允许用于 mini-css-extract-plugin 或 WASM 模块的 HMR。


迁移:由于这是新引入的功能,因此无需迁移。


import.meta.webpackHot 公开与 module.hot 相同的 API。这也可以从无法访问 module 的严格 ESM 模块(在 package.json 中类型为"module"的.mjs)来使用。


工作队列


之前 webpack 通过调用函数的函数和限制并行性的 semaphore 来应对模块处理。Compilation.semaphore 已移除,异步队列现在可以处理工作队列和处理。每个步骤都有一个单独的队列:


  • Compilation.factorizeQueue:为一组依赖项调用模块工厂。

  • Compilation.addModuleQueue:将模块添加到编译队列中(可以从缓存中还原模块)。

  • Compilation.buildQueue:如有必要,构建模块(可以将模块存储到缓存中)。

  • Compilation.rebuildQueue:如果手动触发,则再次构建模块。

  • Compilation.processDependenciesQueue:处理模块的依赖项。


这些队列有一些 hooks 来监视和拦截作业处理。


将来,多个编译器可能会一起工作,并且可以通过拦截这些队列来完成工作编排。


迁移:由于这是新引入的功能,因此无需迁移。


日志


webpack internals 现在包括一些日志记录。stats.logging 和 InfrastructureLogging 选项可用于启用这些消息。


模块和块图


之前 webpack 将解析的模块存储在依赖项中,并将包含的模块存储在块中。现在情况变了。现在,有关模块图中模块连接方式的所有信息都存储在 ModuleGraph 类中,有关模块与块连接方式的所有信息都存储在 ChunkGraph 类中。取决于块图之类的信息也存储在相关类中。


这意味着以下有关模块的信息已移除:


  • Module connections -> ModuleGraph

  • Module issuer -> ModuleGraph

  • Module optimization bailout -> ModuleGraph

  • Module usedExports -> ModuleGraph

  • Module providedExports -> ModuleGraph

  • Module pre order index -> ModuleGraph

  • Module post order index -> ModuleGraph

  • Module depth -> ModuleGraph

  • Module profile -> ModuleGraph

  • Module id -> ChunkGraph

  • Module hash -> ChunkGraph

  • Module runtime requirements -> ChunkGraph

  • Module is in chunk -> ChunkGraph

  • Module is entry in chunk -> ChunkGraph

  • Module is runtime module in chunk -> ChunkGraph

  • Chunk runtime requirements -> ChunkGraph


从缓存中还原时,以前 webpack 会从图中断开模块,现在不会了。模块不存储有关图的信息,并且可以在技术上用于多个图。这改善了缓存。


这些更改大都有一个兼容层,在使用时会打印弃用警告。


迁移:使用在 ModuleGraph 和 ChunkGraph 上新的 API。


InitFragments


DependenciesBlockVariables 已移除,改用 InitFragments。DependencyTemplates 现在可以添加 InitFragments 来将代码注入模块源的顶部。InitFragments 允许重复数据移除。


迁移:使用 InitFragments,而不是插入某些负索引的内容到源中。


模块源类型


现在,模块必须通过 Module.getSourceTypes() 定义其支持的源类型。因此,不同的插件会使用这些类型调用 source()。例如,对于 javascript 类型的源,JavascriptModulesPlugin 将源嵌入到包中。源类型 webassembly 将使 webAssemblyModulesPlugin 发出 wasm 文件。还支持自定义源类型,如 mini-css-extract-plugin 可能会使用源类型 stylesheet 将源代码嵌入到 CSS 文件中。


模块类型和源类型之间没有关系。如,模块类型 json 还使用源类型 javascript,模块类型 webassembly/experimental 使用源类型 javascript 和 webassembly。


迁移:自定义模块需要实现这些新的接口方法。


Stats 插件


preset、default、json 和 toString 的 Stats 现在由插件系统烘培。将当前 Stats 转换为插件。


迁移:你现在可以自定义它,而不必替换整个 Stats 功能。现在可以将其他信息添加到 stats json 中,而不用编写单独的文件。


新的监视


webpack 使用的 watcher 已重构。它以前使用 chokidar 和原生依赖项 fsevents(仅用于 OSX)。现在,它仅基于原生 Node.js fs。这意味着 webpack 中不存在任何原生依赖。


它在监视时还会捕获有关文件系统的更多信息。现在,它可以捕获 mtimes 并监视事件时间,以及有关丢失文件的信息。为此,WatchFileSystemAPI 进行了一些更改。我们还将 Array 转换为 Set,将 Object 转换为 Map。


发出后的 SizeOnlySource


webpack 现在使用 SizeOnlySource 变体替换了 Compilation.assets 中的 Sources,以减少内存使用量。


多次发射资产


警告 Multiple assets emit different content to the same filename 已改为错误。


ExportsInfo


重构了有关模块导出的信息的存储方式。现在,ModuleGraph 为每个 Module 提供一个 ExportsInfo,存储每次导出的信息。它还存储有关 unknown 导出的信息,以及是否以仅副作用方式使用模块。


对于每次导出,将存储以下信息:


  • 是否使用 export?yes,no,not statically known,not determined。(另请参见 optimization.usedExports)

  • 是否提供 export?yes,no,not statically known,not determined。(另请参见 optimization.providedExports)

  • 可以重命名导出名称吗?yes,no,not,determined

  • 新名称(如果导出已重命名)。(另请参见 optimization.mangleExports)

  • 嵌套的 ExportsInfo(如果导出是本身附加了信息的对象)

  • 用于重新导出名称空间对象:import * as X from "..."; export { X };

  • 用于表示 JSON 模块中的结构


代码生成阶段


现在,编译功能将代码生成作为单独的编译阶段。它不再隐藏在 Module.source() 或 Module.getRuntimeRequirements() 中运行。


这样可以使流程更整洁。它还允许报告此阶段的进度,并在分析时使“代码生成”更加可见。


迁移:Module.source() 和 Module.getRuntimeRequirements() 已弃用,请改用 Module.codeGeneration()。


DependencyReference


webpack 过去只有一个方法和类型来表示依赖引用(Compilation.getDependencyReference 返回 DependencyReference)。此类型曾经包括有关此引用的所有信息,例如被引用的模块(如果是弱引用,则其导出已被导入),以及一些与顺序有关的信息。


将所有这些信息打包在一起会使引用变得很大,引用频率也会增多(每次有人需要一点信息时都会引用)。


在 webpack5 中,代码库的这一部分已重构,并且该方法已拆分。


  • 可以从 ModuleGraphConnection 中读取引用的模块;

  • 可以通过 Dependency.getReferencedExports() 获取导入的导出名称;

  • Dependency 类上有一个 weak 标志;

  • 排序仅与 HarmonyImportDependencies 相关,并且可以通过 sourceOrder 属性获取。


Presentational Dependencies


现在,NormalModules 中有一种新的依赖类型:PresentationalDependencies


这些依赖仅在代码生成阶段使用,而在模块图构建期间不使用。因此,它们永远不会引用模块或影响导入导出。


这些依赖项的处理成本较低,并且 webpack 会尽可能使用它们。


弃用 loaders


null-loader 已弃用,改用:


module.exports = {  resolve: {    alias: {      xyz$: false    }  }};
复制代码


或使用绝对路径:


module.exports = {  resolve: {    alias: {      [path.resolve(__dirname, '....')]: false    }  }};
复制代码


原文链接


Webpack 5 Release

2020-11-30 16:523630

评论

发布
暂无评论
发现更多内容

想进阶高级架构师,你需要养成这10个习惯!,掌握这套精编Android高级面试题解析

android 程序员 移动开发

我肝了3个月终于整理出了这份超全面的《Android面试题及解析》(1)

android 程序员 移动开发

微信逆向之朋友圈,2021最新Android大厂面试真题大全

android 程序员 移动开发

成为一个优秀的Android开发者,需要必备哪些技术&工作技能?

android 程序员 移动开发

我们来剖析一下这个Android猴子的面试过程,以及被问到的问题

android 程序员 移动开发

我来告诉你:零基础如何成为高级Android开发,入职字节跳动

android 程序员 移动开发

微信小程序之加载更多(分页加载)实例 ,flutter瀑布流列表

android 程序员 移动开发

想搞懂Jetpack架构可以不搞懂生命周期知识吗?,阿里Android面试必问

android 程序员 移动开发

我的2020年终总结:疫情之下裸辞后的一些感悟和面试心得(上篇

android 程序员 移动开发

我,Android开发,40岁熬到中层月薪15K,百度Android岗一面+二面内容

android 程序员 移动开发

往事只能回味!春招 Android 开发岗:我居然三天就拿到了offer

android 程序员 移动开发

惨遭社会毒打,公司倒闭突然失业,程序员该如何在下次危机对准时狠狠还击

android 程序员 移动开发

想掌握Android面试官必问的 Binder 机制?那别想绕开 Binder 驱动源码分析!

android 程序员 移动开发

想掌握Android面试官必问的-Binder-机制?那别想绕开-Binder-驱动源码分析!

android 程序员 移动开发

我是如何做到在-5-分钟之内将应用大小减少-60%-的?,零基础学android编程

android 程序员 移动开发

怎样才是刷面试题的正确姿势?Android400道面试题+通关知识宝典助你进大厂

android 程序员 移动开发

战战兢兢面试了一个35岁Android开发程序员,就这水平开发了8年?

android 程序员 移动开发

微信小程序之商品属性分类 —— 微信小程序实战商城系列

android 程序员 移动开发

怎样让你更快的完成工作去“摸鱼”,我的Android美团求职之路

android 程序员 移动开发

我又开发了一个非常好用的开源库,调试Android数据库有救了

android 程序员 移动开发

我想谈谈关于-Android-面试那些事,写给有开发经验的你们

android 程序员 移动开发

我敢打赌!你从未见过如此简单的Dagger-导航---基于-Android-Studio-4-1

android 程序员 移动开发

性能优化,还得看AspectJ,android高级开发实战

android 程序员 移动开发

我学习Android的一些套路,这份333页关于性能优化知识点的PDF你不能不看

android 程序员 移动开发

我肝了3个月终于整理出了这份超全面的《Android面试题及解析》

android 程序员 移动开发

我才知道原来Flutter内置了10多种Button控件,音视频开发工程师抖音

android 程序员 移动开发

微博热门清华学霸的计划表刷屏,程序员该如何制定你的学习计划?

android 程序员 移动开发

总结了30个例子之后,我悟到了Flutter的布局原理,android移动开发基础答案

android 程序员 移动开发

成功逆袭:越来越胖怎么能忍,我的APK瘦身之路,完整版开放免费下载

android 程序员 移动开发

我们来剖析一下这个Android猴子的面试过程,以及被问到的问题(1)

android 程序员 移动开发

我又开发了一个非常好用的开源库,调试Android数据库有救了 (1)

android 程序员 移动开发

Webpack 5.0正式发布:更好的持久化缓存算法、提高Web平台的兼容性、带来Node生态新功能_语言 & 开发_Webpack官方团队_InfoQ精选文章