看其他的人写webpack的面试题写的就差不多是把文档抄了一遍。作为学习资料还行,真正面试中面试官不会问出API如何使用等类似问题。此类问题最没有价值。
在面的这六七家公司中,关于webpack方面问的最多的问题就2个。
webpack loader和plugin的区别
该问题问的最多,只要你了解webpack基本都能说上来
- webback中js作为一等公民,loader 赋予了webpack处理其他类型文件的能力,比如css、图片等。
- plugin是用来增强文webpack的能力,让webpack有更多的活性。
如何编写一个plugin
这个问题看官方文档最明了。自己总结一下就是:
- 使用类来编写,必须实现一个apply方法;如果是方法实现,需要在其prototype 上定义一个 apply 方法。
class webpackPlugin {
constructor(){
console.log('插件被使用了')
}
apply(compiler) {
// compiler 很重要,是webpack的一个实例,这个实例存储了webpack各种信息,所有打包信息
}
}
module.exports = webpackPlugin;
- 我们的逻辑是在apply中实现
目前是用两种实现api,一种是 compiler.plugin('done'…);一种是 compiler.hooks.done。后者是新的api,目前使用较多。
给大家一个小的demo吧,实现一个生成versionMapping文件的plugin,就是每次生成的version 和 源文件写到一个文件中。
/**
*
* options={
* outputName 输出的文件名
* }
*/
class CreateVersionsMapping {
constructor(options) {
this.options = options;
}
apply(compiler) {
const outputName = this.options.outputName || 'versions.mapping';
compiler.hooks.emit.tap('createVm', (compilation) => {
// 创建一个头部字符串:
let fileList = '';
for (let filename in compilation.assets) {
const reg = /(.+)@([0-9a-z]+)\.((?:js|css))/;
const matchList = filename.match(reg);
if (matchList) {
filename = `{matchList[1]}.{matchList[3]}#{matchList[2]}`;
fileList += `${filename}\n`;
}
}
// 把它作为一个新的文件资源插入到 webpack 构建中:
compilation.assets[outputName] = {
source() {
return fileList;
},
size() {
return fileList.length;
},
};
// callback();
});
}
}
module.exports = CreateVersionsMapping;
demo 使用方法,在webpack的配置中
...
const Cvm = require('./CreateVersionsMapping');
...
...
...
plugins:[
new Cvm({ outputName: 'version.mapping' }),
]
...
执行后我们的输入目录中会增加一个 version.mapping 文件,内容就是version和文件的对应关系。
common~views/Main.js#3382d0a4a3ce
index.js#acaee480b87e
vendors~views/Main.js#1b7e16be8c04
views/Main.js#38ceb72893a4
好多初次接触webpack的朋友会觉得写一个plugin很难,其实很简单。敢下手写就行,不要怂,就是怼。
webpack的执行逻辑是什么
说句实在的,我面试的这几家公司,包括美团、阿里、头条等大厂,我觉得他们的面试官都不是特别的熟悉这套流程。
- 解析shell和config
- config合并和插件加载,生成一个options对象
- 开始编译和构建流程
- new 一个 compiler,调用compiler.run() 构建一个 Compilation对象,拥有超多的fun。两个作用:
1、负责组织整个打包过程,包含了每个构建环节和输出环节所对应的方法,如 addEntry() , _addModuleChain() , buildModule() , seal() , createChunkAssets() (在每一个节点都会触发 webpack 事件去调用各插件)。
2、二是该对象内部存放着所有 module ,chunk,生成的 asset 以及用来生成最后打包文件的 template 的信息。
-
调用addEntry开始构建
1、调用各loader处理模块之间的依赖,对每一个 require() 用对应的 loader 进行加工,最后生成一个 js module
2、调用AST解析器将loader处理后的源文件生成抽象语法树
3、遍历AST,构建改模块所依赖的模块。依赖模块会按照上述方法递归处理
-
所有的模块及其依赖项build之后,调用各插件对构建的结果进行封装,对每一个module和chunk进行整理。生成编译后的源码。 这是我们在开发时进行代码优化和功能添加的关键环节。
-
生成最终assets
1、模块封装在这一步,判断是入口js还是异步加载的js,调用不同的模板对象进行封装(入口js renderChunkModules)(异步加载的 js 会调用 chunkTemplate 中的 render 方法)。
2、模块封装,module.source()
3、生成assets
- 输出 按照 output 中的配置项将文件输出到了对应的 path 中,从而 webpack 整个打包过程结束。
如何使用webpack项目优化
这个完全是实战经验,并且每个公司或者项目根据使用场景不同,优化的手段也是不同的。但是大致就是几个方向。
- css的优化,
- 使用 mini-css-extract-plugin 来做文件合并
- 使用postcss-loader、autoprefixer来帮我们自动补齐浏览器前缀等
- css压缩
- js的优化:
- 抽取公共代码 SplitChunksPlugin,这个在webpack4以后可以直接在 optimization 中配置。
- 懒加载,这个可以自己实现一个,或者是使用现有的库。
- noParse
在使用类似于jq库的时候,我们不需要webpack再去构建其内部依赖关系,这时候我们可以手动干预。在module中 noParse: /jquery/ -
DLLPlugin
dll是一个很好的优化手段,可以极大的提升我们的编译速度。让我们每次只编译业务代码,类似于React、Redux等我们不更改的包,只打包一次就可以。 DLL的使用需要自己写一丢丢代码,也很简单,具体看官方文档。
总结
上述四个问题是我最近的面试中问的webpack题目中最频繁的,也是我们应该掌握的基本题目。当然如果你了解webpack的核心 tapable 不妨在面试中拿出来讲讲。webpack整个流程都依赖于这套插件系统。
文章评论