webpack -- 收集面试题篇

136 阅读9分钟

Webpack 本身是一 个面向 JavaScript 且只能处理 JavaScript 代码的模块化打包工具;

webpack是自动化打包的解决方案,或者说是一个模块打包机

image.png

webpack 工作流程

  • 参数解析:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数

  • 找到入口文件:从 Entry 里配置的 Module 开始递归解析 Entry 依赖的所有 Module

  • 调用 Loader 编译文件:每找到一个 Module, 就会根据配置的 Loader 去找出对应的转换规则

  • 遍历 AST,收集依赖:对 Module 进行转换后,再解析出当前 Module 依赖的 Module

  • 生成 Chunk:这些模块会以 Entry 为单位进行分组,一个 Entry 和其所有依赖的 Module 被分到一个组也就是一个 Chunk

  • 输出文件:最后 Webpack 会把所有 Chunk 转换成文件输出

webpack 构建过程

  1. 初始化: 加载Plugin
  2. 开始编译
  3. 确定入口
  4. 编译模块
  5. 完成编译
  6. 输出资源
  7. 输出完成: 将编译后的模块组成Chunk,并转换成文件,输出

webpack5新特性

  • 持久化缓存: cahceDirectory【w4需要下载插件】
  • mode=“production” 会自动开启js压缩功能,自动开启tree-shaking
  • 替换了一些w4里面的一些plugin
  • 热更新:w5 只需要加一个hot:true
  • 自动清除打包后的dist文件夹
    • w4需要下载插件:CleanWebpackPlugin
    • w5仅需在output对象中添加clean: true即可

webpack 如何做代码拆分

Code Split

ⅰ. 为什么
  1. 打包代码时候会将js全部打包到一个文件中,体积太大了,若是只渲染首页,应该只加载首页js文件就行
  2. 所以可以进行分割,生成多个js文件,这样加载资源就少,速度就会更快
ⅱ. 是什么
  1. 主要做两件事:
    a. 分割文件:将打包生成的文件进行分割,生成多个js文件
    b. 按需加载,需要哪个文件就加载哪个文件
ⅲ. 怎么用
  1. 多入口
    a. entry: {}
    b. 多入口按需加载
    c. import 动态导入,会将动态导入的文件代码分割(拆分成单独模块),需要的时候自动加载
  2. 单入口
  
optimization: {
         splitChunks: {
             chunks: 'all'
         },
    },

webpack 中 less-loader的 less 转成 css 的底层原理

主要是依靠less插件中的方法实现的 less.render(scource,{filename: 地址}, ()=>{})

webpack 提升开发体验

  • SourceMap开启:代码映射
  • 提升代码打包速度: 热更新、cache(在plugin中的 new ESLintWebpackPlugin({...,cache:true}))
 devServer: {
        host: "localhost", // 启动服务器域名
        port: "3000", // 启动服务器的端口号
        open: true, // 是否自动打开浏览器
        hot: true // 仅支持css热更新
    },
  • 减少代码体积: tree-shaking、babel、图片压缩
  • 优化代码运行性能:Code Split[代码切割:打包代码会打包在一个文件,太大了。]

webpack 如何优化前端性能

  1. 第三方库按需加载、路由懒加载
  2. 代码分割: 提取第三方库【vendor】、依赖库分离【splitChunks】、删除冗余代码【Tree-shaking】
  3. 压缩代码。uglifyJsPlugin和ParallelUglifyPlugin压缩JS文件;mini-css-extract-plugin压缩css.
  4. 利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径可以利用webpack对于output参数和各loader的publicPath参数来修改资源路
  5. 删除死代码 js用tree shaking ,css需要使用purify-css
  6. 提取公共代码 用commonsChunkPlugin插件

webpack 如何提高构建速度

  1. 缩小编译范围,减少不必要的编译工作,即 modules、mainFields、noParse、includes、exclude、alias 全部用起来。
  2. webpack-parallel-uglify-plugin 能够把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程,从而实现并发编译,进而大幅提升 js 压缩速度
  3. happyPack:在 webpack 运行在 node 中打包的时候是单线程去一件一件事情的做,HappyPack 可以开启多个子进程去并发执行,子进程处理完后把结果交给主进程
  4. 代码压缩: css【css-minimizer-webpack-plugin】、html【HtmlwebpackPlugin】
  5. 图片压缩:image-webpack-loader
  6. tree-shaking
  7. 减少ES6转ES5的冗余代码: babel-plugin-transform-runtime
  8. 提取公共代码:CommonsChunkPlugin
  9. Thead: 开启多进程,获取本地电脑的cpu数量
      //1. 启用进程的数量就是CPU的核数
        //a. 如何获取?
const os = require('os')
const threads = os.cups().lenghth

webpack 热更新原理

热更新又称热替换(Hot Module Replacement),缩写为 HMR,基于 webpack-dev-server。

当你对代码修改并保存后,将会对代码进行重新打包,并将改动的模块发送到浏览器端,浏览器用新的模块替换掉旧的模块,去实现局部更新页面而非整体刷新页面。

热更新原理

image.png

webpack tree-shaking原理

需满足三个条件

  • 使用ESM规范编写模块代码
  • 配置optimization.usedExports 为true 启动标记功能
  • 启动代码优化功能,通过以下方式
    • 配置mode = production
    • 配置optimazation.minimize = true
    • 提供optimization.minimizer数组

实现原理

Webpack 中,Tree-shaking 的实现

