-
首先我们要了解webpack的构建流程 webpack的运行流程是一个串行的过程,从启动到结束会依次执行以下流程
1: 初始化参数:从配置文件和shell语句中读取和合并参数,并得到最终的参数
2: 开始编译:拿到上面的参数生成一个Compiler对象,先加载所有的配置插件,然后执 行run方法,最后开始编译
3: 确定入口:根据配置的entry找到所有的入口文件
4: 编译模块:从入口文件出现,调用所有配置的loader对模块进行编译,然后找到模块所依赖的模块,在递归出本步骤直到所有入口依赖文件都经过编译
5: 完成模块的编译:根据第四步的步骤使用loader编译完所有模块后,得到每个模块被编译后的最终内容和所有的依赖关系
6: 输出资源:根据入口和模块的依赖关系,组装成一个个包含多个模块的Chunk,再把每个Chunk转换成单独的文件添加到输出列表当中,这不是可以修改输出内容的最后机会
7: 输出完成:确定好输出内容过后,根据所配置的输出路径和输出文件名,将文件内容输出到文件系统当中
- webpack的基本配置
1: entry: 入口文件
2: output: 输出文件
3: Module: 依赖模块
4: Chunk: 有多个模块组成的文件,用于代码的合并和分割
5: loader: loader去处理那些非js文件的,loader可以将所有类型的文件转换为 webpack 能够处理的有效模块
6:Plugin:loader被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务,插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。 - 手写一个小型webpack
class Compiler {
constructor(options) {
const { entry, output } = options
this.entry = entry
this.output = output
this.modules = []
}
run() {
// 解析入口文件
const info = this.build(this.entry)
this.modules.push(info)
this.modules.forEach(({ dependecies }) => {
if (dependecies) {
for (const dependency in dependecies) {
this.modules.push(this.build(dependecies[dependency]))
}
}
});
// 生成依赖关系图
const dependencyGraph = this.modules.reduce(
(graph, item) => ({
...graph,
[item.filename]: {
dependecies: item.dependecies,
code: item.code
}
}),
{}
),
}
build(filename) {
// const
const ast = parser.getAST(this.entry)
const dependecies = parser.getDependecies(ast, this.entry)
const code = parser.getCode(ast)
return {
filename,
dependecies,
code
}
}
generate() {
const filePath = path.join(this.output.path, this.output.filename)
const bundle = `(function(graph){
function require(module){
function localRequire(relativePath){
return require(graph[module].dependecies[relativePath])
}
var exports={}
(function(require,exports,code){
eval(code)
})(localRequire,exports,graph[module].code);
return exports
}
require("${this.entry}")
}
)(${JSON.stringify(code)})`
fs.writeFileSync(filePath, bundle, "utf-8")
}
}
new Compiler(options).run()
const path = require("path")
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "./dist"),
filename: "main.js"
}
}
const fs = require("fs")
const parser = require("@babel/parser") //将入口的文件转换成AST语法树
const options = require("./webpack.config")
// const { parse } = require("path/posix")
const traverse = require("@babel/traverse").default
// const { parse } = require("path/posix")
const { transformFromAst } = require("@babel/core")
const parser = {
getAST: (path) => {
// 读取入口文件
const content = fs.readFileSync(path, "utf-8")
return parser.parse(content, {
sourceType: "module"
})
},
getDependecies: (ast, filename) => {
const dependecies = {}
traverse(ast, {
//类型为ImportDeclaration的AST节点
ImportDeclaration({ node }) {
const dirname = path.dirname(filename)
const filepath = "./" + path.join(dirname, node.source.value)
dependecies[node.source.value] = filepath
}
})
return dependecies
},
getCode: (ast) => {
const { code } = transformFromAst(ast, null, {
presets: ["@babel/preset-env"]
})
return code
}
}