Webpack 面试经验知识点分享

发布于 2023-10-10 12:42:50 字数 5079 浏览 32 评论 0

2. 减少文件搜索范围

resolve.modules 的默认值是 ['node_modules'],含义是先去当前目录下的 ./node_modules ​ 目录下去找想找的模块,如果没找到就去上一级目录 ../node_modules ​ 中找,再没有就去 ../../node_modules ​ 中找,以此类推,这和 Node.js 的模块寻找机制很相似。

resolve.alias 配置项通过别名来把原导入路径映射成一个新的导入路径。

resolve: {
  // 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤
  // 其中 __dirname 表示当前工作目录,也就是项目根目录
  modules: [path.resolve(__dirname, 'node_modules')]
  // 只采用 main 字段作为入口文件描述字段,以减少搜索步骤
  mainFields: ['main'],
  // 使用 alias 把导入 react 的语句换成直接使用单独完整的 react.min.js 文件,
  // 减少耗时的递归解析操作
  alias: {
    'react': path.resolve(__dirname, './node_modules/react/dist/react.min.js'), // react15
    // 'react': path.resolve(__dirname, './node_modules/react/umd/react.production.min.js'), // react16
  }

  // 尽可能的减少后缀尝试的可能性
  //在导入语句没带文件后缀时,Webpack 会自动带上后缀后去尝试询问文件是否存在
  extensions: ['js'],
  // 独完整的 `react.min.js` 文件就没有采用模块化,忽略对 `react.min.js` 文件的递归解析处理
  //让 Webpack 忽略对部分没采用模块化的文件的递归解析处理,这样做的好处是能提高构建性能。
  noParse: [/react\.min\.js$/],
},

3 动态链接库

在一个动态链接库中包含给其他模块调用的函数和数据

  • 把网页依赖的基础模块抽离出来,打包进一个个动态链接库,一个动态链接库中包含多个模块
  • 当需要导入的模块在动态链接中,这个模块不能在被重新打包 而是直接去动态链接库中
  • 页面依赖的所有动态链接库需要被加载 原因在于包含大量复用模块的动态链接库只需要编译一次,在之后的构建过程中被动态链接库包含的模块将不会在重新编译,而是直接使用动态链接库中的代码。

由于动态链接库中大多数包含的是常用的第三方模块,例如 react、react-dom,只要不升级这些模块的版本,动态链接库就不用重新编译。

4 Cdn 加速 内容分发网络

压缩代码减少网络传输大小

Webpack 接入 cdn

  1. 静态资源的导入 URL 需要变成指向 CDN 服务的绝对路径的 URL 而不是相对于 HTML 文件的 URL。
  2. 静态资源的文件名称需要带上有文件内容算出来的 Hash 值,以防止被缓存。
  3. 不同类型的资源放到不同域名的 CDN(同一时刻针对同一个域名的资源并行请求是有限制的话(具体数字大概 4 个左右,不同浏览器可能不同)) 服务上去,以防止资源的并行加载被阻塞。这样会增加域名解析的时间 可以通过 head 标签<link rel="dns-perfetch" href="//sxx.com"/>预解析域名 减少域名解析的时间

压缩代码

  1. 混淆源码
  2. 减少网络传输流量
  3. 提升网页加速

5 背后的运行机制

  1. plugin:扩展插件,在 webpack 构建过程中的特定时机会广播事件,插件监听特定事件,在特定时机作一些操作
  2. loader:模块转换器,用于把模块原内容转换成新的内容

webpack 的构建过程是串行化的从启动到结束 经过这些过程

  1. 确定配置参数: 配置文件中的参数和 shell 命令行中的参数合并成最终的参数
  2. 初始化 compiler 对象: 使用得出最终的参数初始化 compiler 对象,加载所有插件配置,执行 compiler 对象的 run 方法开始构建
  3. 确定入口:根据 entry 参数确定入口文件
  4. 编译模块:从入口文件开始调用配置的 loader 对文件进行转换,在找出入口文件依赖的模块,逐个对他们进行转换,入口文件依赖的模块都被转换过了
  5. 完成编译: 在经过第 4 步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
  6. 输出资源:根据入口与模块之间的依赖关系,产出一个个包含多个模块的 chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这里是改变输出内容的最后机会
  7. 输出完成:在确定好输出资源后,根据配置的路径和文件名,把文件内容写进到文件系统

Compiler 全局只有一个,包含完整的 webpack 的配置, 负责文件监听和启动编译

