Webpack

介绍目前常见前端面试题,Webpack 基础原理题

参考慕课网实战课 前端框架及项目面试-聚焦 Vue3 React Webpack 总结的知识点

webpack

  • webpack 已是前端打包构建的不二选择
  • 每日必用,面试必考
  • 成熟的工具,重点在于配置和使用,原理并不高优

讲解范围

  • 基本配置
  • 高级配置
  • 优化打包效率
  • 优化产出代码
  • 构建流程概述
  • babel

关于webpack 5

  • webpack5 主要是内部效率的优化
  • 对比 webpack4,没有太多使用上的改动
  • 你可以直接使用 webpack5 来学习课程

回顾之前的 webpack 面试题

  • 前端代码为何要进行构建和打包?
  • module chunk bundle 分别什么意思,有何区别?
  • loader 和 plugin 的区别?
  • webpack 如何实现懒加载?
  • webpack 常见性能优化
  • babel-runtime 和 babel-polyfill 的区别

webpack 基本配置

  • vue-cli create-react-app
  • 常用上述脚手架,而不会自己配置 webpack?
  • 则面试不会通过
  • 拆分配置和merge
  • 启用本地服务
  • 处理 ES6
  • 处理样式
  • 处理图片
  • (模块化)

代码示例用到 webpack-demo 项目的 build-base-conf

webpack 基本配置

webpack 高级配置

  • 基本配置只能做demo,不能做线上项目
  • 面试考察基本配置,只是为了快速判断你是否用过 webpack
  • 以下高级配置,也是通过面试的必要条件
  • 多入口
  • 抽离 CSS 文件
  • 抽离公共代码
  • 多入口

代码示例用到 webpack-demo 项目的 build-multi-entry

多入口

  • 抽离 CSS 文件

代码示例用到 webpack-demo 项目的 build-min-extract-css

抽离 CSS 文件

  • 抽离公共代码

代码示例用到 webpack-demo 项目的 build-splitChunks

抽离公共代码

  • 懒加载
懒加载
  • 处理 JSX

处理 JSX

  • 处理 Vue

处理 Vue

module chunk bundle 的区别

  • module - 各个源码文件,webpack 中一切皆模块
  • chunk - 多模块合并成的,如entry import() splitChunk
  • bundle - 最终的输出文件

module chunk bundle的区别

webpack 性能优化

  • 大厂必考 & 社区热议话题
  • 优化打包构建速度 - 开发体验和效率
  • 优化产出代码 - 产品性能

webpack 性能优化 - 构建速度

  • 优化 babel-loader
  • IgnorePlugin
  • noParse
  • happyPack
  • ParallelUglifyPlugin
  • 自动刷新
  • 热更新
  • DllPlugin

代码演示:

  • 优化 babel-loader

代码示例用到 webpack-demo 项目的 build-optimization

优化 babel-loader

  • IgnorePlugin 避免引入无用模块

    • import moment from ‘moment'
    • 默认会引入所有语言 JS 代码,代码过大
    • 如何只引入中文?

    IgnorePlugin 避免引入无用模块

  • noParse 避免重复打包

noParse 避免重复打包
  • IgnorePlugin vs noParse

    • IgnorePlugin 直接不引入,代码中没有
    • noParse 引入,但不打包
  • happyPack 多进程打包

    • JS 单线程,开启多进程打包
    • 提高构建速度(特别是多核CPU)

    happyPack 多进程打包

  • ParallelUglifyPlugin 多进程压缩 JS

    • webpack 内置 Uglify 工具压缩 JS
    • JS单线程,开启多进程压缩更快
    • 和 happyPack 同理

    ParallelUglifyPlugin 多进程压缩 JS

  • 关于开启多进程

    • 项目较大,打包较慢,开启多进程能提高速度
    • 项目较小,打包很快,开启多进程会降低速度(进程开销)
    • 按需使用
  • 自动刷新

自动刷新
  • 热更新
    • 自动刷新:整个网页全部刷新,速度较慢
    • 自动刷新:整个网页全部刷新,状态会丢失
    • 热更新:新代码生效,网页不刷新,状态不丢失

热更新

  • DlIPlugin 动态链接库插件
    • 前端框架如 vue React,体积大,构建慢
    • 较稳定,不常升级版本
    • 同一个版本只构建一次即可,不用每次都重新构建
    • webpack已内置DlIlPlugin支持
    • DlIPlugin-打包出dll文件
    • DlIReferencePlugin-使用dll文件

代码示例用到 webpack-dll-demo 项目

