⚡ 30 秒速记
- 入口出发 → 找依赖 → 转换模块 → 组织产物
loader管源码转换,plugin介入构建流程- 模块关系是图,共享依赖和循环引用都可能存在
chunk是分组单位,输出资源还包含运行时代码Babel是可选转换工具,不能等同于整个webpack
打包就是把源码之间的依赖关系整理好,生成浏览器能加载的一组资源。 webpack 从入口找出引用的模块,交给匹配的 loader 转换,再分析转换后的依赖,递归完成模块图。接着按入口、动态导入和拆包配置组织代码块,输出文件与加载它们的运行时。理解这条链路,才能分清语法转换报错、模块找不到和浏览器加载资源失败分别发生在哪一步。
版本校准: 旧文中的 webpack 4、JSONP 更新清单或 react-hot-loader 代码用于解释历史链路,不应直接复制到新项目。当前 webpack-dev-server 4+ 默认启用 HMR,严格 ESM 可使用 import.meta.webpackHot;生产环境不得携带 HMR runtime。以 webpack HMR 官方指南 和项目锁定版本为准。
webpack是一个打包模块化JavaScript的工具,在webpack里一切文件皆模块,通过Loader转换文件,通过Plugin注入钩子,最后输出由多个模块组合成的文件。webpack专注于构建模块化项目。
# 简单版打包模型步骤
我们先从简单的入手看,当 webpack 的配置只有一个出口时,不考虑分包的情况,其实我们只得到了一个bundle.js的文件,这个文件里包含了我们所有用到的js模块,可以直接被加载执行。那么,我可以分析一下它的打包思路,大概有以下4步:
- 利用
babel完成代码转换及解析,并生成单个文件的依赖模块Map - 从入口开始递归分析,并生成整个项目的依赖图谱
- 将各个引用模块打包为一个立即执行函数
- 将最终的
bundle文件写入bundle.js中
# 单个文件的依赖模块Map
- 我们会可以使用这几个包:
@babel/parser:负责将代码解析为抽象语法树@babel/traverse:遍历抽象语法树的工具,我们可以在语法树中解析特定的节点,然后做一些操作,如ImportDeclaration获取通过import引入的模块,FunctionDeclaration获取函数@babel/core:代码转换,如ES6的代码转为ES5的模式
由这几个模块的作用,其实已经可以推断出应该怎样获取单个文件的依赖模块了,转为
Ast->遍历Ast->调用ImportDeclaration。代码如下
// exportDependencies.js
const fs = require('fs')
const path = require('path')
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
const babel = require('@babel/core')
const exportDependencies = (filename)=>{
const content = fs.readFileSync(filename,'utf-8')
// 转为Ast
const ast = parser.parse(content, {
sourceType : 'module'//babel官方规定必须加这个参数,不然无法识别ES Module
})
const dependencies = {}
//遍历AST抽象语法树
traverse(ast, {
//调用ImportDeclaration获取通过import引入的模块
ImportDeclaration({node}){
const dirname = path.dirname(filename)
const newFile = './' + path.join(dirname, node.source.value)
//保存所依赖的模块
dependencies[node.source.value] = newFile
}
})
//通过@babel/core和@babel/preset-env进行代码的转换
const {code} = babel.transformFromAst(ast, null, {
presets: ["@babel/preset-env"]
})
return{
filename,//该文件名
dependencies,//该文件所依赖的模块集合(键值对存储)
code//转换后的代码
}
}
module.exports = exportDependencies