前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >前端工程化 - Webpack 常见面试题速查

前端工程化 - Webpack 常见面试题速查

作者头像
Cellinlab
发布2023-05-17 16:49:53
4420
发布2023-05-17 16:49:53
举报
文章被收录于专栏:Cellinlab's BlogCellinlab's Blog

# webpack 与 grunt、gulp 的不同

Grunt、Gulp 是基于任务运行的工具:

它们会自动执行指定的任务,就像流水线,把资源放上去然后通过不同插件进行加工,有活跃的社区,丰富的插件,能方便地打造各种工作流。

Webpack 是基于模块化打包的工具:

自动化处理模块,webpack 把一切当成模块,当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些打包成一个或多个 bundle。

二者是完全不同的两类工具,而现在主流的方式是用 npm script 代替 Grunt、Gulp,npm script 同样可以打造任务流。

# webpack、rollup、parcel 优劣

  • webpack
    • 适用于大型复杂的前端站点构建
    • webpack 有强大的 loader 和 插件生态,打包后的文件实际上就是一个立即执行函数,这个立即执行函数接收一个参数,该参数是模块对象,键为各个模块的路径,值为模块内容
    • 立即执行函数内部则处理模块之间的引用,执行模块等,适合文件依赖复杂的应用开发
  • rollup
    • 适用于基础库的打包,如 vue、d3 等
    • Rollup 是将各个模块打包进一个文件中,并且通过 Tree-Shaking 来删除无用的代码,可以最大程度上降低代码体积
    • 但是 rollup 没有 webpack 如此多的如代码分割,按需加载等高级功能,更聚焦于库的打包,因此更适合库的开发
  • parcel
    • 适用于简单的实验性项目
    • 可以满足低门槛的快速看到效果
    • 但生态差、报错信息不全,仅推荐在实验项目中使用

# 有哪些常见的 Loader

  • file-loader 把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
  • url-loader 和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
  • source-map-loader 加载额外的 Source Map 文件,方便调试
  • image-loader 加载并且压缩图片文件
  • babel-loader 把 ES6 转换成 ES5
  • css-loader 加载 CSS,支持模块化、压缩、文件导入等特性
  • style-loader 把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS
  • eslint-loader 通过 ESLint 检查 JavaScript 代码

# 有哪些常见的 Plugin

  • define-plugin 定义环境变量
  • html-webpack-plugin 简化 html 文件创建
  • uglifyjs-webpack-plugin 通过 uglifyjs 压缩 ES6 代码
  • webpack-parallel-uglify-plugin 多核压缩,提高压缩速度
  • webpack-bundle-analyzer 可视化 webpack 输出文件的体积
  • mini-css-extract-plugin CSS 提取到单独的文件中,支持按需加载

# Loader 和 Plugin 的不同

  • 作用不同:
    • Loader 为加载器。
      • Webpack 将一切文件视为模块,但是 webpack 原生是只能解析 js 文件,如果想将其他文件也打包的话,就会用到 loader
      • Loader 的作用是让 webpack 拥有了加载和解析非 JavaScript 文件的能力
    • Plugin 为插件
      • Plugin 可以扩展 webpack 的功能,让 webpack 具有更多的灵活性
      • 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果
  • 用法不同:
    • Loader 在 module.rules 中配置,也就是说作为模块的解析规则而存在。类型为数组,每一项都是一个 Object,里面描述了对于生命类型的文件(test),使用什么加载(loader)和使用的参数(options)
    • Plugin 找 plugins 中单独配置。类型为数组,每一项是一个 plugin 的实例,参数都通过构造函数传入。

# 编写 Loader 或 Plugin 的思路

Loader 像翻译器,将读到的源文件内容转义成新的文件内容,并且每个 Loader 通过链式操作,将源文件一步步翻译成想要的样子。

编写 Loader 时要遵循单一原则,每个 Loader 只做一种“转义工作”。每个 Loader 拿到的是源文件内容(source),可以通过返回值的方式将处理后的内容输出,也可以调用 this.callback() 方法,将内容返回给 webpack。还可以通过 this.async() 生成一个 callback 函数,再用这个 callback 将处理后的内容输出出去。