当 Webpack 以开发模式运行时,每当监测到文件变化,就会生成一个新的 Compilation 对象,Compilation 对象包含了当前模块的资源,编译生成资源,变化的文件等,Compilation 对象也提供了很多事件回调供插件使用

初始化阶段:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler。

  1. 初始化参数
  2. 实例化 compiler
  3. 加载插件,依次调用插件的 apply 方法,让插件可以监听后续的所有事件节点。
  4. environment: 应用 node.js 的文件系统到 compiler 对象
  5. entry-option 根据配置的 entry 信息,给每个 entry 生成一个对应的 entryPlugin,为入口的解析,递归解析依赖作准备
  6. after-plugins:当所有插件 apply 方法调用完成
  7. after-resolvers

编译阶段:从 Entry 发出,针对每个 Module 串行调用对应的 Loader 去翻译文件内容,再找到该 Module 依赖的 Module,递归地进行编译处理。

  1. run 开始一次新的编译
  2. complier: 通知插件将开始一次新的编译
  3. complilation: 生成新的 complilation 对象
  4. make 一个新的 Compilation 创建完毕,即将从 Entry 开始读取文件,根据文件类型和配置的 Loader 对文件进行编译,编译完后再找出该文件依赖的文件,递归的编译和解析。
  5. after-compile 一次 Compilation 执行完成。
  6. invalid 当遇到文件不存在、文件编译错误等异常时会触发该事件,该事件不会导致 Webpack 退出。

输出阶段:对编译后的 Module 组合成 Chunk,把 Chunk 转换成文件,输出到文件系统。

  1. emit: 确定好要输出哪些文件后,执行文件输出,可以在这里获取和修改输出内容。
  2. after-emit 文件输出完毕
  3. done:成功完成一次完成的编译和输出流程。
  4. failed 如果在编译和输出流程中遇到异常导致 Webpack 退出时,就会直接跳转到本步骤,插件可以在本事件中获取到具体的错误原因。

在输出阶段已经得到了各个模块经过转换后的结果和其依赖关系,并且把相关模块组合在一起形成一个个 Chunk。

6.输出文件分析

原来一个个单独的模块文件被合并到一个文件中:bundle.js

为什么 bunlde.js 能直接在浏览器中运行呢?bundle.js 中能直接运行在浏览器中原因是在输出文件中通过_webpack_require_函数定义了一个在浏览器中执行的加载函数来模拟 node.js 中的 require 语句。

做不到和 node 一样在本地加载文件,当发现需要加载新的文件时需要通过网络,但是模块多了的化,时间就很长,所有就放在数组里,一次加载完

_webpack_require_ 函数还有缓存处理,已经加载过的模块不会进行二次加载,会去缓存中获取

7 抽取页面公共代码

改善什么现象

  1. 相同的资源被重复加载,浪费用户的流量 服务器的成本
  2. 包体也大

8 按需加载

最关键的一行是 chunkFilename: '[name].js', ​,它专门指定动态生成的 Chunk 在输出时的文件名称。 如果没有这行,分割出的代码的文件名称将会是 [id].js。

import('/*webpackChunkName:"show"*/''./show.js').then((show)=> {
show('Webpack');
})

9 plugin

webpack 就像一个生产线,通过一系列流程才能将源文件转换成输出结果,生产线上每个处理流程的职责是单一的,多个流程存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。

插件就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。

10 loader 链式调用 翻译员

Loader 运行在 Node.js 中,你可以调用任何 Node.js 自带的 API,loader 将源代码经过转换后输出新的结果支持链式调用。

在你开发一个 Loader 时,请保持其职责的单一性,你只需关心输入和输出

module.exports = function(source){
return source;
}

loader 有同步和异步之分

Webpack 会默认缓存所有 Loader 的处理结果,

加载本地 loader

  1. npm-link

11 Prepack

Prepack 在编译阶段就预先执行源码得到结果,再把结果输出来以提高性能,而不是在运行时再去求结果,缺点:

  • 还不够成熟
  • 不能够识别 Dom APi 部分 Node.js API
  • 存在优化后代码性能更低的情况
  • 存在优化后代码体积更大的情况

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

0 文章
0 评论
22 人气
更多

推荐作者

内心激荡

文章 0 评论 0

JSmiles

文章 0 评论 0

左秋

文章 0 评论 0

迪街小绵羊

文章 0 评论 0

瞳孔里扚悲伤

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文