对于webpack面试题的总结

前言


最近在网上看到的一些优秀的webpack的面试总结:
「吐血整理」再来一打Webpack面试题
浅谈 webpack 性能优化(内附 webpack 学习笔记)
揭秘webpack plugin
webpack 的 loader 和 plugin 你真的弄懂了吗
Webpack HMR 原理解析
根据以上文档,自己总结一份简陋版的webpack笔记

webpack构建流程


  1. 初始化参数:从配置文件webpack.config.js和shell语句中读取与合并,得到最终参数options。
  2. 开始编译:用步骤1得到的参数options初始化Compiler对象,加载并配置所有插件,执行对象的run方法。
  3. 确定入口:根据配置的entry找出所有入口文件。
  4. 编译模块:从入口文件出发,调用所有配置的loader对模块进行翻译,再找出该模块依赖的模块,递归以上步骤描述,直到所有入口依赖的文件经过了处理。
  5. 完成模块编译:在经过步骤4使用Loader对所有模块都进行翻译过后,得到每个模块被翻译后的最终内容以及其相互之间的依赖关系。
  6. 输出资源:根据入口及模块之间的依赖关系组装成一个个包含多模块的chunk,再把每个chunk转换成一个单独的文件加入到输出列表,此步骤为可修改输出内容的最后机会。
  7. 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。

Loader和Plugin的区别及实现


提到 webpack,自然离不开 loader 与 blugin。Webpack 就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出结果(loader)。这条生产线上的每个处理流程的职责都是单一的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。而插件(plugin)就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。

  • Loader:用于对模块源码的转换,loader描述了webpack如何处理非javascript模块,并且在build中引入这些依赖。loader可以将文件从不同的语言(如TypeScript)转换为JavaScript。
  • Plugin:目的在于解决loader无法实现的其他事,从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务。

简而言之,loader可以理解成webpack的横向广度,有了loader,webpack才可以打包处理各种的扩展语言。而plugin可以理解为webpack的纵向深度,在生命周期内注入不同的插件来扩展更多的能力。

Loader

Loader 就像是一个翻译官,每个 loader 可以把源资源转换成新的结果输出并传递给下一个 loader ,但是最后一个 Loader 必须返回 JavaScript (浏览器只能运行js代码,不支持其他扩展语言)。

以处理less文件为例:

module:{ 
    rules: [  
        {    
            test: /\.less$/,    
            use: ['style-loader', 'css-loader', 'less-loader']  
        } 
    ]
}
  • less-loader: 将 less 源代码转化为 css
  • css-loader:处理 less-loader 输出的 css,找出 css 中依赖的资源(@import 等),压缩资源
  • sytle-loader:处理 css-loader 输出的 css,把 css 转换成脚本加载的 js 代码插入到 DOM 中

项目中常用的loader

  • file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片和字体)
  • url-loader:与 file-loader 类似,区别是用户可以设置一个阈值,大于阈值会交给 file-loader 处理,小于阈值时返回文件 base64 形式编码 (处理图片和字体)
  • image-loader:加载并且压缩图片文件
  • svg-inline-loader:将压缩后的 SVG 内容注入代码中
  • babel-loader:把 ES6 转换成 ES5
  • ts-loader: 将 TypeScript 转换成 JavaScript
  • awesome-typescript-loader:将 TypeScript 转换成 JavaScript,性能优于 ts-loader
  • sass-loader:将SCSS/SASS代码转换成CSS
  • less-loader: 将 less 源代码转化为 css
  • css-loader:加载处理CSS,支持模块化、压缩、文件导入等特性
  • style-loader:处理 css-loader 输出的 css,把 css 转换成脚本加载的 js 代码插入到 DOM 中
  • eslint-loader:通过 ESLint 检查 JavaScript 代码
  • tslint-loader:通过 TSLint检查 TypeScript 代码
  • cache-loader: 可以在一些性能开销较大的 Loader 之前添加,目的是将结果缓存到磁盘里

那么,实现一个loader
要求:把项目 txt 文件中的蒋梨花全部替换为梨花酱
1) 在config.js中配置项目中 .txt 结尾的文件使用我们的 demo-loader

// webpack.config.js
module:{  
    rules: [    
        {        
            test: /\.txt$/,        
            use: ['demo-loader'],        
            options: {            
                name: '梨花酱' // 将要变更的通过配置项传入        
            }      
        }  
    ]
}

2)创建一个包含蒋梨花的txt文件,并引用(webpack不会处理未引用的文件)

// test.txt
你好,我是蒋梨花

// app.js (入口文件引用)
const text = require(./text.txt)
console.log(test)

3)编写 loader