此外 webpack 还为开发者准备了开发 loader 的工具函数集——loader-utils。

相对于 Loader 而言,Plugin 的编写就灵活了许多。webpack 在运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

# bundle、chunk、module 是什么

  • bundle:是由 webpack 打包出来的文件
  • chunk:代码块,一个 chunk 由多个模块组合而成,用于代码的合并和分割
  • module:是开发中的单个模块,在 webpack 的世界,一切皆模块,一个模块对应一个文件,webpack 会从配置的 entry 中递归开始找出所有依赖的模块

# Webpack 的构建流程是什么

Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:

  1. 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
  2. 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
  3. 确定入口:根据配置中的 entry 找出所有的入口文件;
  4. 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
  5. 完成模块编译:在经过第 4 步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
  6. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
  7. 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统 在以上过程中, Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果

# Webpack 的热更新是如何实现的

热更新又称热替换(Hot Module Replacement)HMR。该机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。

原理:

  1. 在 webpack 的watch 模式下,文件系统中某一个文件发生修改,webpack 监听到文件变化,根据配置文件对模块重新编译打包,并将打包后的代码通过简单的 JavaScript 对象保存在内存中
  2. webpack-dev-server 和 webpack 之间的接口交互,而这一步,主要是 dev-server 的中间件 webpack-dev-middleware 和 webpack 之间的交互,webpack-dev-middleware 调用 webpack 暴露的 API 对代码变化进行监控,并且告诉 webpack,将代码打包到内存中
  3. webpack-dev-server 对文件变化的一个监控,这一步不同于第一步,并不是监控代码变化重新打包。当我们在配置文件中配置了 devServer.watchContentBase 为 true 时,Server 会监听这些配置文件夹中静态文件的变化,变化后会通知浏览器端对应用进行 live reload。注意,这里是浏览器刷新,和 HMR 是两个概念
  4. 也是 webpack-dev-server 的工作,主要是通过 sockjs(webpack-dev-server的依赖)在浏览器端和服务端之间建立一个 websocket 长连接,将 webpack 编译打包的各个阶段的状态信息告知浏览器,同时也包括第三步中 Server 监听静态文件变化的信息。浏览器端会根据这些 socket 消息进行不同的操作。当然服务端传递的最主要信息还是新模块的 hash 值,后面的步骤根据这一 hash 值来进行模块热替换
  5. webpack-dev-server/client 端并不能强求更新的代码,也不会执行热更新模块操作,而把这些工作又交回给了 webpack,webpack/hot/dev-server 的工作就是根据 webpack-dev-server/client 传给他的信息以及 dev-server 的配置决定是刷新浏览器还是进行模块热更新。当然如果仅仅是刷新浏览器,就没有后面步骤
  6. HotModuleReplacement.runtime 是客户端 HMR 的中枢,它接收到上一步传递给他的新模块的 hash值,它通过 JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求,服务端返回一个 json,该 json 包含了所有要更新的模块的 hash 值,获取到更新列表后,该模块再次通过 jsonp 请求,获取到最新的模块代码。这就是7、8、9步骤
  7. 而第 10 步是决定 HMR 成功与否的关键步骤,该步骤,HotModulePlugin 将会对新旧模块进行对比,决定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用
  8. 最后一步,当 HMR 失败后,回退到 live reload 操作,也就是进行浏览器刷新来获取最新打包代码

# 如何用 webpack 来优化前端性能