、是先「标记」出模块导出值中哪些没有被用过

、是使用 Terser 删掉这些没被用到的导出语句。

标记过程大致可划分为三个步骤:

  • Make 阶段,收集模块导出变量并记录到模块依赖关系图 ModuleGraph 变量中
  • Seal 阶段,遍历 ModuleGraph 标记模块导出变量有没有被使用 ---- 主要集中在‘FlagDependencyUsagePlugin ’插件中,最终会记录在模块导出语句对应的 exportInfo._usedInRuntime 字典中。
  • 生成产物时,若变量没有被其它模块使用则删除对应的导出语句

标记功能需要配置 optimization.usedExports = true 开启

总结:

Webpack 中 Tree Shaking 的实现分为如下步骤:

  • 在 FlagDependencyExportsPlugin 插件中根据模块的 dependencies 列表收集模块导出值,并记录到 ModuleGraph 体系的 exportsInfo 中
  • 在 FlagDependencyUsagePlugin 插件中收集模块的导出值的使用情况,并记录到 exportInfo._usedInRuntime 集合中
  • 在 HarmonyExportXXXDependency.Template.apply 方法中根据导出值的使用情况生成不同的导出语句
  • 使用 DCE 工具删除 Dead Code,实现完整的树摇效果

webpack 与 gulp grunt的区别

webpack Loader的工作流程

从右往左、从下往上
  • webpack.config.js 里配置了一个 模块 的 Loader;

  • 遇到 相应模块 文件时,触发了 该模块的 loader;

  • loader 接受了一个表示该 模块 文件内容的 source;

  • loader 使用 webapck 提供的一系列 api 对 source 进行转换,得到一个 result;

  • 将 result 返回或者传递给下一个 Loader,直到处理完毕。

常见的Loader

模块转换器,用于把模块原内容按照需求转换成新内容。

在 Webpack 中,loader 的执行顺序是从右向左执行的。因为 webpack 选择了 compose 这样的函数式编程方式,这种方式的表达式 执行是从右向左的。

  • url-loader:和 file-loader 类似,但是能在⽂件很⼩的情况下以base64 的⽅式把⽂件内容注⼊到代码中去

  • source-map-loader:加载额外的 Source Map ⽂件,以⽅便断点调试

  • image-loader:加载并且压缩图⽚⽂件

  • image-loader:加载并且压缩图片文件。

  • less-loader: 加载并编译 LESS 文件。

  • sass-loader:加载并编译 SASS/SCSS 文件。

  • css-loader:加载 CSS,支持模块化、压缩、文件导入等特性,使用css-loader必须要配合使用style-loader

  • style-loader:用于将 CSS 编译完成的样式,挂载到页面的 style 标签上。需要注意 loader 执行顺序,style-loader 要放在第一位,loader 都是从后往前执行。

  • babel-loader:把 ES6 转换成 ES5

  • postcss-loader:扩展 CSS 语法,使用下一代 CSS,可以配合 autoprefixer 插件自动补齐 CSS3 前缀。

  • eslint-loader:通过 ESLint 检查 JavaScript 代码。

  • vue-loader:加载并编译 Vue 组件。

  • file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片和字体)

  • url-loader:与 file-loader 类似,区别是用户可以设置一个阈值,大于阈值会交给 file-loader 处理,小于阈值时返回文件 base64 形式编码 (处理图片和字体)**

webpack Plugin简易原理

  • 一个 JavaScript 命名函数。
  • 在插件函数的 prototype 上定义一个 apply 方法。
  • 指定一个绑定到 webpack 自身的事件钩子。
  • 处理 webpack 内部实例的特定数据。
  • 功能完成后调用 webpack 提供的回调

常见的plugin

扩展插件,在 Webpack 构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情。

  • define-plugin:定义全局环境变量
  • html-webpack-plugin:作用一是创建 HTML 页面文件到你的输出目录,作用二是将 webpack 打包后的 chunk 自动引入到这个 HTML 中
  • uglifyjs-webpack-plugin:通过 UglifyES 压缩 ES6 代码
  • webpack-parallel-uglify-plugin: 多核压缩,提⾼压缩速度
  • webpack-bundle-analyzer: 可视化 webpack 输出⽂件的体积
  • mini-css-extract-plugin: CSS 提取到单独的⽂件中,⽀持按需加载

webpack中Loader 与 Plugin 区别

在运行时机上,`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 的实例,参数都通过构造函数传⼊。

webpack 处理css

Webpack 在 loader 的辅助下,是可以处理 CSS 的。

css-loader:导入 CSS 模块,对 CSS 代码进行编译处理; style-loader:创建 style 标签,把 CSS 内容写入标签。

css-loader 必须要写在style-loader前面,只有先编译才能对css代码进行插入

babel原理是什么?

Babel 分为三个阶段

  1. 解析:把代码解析成AST
  2. 变换:对抽象语法树进行变换操作
  3. 再建:根据变化后的AST生成代码

image.png

babel 插件的实现

webpack 是否写过自定义插件

// 自定义事件的插件函数
function myPlugin () {

}
// 对 插件函数扩展 apply 方法
myPlugin.prototype.apply = function(compiler) {
    // webpack提供的编译函数模板,监听webpack的钩子事件,然后会触发 compilation,和插件执行完成的回调。
    compiler.plugin('webpacksEventHook', function(compilation,callback) {
        console.log('this is customer plugins');
        callback();
    });
}