Webpack 面试题

webpack 性能优化

优化前的准备工作

  • 准备基于时间的分析工具:我们需要一类插件,来帮助我们统计项目构建过程中在编译阶段的耗时情况。speed-measure-webpack-plugin 分析插件加载的时间
  • 使用 webpack-bundle-analyzer 分析产物内容

代码优化:

无用代码消除,是许多编程语言都具有的优化手段,这个过程称为 DCE (dead code elimination),即 删除不可能执行的代码;

例如我们的 UglifyJs,它就会帮我们在生产环境中删除不可能被执行的代码,例如:

1
2
3
4
5
6
7
var fn = function() {
return 1;
// 下面代码便属于 不可能执行的代码;
// 通过 UglifyJs (Webpack4+ 已内置) 便会进行 DCE;
var a = 1;
return a;
}

摇树优化 (Tree-shaking),这是一种形象比喻。我们把打包后的代码比喻成一棵树,这里其实表示的就是,通过工具 “摇” 我们打包后的 js 代码,将没有使用到的无用代码 “摇” 下来 (删除)。即 消除那些被 引用了但未被使用 的模块代码。

  • 原理: 由于是在编译时优化,因此最基本的前提就是语法的静态分析,ES6的模块机制 提供了这种可能性。不需要运行时,便可进行代码字面上的静态分析,确定相应的依赖关系。
  • 问题: 具有 副作用 的函数无法被 tree-shaking
    • 在引用一些第三方库,需要去观察其引入的代码量是不是符合预期;
    • 尽量写纯函数,减少函数的副作用;
    • 可使用 webpack-deep-scope-plugin,可以进行作用域分析,减少此类情况的发生,但仍需要注意;

code-spliting: 代码分割技术,将代码分割成多份进行 懒加载 或 异步加载,避免打包成一份后导致体积过大,影响页面的首屏加载;

  • Webpack 中使用 SplitChunksPlugin 进行拆分;
  • 按 页面 拆分: 不同页面打包成不同的文件;
  • 按 功能 拆分:
    • 将类似于播放器,计算库等大模块进行拆分后再懒加载引入;
    • 提取复用的业务代码,减少冗余代码;
  • 按 文件修改频率 拆分: 将第三方库等不常修改的代码单独打包,而且不改变其文件 hash 值,能最大化运用浏览器的缓存;

scope hoisting: 作用域提升,将分散的模块划分到同一个作用域中,避免了代码的重复引入,有效减少打包后的代码体积和运行时的内存损耗;

编译性能优化:

  • 升级至 最新 版本的 webpack,能有效提升编译性能;
  • 使用dev-server/ 模块热替换 (HMR) 提升开发体验;
    • 监听文件变动 忽略 node_modules 目录能有效提高监听时的编译效率;
  • 缩小编译范围
    • modules: 指定模块路径,减少递归搜索;
    • mainFields: 指定入口文件描述字段,减少搜索;
    • noParse: 避免对非模块化文件的加载;
    • includes/exclude: 指定搜索范围/排除不必要的搜索范围;
    • alias: 缓存目录,避免重复寻址;
  • babel-loader
    • 忽略node_moudles,避免编译第三方库中已经被编译过的代码
    • 使用cacheDirectory,可以缓存编译结果,避免多次重复编译
  • 多进程并发
    • webpack-parallel-uglify-plugin: 可多进程并发压缩 js 文件,提高压缩速度;
    • HappyPack: 多进程并发文件的 Loader 解析;
  • 第三方库模块缓存:
    • DLLPluginDLLReferencePlugin 可以提前进行打包并缓存,避免每次都重新编译;
  • 使用分析
    • Webpack Analyse / webpack-bundle-analyzer 对打包后的文件进行分析,寻找可优化的地方
    • 配置profile:true,对各个编译阶段耗时进行监控,寻找耗时最多的地方
  • source-map:
    • 开发: cheap-module-eval-source-map
    • 生产: hidden-source-map

优化webpack打包速度

  • 减少文件搜索范围
    • 比如通过别名
    • loadertestinclude & exclude
  • Webpack4 默认压缩并行
  • Happypack 并发调用
  • babel 也可以缓存编译
  • Resolve 在构建时指定查找模块文件的规则
  • 使用DllPlugin,不用每次都重新构建
  • externalsDllPlugin解决的是同一类问题:将依赖的框架等模块从构建过程中移除。它们的区别在于
    • 在 Webpack 的配置方面,externals 更简单,而 DllPlugin 需要独立的配置文件。
    • DllPlugin 包含了依赖包的独立构建流程,而 externals 配置中不包含依赖框架的生成方式,通常使用已传入 CDN 的依赖包
    • externals 配置的依赖包需要单独指定依赖模块的加载方式:全局对象、CommonJS、AMD 等
    • 在引用依赖包的子模块时,DllPlugin 无须更改,而 externals 则会将子模块打入项目包中

