记录一下学习webpack原理的过程
1.1 打包的主要流程如下
- 需要读到入口文件里面的内容。
- 分析入口文件,递归的去读取模块所依赖的文件内容,生成AST语法树。
- 根据AST语法树,生成浏览器能够运行的代码
1.2 具体细节
- 获取主模块内容
- 分析模块
- 安装@babel/parser包(转AST)
- 对模块内容进行处理
- 安装@babel/traverse包(遍历AST收集依赖)
- 安装@babel/core和@babel/preset-env包 (es6转ES5)
- 递归所有模块
- 生成最终代码
- 项目目录如下:
- 在此目录的基础下,在根目录创建boudle.js 代码如下:
// 获取主入口文件 const fs = require('fs') // 引入生成ast 关键模块 const parser = require('@babel/parser') const path = require('path') // 引入遍历ast 得到的import路径 将其放入deps中(将用到的依赖收集起来) const traverse = require('@babel/traverse').default // ES6的AST转化成ES5 const babel = require('@babel/core') // 分析模块的方法 const getModuleInfo = (file)=>{ const body = fs.readFileSync(file,'utf-8') //分析模块 生成AST const ast = parser.parse(body, { sourceType: 'module' // 这个类型表示我们要解析ES模块 }) // 收集依赖 const deps = {} traverse(ast,{ ImportDeclaration({node}){ const dirname = path.dirname(file) const abspath = './' + path.join(dirname,node.source.value) deps[node.source.value] = abspath } }) // 将ES6的AST转化成ES5 const { code } = babel.transformFromAst(ast, null, { presets: ['@babel/preset-env'] }) // 将获取到的file deps(依赖) ES5 导出 const moduleInfo = {file,deps,code} return moduleInfo } // 递归获取依赖方法 const parseModules = (file) => { const entry = getModuleInfo(file) const temp = [entry] // 将文件路径为key {code, deps}为值存入对象 const depsGraph = {} for( let i = 0; i<temp.length; i++) { const deps = temp[i].deps if(deps) { for(const key in deps) { if (deps.hasOwnProperty(key)) { temp.push(getModuleInfo(deps[key])) } } } } // 将文件路径为key {code, deps}为值存入对象 temp.forEach(item => { depsGraph[item.file] = { deps: item.deps, code: item.code } }) return depsGraph } // 整合完整的字符串代码 const bundle = (file) =>{ const depsGraph = JSON.stringify(parseModules(file)) return `(function (graph) { function require(file) { function absRequire(relPath) { return require(graph[file].deps[relPath]) } var exports = {}; (function (require,exports,code) { eval(code) })(absRequire,exports,graph[file].code) return exports } require('${file}') })(${depsGraph})` } const content = bundle('./src/index.js') fs.mkdirSync('./dist'); fs.writeFileSync('./dist/bundle.js',content)