theme: vue-pro
1、Webpack 的构建流程主要有哪些环节?如果可以请尽可能详尽的描述 Webpack 打包的整个过程。
webpack的概述:
webpack是一个模块打包工具,它将一切文件都视为模块,通过loader编译转换文件,通过plugin注入钩子,最后
将输出的资源模块组合成文件。主要的配置信息有entry、output、module、plugins
构建流程:
- 创建
compiler实例,用于控制构建流程,compiler实例包含webpack基本环境信息 - 根据配置项转换成对应的内部插件,并初始化
options配置项 - 执行
compiler.run - 创建
comppilation实例,每次构建都会新建一个comppilation实例,包含了这次构建的基本信息 - 从
entry开始递归分析依赖,对每个依赖模块会进行buildModule,通过Loader将不同类型的模块转换成webpack模块 - 通过
Parser.parse将上面的结果转换成AST树 - 遍历
AST树,收集依赖dependency,并保存在compilation实例的dependiencies属性中 - 生成
chunks,不同entry生成不同chunk, 动态导入也会生成自己的chunk,生成chunk后还会进行优化 - 使用
template基于compilation的数据生成结果代码
总结:
webpack打包输出的文件其实就是一个闭包,传入的参数是一个对象,键值为所有输出文件的路径,内容为eval包裹的文件内容;闭包内重写了模块的加载方式,自己定义了__webpack_require__方法,来实现模拟的common.js规范模块加载机制。
webpack实际是基于事件流的,通过一系列的插件运行。webpack利用tapable库提供各种钩子来实现对于整个构建流程各个步骤的控制。
2、Loader 和 Plugin 有哪些不同?请描述一下开发 Loader 和 Plugin 的思路。
Loader:
用于对模块文件进行编译转换和加载处理,在modules.rules数组中进行配置,它用于告诉webpack在遇到什么类型的文件,应该采用哪些Loader进行加载和转换,loader可以通过querystring或object的方式指定选项参数。处理一类文件可以使用多个loader,loader的执行顺序类似出栈的方式(从后向前执行)
Plugin:
主要是通过webpack内部的钩子机制,在webpack构建的不同阶段执行一些额外的工作。从打包 优化和压缩,到从新定义环境变量,功能强大到可以用来处理各种各样的任务。plugin让webpack的机制更加灵活,他的编译过程中留下的一系列生命周期钩子,通过调用这些钩子来实现在不同编译结果时对源模块进行处理。它的插件是一个函数或者一个包含apply方法的对象,接收一个compile对象,通过webpack的钩子来处理资源
开发Loader的思路:
- 通过module.export 导出一个函数
- 函数的默认参数为要处理的文件source
- 函数体中处理资源
- 返回处理后结果(交给下一个loader 进行处理)
const marked = require('marked')
module.exports = source => {
// console.log(source)
const html = marked(source)
// 返回一段js代码
// return `export default ${JSON.stringify(html)}`
return html
}
开发Plugin的思路:
- 通过钩子机制实现,在生命周期的钩子中挂载函数实现扩展
- 函数方法体内通过webpack提供的api获取资源做相应处理
- 将处理完的资源通过webpack提供的方法返回
class MyPlugin {
apply (compiler) {
console.log('自定义插件')
// tap方法注册钩子函数(emit是其中一个钩子)
compiler.hooks.emit.tap('MyPlugin', compilation => {
// compilation可以理解为此次打包的上下文
for (const name in compilation.assets) {
if (name.endsWith('.js')) {
const contents = compilation.assets[name].source()
const withoutComments = contents.replace(/\/\*\*+\*\//g, '')
compilation.assets[name] = {
source: () => withoutComments,
size: () => withoutComments.length
}
}
}
})
}
}