1.产出结果
webpack打包输出的结果就是一个IIFE,通过这个立即执行函数及webpack_require来支持各种模块化打包方案。按需加载时会输出执行入口文件和异步加载文件。
2.工作原理
- 读取配置:读取项目中开发者定义的webpack.config.js配置文件,或者从shell语句中获取必要参数。
- 挂载插件:将所需插件实例化,在事件流上挂载插件钩子。
- 编译解析:从定义的入口文件(可以不止一个)开始,进行依赖收集,依赖loaders对所有依赖的文件进行编译。编译好的内容使用acorn或其他抽象语法树能力,解析生成抽象语法树(方便开发者提取模块文件中的关键信息),分析文件依赖关系,将不同模块化语法(如require)等替换为__webpack_require__,即使用webpack加载器进行模块化实现。
- 产出结果:根据配置将结果打包到相应目录。
3.核心对象
- compiler:它的实例包含了完整的webpack配置,且全局只有一个compiler实例,通过这个对象可以访问webpack的内部环境。
- compilation:以开发模式运行时,每当检测到文件变化时,就会创建一个新的对象,它包含了当前的模块资源、编译生成资源、变化的文件等信息。构建过程中产生的构建数据都会被存储在该对象上,还提供了很多事件回调供插件做扩展。
webpack的构建过程是通过compiler控制流程,通过compilation进行代码解析。
4.loader
loader的工作就是接收源文件,对源文件进行处理,并返回编译后的文件,比如babel将ES Next编译成ES5,sass-loader将SCSS/Sass编译成CSS等。
module.exports = [
...
module: {
rules: [{
test: /\.less$/,
use: [{
loader: 'style-loader'
},{
loader: 'css-loader'
},{
loader: 'less-loader'
}]
}]
}
]
串联调用多个loader时,执行顺序和配置顺序是相反的。
5.plugin
在webpack构建的生命周期中,webpack会广播许多事件,在该机制下,开发者注册的各种插件可以根据需要监听与自身相关的事件。插件捕获事件后,可以在合适的时机通过webpack提供的API去改变编译输出结果。
- loader是一个转换器,执行单纯的文件转换操作。
- plugin是一个扩展器,丰富了webpack。监听打包过程中的某些事件,修改打包结果。
//对相关钩子进行访问
compiler.hooks.someHook.tap()
//读取配置后执行某项工作
compiler.hooks.entryOption.tap()
//资源输出前执行某个功能
compiler.hooks.emit.tap()
//异步操作
compiler.hooks.emit.tapAsync()
compiler.hooks.emit.tapPromise()
自定义plugin实例:
class CustomPlugin {
constructor(options) {
this.options = options
}
apply(compiler) {
//相关钩子的注册回调
compiler.hooks.someHook.tap('CustomPlugin', () => {
//
})
}
}
module.exports = customPlugin
6.Rollup
号称下一代打包方案,功能和特点:依赖解析,打包构建;仅支持ES Next模块;内置支持tree shaking。
建库用Rollup,其他场景用webpack:
- 使用webpack打包会生成比较多的冗余代码,对于业务代码来说,能保证较强的健壮性和语法还原度,有利于兼容性。
- Rollup通过将代码顺序引入同一个文件来解决模块依赖问题,复杂冗余的代码就会完全消失。