// demo-loader.js
const loaderUtils = require('loader-utils') 
// 接收options配置
module.exports = function(source) {    
    const options = loaderUtils.getOptions(this)    
    source = source.replace(/蒋梨花/g, options.name)    
    return `module.exports = ${JSON.stringify(sorce)}`    
    // 最终需要返回一段可执行的js脚本
}


Plugin

plugin是运行在webpak打包过程中的某段逻辑,它主要的作用是根据webpack提供的一些hooks来进行一些额外的操作,使 webpack 更加灵活扩展。

plugins: [    
    new HtmlWebpackPlugin()  
]

我们通过 new 来使用这个插件,可以看出插件的本质是一个构造函数。
首先了解两个概念:Compiler 和Compilation

compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。

compilation 对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。

在 webpack 启动后,它会执行 new xxxPlugin(options) 来初始化插件实例。在初始化对象后,会去调用 xxxPlugin.apply(compiler) 并传入 compiler 对象。插件获得 compiler 对象后,可以通过
compiler.plugin('事件名', 回调函数) 的方式进行监听 webpack 广播出来的事件了。

项目中常用的plugin

  • html-webpack-plugin: 在打包结束后,⾃动生成⼀个 html ⽂文件,并把打包生成的js 模块引⼊到该 html 中
  • clean-webpack-plugin: 删除(清理)构建目录
  • mini-css-extract-plugin: 提取 CSS 到一个单独的文件中
  • terser-webpack-plugin: 支持压缩 ES6 (Webpack4)
  • webpack-merge: 提取公共配置,减少重复配置代码
  • HotModuleReplacementPlugin: 模块热更新(启用 HMR 很容易,且在大多数情况下不需要任何配置。)

那么,实现一个plugin
1) 在配置文件中,使用插件

// webpack.config.js   
plugins: [    
  new MyTestPlugin({       
    msg: '你好我是梨花酱' // 传入的插件配置    
  })
]

2)编写 plugin 插件

// MyTestPlugin.js
const { ConcatSource } = require("webpack-sources") // 用来写入
class MyBannerPlugin {  
    constructor(options) { 
        // 获取传入的option信息    
        this.msg = options.msg  
    },  // 我们需要一个apply方法(为了获取compiler),接收compiler作为参数表示这次打包的上下文。  
    apply (compiler) {    
        const msg = this. msg    // 指定挂载的 webpack 钩子函数    
        // 使用compiler钩子compilation,即编译(compilation)创建之后,执行插件。    
        compiler.hooks.compilation.tap("MyTestPlugin", compilation => {      
        // compilation的 optimizeChunkAssets 钩子,可以利用这个钩子实现为每个文件插入信息      
            compilation.hooks.optimizeChunkAssets.tap("MyTestPlugin", chunks => {        
                for (const chunk of chunks) {          
                    for (const file of chunk.files) {            
                        compilation.updateAsset(file, old => {                       
                            return new ConcatSource(msg,"\n", old);            
                        });          
                    }        
                }      
            })    
        })  
    }
}
module.exports = MyTestPlugin

可以看出,要实现一个plugin需要以下几步:

  • 首先需要声明一个 class 构造函数
  • 在class里面定义一个apply方法,接收compiler作为参数表示这次打包的上下文。
  • 指定挂载的webpack事件钩子
  • 处理webpack内部实例的特定数据
  • 功能完成后调用webpack提供的回调

具体的 compiler 钩子 和 compilation 钩子可以参考官方文档:https://www.webpackjs.com/api/compilation-hooks/#optimizechunkassets


利用webpack进行优化


  • JS压缩webpack4.0 默认是使用 terser-webpack-plugin 这个压缩插件,在此之前是使用 uglifyjs-webpack-plugin,两者的区别是后者对 ES6 的压缩不是很好,同时我们可以开启 parallel 参数,使用多进程压缩,加快压缩。
  • CSS 压缩:我们可以借助 optimize-css-assets-webpack-plugin 插件来压缩 css,其默认使用的压缩引擎是 cssnano
  • 擦除无用的 CSSoptimize-css-assets-webpack-plugin + mini-css-extract-plugin
  • 图片压缩: 我们就可以借助 image-webpack-loader 帮助我们来实现。(只要在 file-loader 之后加入 image-webpack-loader)
  • 减少图片的HTTP请求:借助url-loader
  • webpack-merge来整合两个配置文件共同的配置 webpack.common.js
  • 利用多线程提升构建速度thread-loader
  • 预先编译资源模块DllPlugin
  • 缓存 Cache 相关: babel-loader 开启缓存、terser-webpack-plugin 开启缓存、使用 cache-loader 或者 hard-source-webpack-plugin

详细内容可参考:https://zhuanlan.zhihu.com/p/139498741

你可能感兴趣的:(对于webpack面试题的总结)