用 webpack 优化前端性能是指优化 webpack 的输出结果,让打包的最终结果再浏览器运行快速高效。

  • 压缩代码:
    • 删除多余代码、注释、简化代码的写法等等
    • 可以利用 webpackUglifyJsPluginParallelUglifyPlugin 来压缩 JS 文件,利用 cssnano (css-loader 中 minimize 配置)来压缩 css
  • 利用 CDN 加速
    • 在构建过程中,将引用的静态资源路径修改为 CDN 上对应的路径
    • 可以利用 webpack 对于 output 参数和各 loader 的 publicPath 参数来修改资源路径
  • Tree Shaking
    • 将代码中永远不会走到的片段删除掉
    • 可以通过在启动 webpack 时追加参数 --optimize-minimize 来实现
  • Code Splitting:
    • 将代码按路由维度或者组件分块(chunk),这样做到按需加载,同时可以充分利用浏览器缓存
  • 提取公共第三方库
    • SplitChunksPlugin 插件来进行公共模块抽取,利用浏览器缓存可以长期缓存这些无需频繁变动的公共代码

# 如何提高 webpack 的打包速度

  • happypack:利用进程并行编译 loader,利用缓存来使得 rebuild 更快(以停止维护,可以用 thread-loader 替代)
  • 外部扩展:将不怎么需要更新的第三方库脱离 webpack 打包,不被打入 bundle 中,从而减少打包时间,比如 jQuery 用 script 标签引入
  • dll:采用 webpack 的 DllPlugin 和 DllReferencePlugin 引入 dll,让一些基本不会改动的代码先打包成静态资源,避免反复编译浪费时间
  • 利用缓存:webpack.cache、babel-loader.cacheDirectory、HappyPack.cache 都可以利用缓存提高 rebuild 效率
  • 缩小文件搜索范围:比如 babel-loader 插件,如果你的文件仅存于 src 中,那么可以 include: path.resolve(__dirname, 'src'),当然绝大多数情况下这种操作的提升有限,除非不小心 build 了 node_modules 文件

# 如何提高 webpack 的构建速度

  1. 多入口情况下,使用 CommonsChunkPlugin 来提取公共代码
  2. 通过 externals 配置来提取常用库
  3. 利用 DllpluginDllReferencePlugin 预编译资源模块 通过 DllPlugin 来对那些我们引用但是绝对不会修改的 npm 包来进行预编译,再通过 DllReferencePlugin 将预编译的模块加载进来
  4. 使用 HappyPack 实现多线程加速编译
  5. 使用 webpack-uglify-parallel 来提升 uglifyPlugin 的压缩速度。原理上 webpack-uglify-parallel 采用了多核并行压缩来提升压缩速度
  6. 使用 Tree-shakingScope Hoisting 来剔除多余代码

# 怎么配置单页应用和多页应用

单页应用可以理解为 webpack 的标准模式,直接在 entry 中指定单页应用的入口即可

多页面应用的话,可以使用 webpack 的 AutoWebPlugin 来完成简单自动化的构建,但是前提是项目的目录结构必须遵守预设的规范。

多页面应用要注意的是:

  • 每个页面都有公共的代码,可以将这些代码抽离出来,避免重复的加载。比如,每个页面都引用了同一套 css 样式表
  • 随着业务的不断扩展,页面可能会不断的追加,所以一定要让入口的配置足够灵活,避免每次添加新页面还需要修改构建配置
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021/8/18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • # webpack 与 grunt、gulp 的不同
  • # webpack、rollup、parcel 优劣
  • # 有哪些常见的 Loader
  • # 有哪些常见的 Plugin
  • # Loader 和 Plugin 的不同
  • # 编写 Loader 或 Plugin 的思路
  • # bundle、chunk、module 是什么
  • # Webpack 的构建流程是什么
  • # Webpack 的热更新是如何实现的
  • # 如何用 webpack 来优化前端性能
  • # 如何提高 webpack 的打包速度
  • # 如何提高 webpack 的构建速度
  • # 怎么配置单页应用和多页应用
相关产品与服务
内容分发网络 CDN
内容分发网络(Content Delivery Network,CDN)通过将站点内容发布至遍布全球的海量加速节点,使其用户可就近获取所需内容,避免因网络拥堵、跨运营商、跨地域、跨境等因素带来的网络不稳定、访问延迟高等问题,有效提升下载速度、降低响应时间,提供流畅的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档