优化打包体积

  • 提取第三方库或通过引用外部文件的方式引入第三方库
  • 代码压缩插件UglifyJsPlugin
  • 服务器启用gzip压缩
  • 按需加载资源文件 require.ensure
  • 优化devtool中的source-map
  • 剥离css文件,单独打包
  • 去除不必要插件,通常就是开发环境与生产环境用同一套配置文件导致
  • Tree Shaking 在构建打包过程中,移除那些引入但未被使用的无效代码
  • 开启scope hosting
    • 体积更小
    • 创建函数作用域更小
    • 代码可读性更好

img img

打包体积 优化思路

  • 提取第三方库或通过引用外部文件的方式引入第三方库
  • 代码压缩插件UglifyJsPlugin
  • 服务器启用gzip压缩
  • 按需加载资源文件 require.ensure
  • 优化devtool中的source-map
  • 剥离css文件,单独打包
  • 去除不必要插件,通常就是开发环境与生产环境用同一套配置文件导致

打包效率

  • 开发环境采用增量构建,启用热更新
  • 开发环境不做无意义的工作如提取css计算文件hash等
  • 配置devtool
  • 选择合适的loader
  • 个别loader开启cachebabel-loader
  • 第三方库采用引入方式
  • 提取公共代码
  • 优化构建时的搜索路径 指明需要构建目录及不需要构建目录
  • 模块化引入需要的部分

Loader

编写一个loader

1
loader`就是一个`node`模块,它输出了一个函数。当某种资源需要用这个`loader`转换时,这个函数会被调用。并且,这个函数可以通过提供给它的`this`上下文访问`Loader API`。 `reverse-txt-loader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 定义
module.exports = function(src) {
//src是原文件内容(abcde),下面对内容进行处理,这里是反转
var result = src.split('').reverse().join('');
//返回JavaScript源码,必须是String或者Buffer
return `module.exports = '${result}'`;
}
//使用
{
test: /\.txt$/,
use: [
{
'./path/reverse-txt-loader'
}
]
},

说一下 webpack 的一些 plugin,怎么使用 webpack 对项目进行优化

构建优化

  • 减少编译体积 ContextReplacementPuginIgnorePluginbabel-plugin-importbabel-plugin-transform-runtime
  • 并行编译 happypackthread-loaderuglifyjsWebpackPlugin开启并行
  • 缓存 cache-loaderhard-source-webpack-pluginuglifyjsWebpackPlugin开启缓存、babel-loader开启缓存
  • 预编译 dllWebpackPlugin && DllReferencePluginauto-dll-webapck-plugin

性能优化

  • 减少编译体积 Tree-shakingScope Hositing
  • hash缓存 webpack-md5-plugin
  • 拆包 splitChunksPluginimport()require.ensure

谈谈你对webpack的看法

  • WebPack 是一个模块打包工具,你可以使用WebPack管理你的模块依赖,并编绎输出模块们所需的静态文件。它能够很好地管理、打包Web开发中所用到的HTMLJavascriptCSS以及各种静态文件(图片、字体等),让开发过程更加高效。对于不同类型的资源,webpack有对应的模块加载器。webpack模块打包器会分析模块间的依赖关系,最后 生成了优化且合并后的静态资源

gulp是什么

  • gulp是前端开发过程中一种基于流的代码构建工具,是自动化项目的构建利器;它不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成
  • Gulp的核心概念:流
  • 流,简单来说就是建立在面向对象基础上的一种抽象的处理数据的工具。在流中,定义了一些处理数据的基本操作,如读取数据,写入数据等,程序员是对流进行所有操作的,而不用关心流的另一头数据的真正流向
  • gulp正是通过流和代码优于配置的策略来尽量简化任务编写的工作
  • Gulp的特点:
    • 易于使用:通过代码优于配置的策略,gulp 让简单的任务简单,复杂的任务可管理
    • 构建快速 利用 Node.js 流的威力,你可以快速构建项目并减少频繁的 IO 操作
    • 易于学习 通过最少的 API,掌握 gulp 毫不费力,构建工作尽在掌握:如同一系列流管道