2020-web questions

92 阅读6分钟

webpack: 有哪些常见的Loader?你用过哪些Loader?

  • raw-loader: 加载文件原始内容(utf-8)
  • file-loader: 把文件输出到一个文件夹中, 在代码中通过相对 URL 去引用输出的文件 (处理图片和字体)
  • url-loader: 与 file-loader 类似, 区别是用户可以设置一个阈值, 大于阈值会交给 file-loader 处理, 小于阈值时返回文件 base64 形式编码 (处理图片和字体)
  • image-loader: 加载并且压缩图片文件
  • json-loader 加载 JSON 文件
  • babel-loader: 把 ES6 转换成 ES5
  • sass-loader: 将SCSS/SASS代码转换成CSS
  • css-loader: 加载 CSS, 支持模块化、压缩、文件导入等特性
  • style-loader: 把 CSS 代码注入到 JavaScript 中, 通过 DOM 操作去加载 CSS
  • postcss-loader: 扩展 CSS 语法, 使用下一代 CSS, 可以配合 autoprefixer 插件自动补齐 CSS3 前缀
  • eslint-loader: 通过 ESLint 检查 JavaScript 代码
  • vue-loader: 加载 Vue.js 单文件组件

webpack: 有哪些常见的Plugin?你用过哪些Plugin?

  • define-plugin: 定义环境变量 (Webpack4 之后指定 mode 会自动配置)
  • ignore-plugin: 忽略部分文件
  • copy-webpack-plugin: 复制不参与构建的文件
  • html-webpack-plugin: 创建HTML文件.并将生成的bundle.js插入到html页面中
  • friendly-errors-webpack-plugin: 构建信息输出
  • webpack-bundle-analyzer: 可视化 Webpack 输出文件的体积
  • circular-dependency-plugin: 循环依赖检查
  • AssetsPlugin: 缓存 bundle.js
  • uglifyjs-webpack-plugin: 压缩JS代码,不包括ES6
  • clean-webpack-plugin: dist目录清理

webpack: Loader和Plugin的区别

  • Loader 本质就是一个函数, 在该函数中对接收到的内容进行转换, 返回转换后的结果. 因为 Webpack 只认识 JavaScript, 所以 Loader 就成了翻译官, 对其他类型的资源进行转译的预处理工作.在 module.rules 中配置, 作为模块的解析规则, 类型为数组.每一项都是一个 Object, 内部包含了 test(类型文件)、loader、options (参数)等属性.
  • Plugin 就是插件, 基于事件流框架 Tapable, 插件可以扩展 Webpack 的功能, 在 Webpack 运行的生命周期中会提供很多钩子函数, Plugin 可以监听这些事件, 在合适的时机通过 Webpack 提供的 API 改变输出结果.

webpack: Webpack构建流程

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

【初始化】: 启动构建, 读取与合并配置参数, 加载 Plugin, 实例化 Compiler 【编译】: 从 Entry 出发, 针对每个 Module 串行调用对应的 Loader 去翻译文件的内容, 再找到该 Module 依赖的 Module, 递归地进行编译处理 【输出】: 将编译后的 Module 组合成 Chunk, 将 Chunk 转换成文件, 输出到文件系统中

webpack: 使用webpack开发时, 你用过哪些可以提高效率的插件?

  • webpack-dashboard:可以更友好的展示相关打包信息.
  • webpack-merge:提取公共配置, 减少重复配置代码
  • friendly-errors-webpack-plugin: 信息提示
  • size-plugin:监控资源体积变化, 尽早发现问题

webpack: source map是什么?生产环境怎么用?

  • source map 是将编译、打包、压缩后的代码映射回源代码的过程.打包压缩后的代码不具备良好的可读性, 想要调试源码就需要 soucre map
  • 开发模式下: cheap-module-eval-source-map
  • 生产模式下: nosources-source-map

webpack: 模块打包原理

Webpack 实际上为每个模块创造了一个可以导出和导入的环境, 本质上并没有修改 代码的执行逻辑, 代码执行顺序与模块加载顺序也完全一致.

webpack: Webpack 的热更新原理

  • Webpack 的热更新又称热替换(Hot Module Replacement), 缩写为 HMR. 这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块.
# HMR的核心就是客户端从服务端拉去更新后的文件,准确的说是 chunk diff (chunk 需要更新的部分)
#  WDS 与浏览器之间维护了一个 Websocket
# 当本地资源发生变化时, WDS 会向浏览器推送更新, 并带上构建时的 hash, 让客户端与上一次资源进行对比
# 客户端对比出差异后会向 WDS 发起 Ajax 请求来获取更改内容(文件列表、hash)
# 客户端就可以再借助这些信息继续向 WDS 发起 jsonp 请求获取该chunk的增量更新.
# 后续的部分(拿到增量更新之后如何处理?哪些状态该保留?哪些又需要更新?)由 HotModulePlugin 来完成

webpack: 如何对bundle体积进行监控和分析

  • 使用 webpack-bundle-analyzer 生成 bundle 的模块组成图,显示所占体积。

webpack: 文件指纹是什么?怎么用

文件指纹是打包后输出的文件名的后缀。

  • Hash:和整个项目的构建相关,只要项目文件有修改,整个项目构建的 hash 值就会更改
  • Chunkhash:和 Webpack 打包的 chunk 有关,不同的 entry 会生出不同的 chunkhash
  • Contenthash:根据文件内容来定义 hash,文件内容不变,则 contenthash 不变
  • JS文件指纹设置: outputfilename 中设置,使用 chunkhash
  • CSS的文件指纹设置: MiniCssExtractPlugin插件中的filename设置,使用contenthash
  • 图片的文件指纹设置: file-loader 中的 options中设置 使用 hash

webpack: 如何保证各个loader按照预想方式工作

  • 可以使用 enforce 强制执行 loader 的作用顺序
  • pre 代表在所有正常 loader 之前执行,
  • post 是所有 loader 之后执行

webpack: 如何优化 Webpack 的构建速度

  • 使用高版本的 Webpack 和 Node.js
  • 多进程/多实例构建: thread-loader
  • 压缩代码
# terser-webpack-plugin / uglifyjs-webpack-plugin 压缩JS代码
# 图片压缩: 基于 Node 库的 imagemin /  image-webpack-loader
# 缩小打包作用域: exclude / include; resolve.modules指明第三模块的绝对路径; resolve.extension 减少后缀尝试的可能性; 合理使用别名
# 提取共通资源
# DLL: 使用DllPlugin进行分别,缓存打包的静态资源,避免反复编译
# 开启Tree shaking

instanceOf code

function instanceOf(A, B) {
  if (!A || !B) {
    return false
  }

  // 获取函数的原型属性
  var O = B.prototype
  // 获取实例的原型
  A = A.__proto__

  while (true) {
    if (A === null) {
      return false // A 已经找到了顶端
    } else if (A === O) {
      return true
    } else {
      A = A.__proto__ // 循环找
    }
  }
}