webpack打包的过程 : 从入口文件开始分析模块依赖,然后深度遍历生成依赖图谱,再然后根据依赖图谱生成抽象语法树AST,最后分析AST拼接js代码输出,这个时候输出的代码就可以在浏览器直接执行了。
在上面文件目录中,index.js引入了message.js ,在message.js中引入了word.js ,我们把index.js作为入口文件打包这三个文件
新建src同级目录文件bundler.js,
首先要分析index.js的页面结构,返回文件名,依赖,可执行代码
const moduleAnalyser = (filename) => { //利用node fs模块读取文件内容 const content = fs.readFileSync(filename, 'utf-8'); //使用@babel/parser 分析文件内容生成AST const ast = parser.parse(content, { sourceType: 'module' }); // console.log(ast); //使用 @babel/traverse 分析AST const dependencies = {}; traverse(ast, { ImportDeclaration({ node }) { //利用 node path 拼接出依赖的完整路径 const dirname = path.dirname(filename) const newFile = path.join(dirname, node.source.value) dependencies[node.source.value] = newFile } }) // console.log(dependencies); //利用@babel/core 解析import语句 ,并且 @babel/preset-env把es6语法转为浏览器可执行的es5 const { code } = babel.transformFromAst(ast, null, { presets: ['@babel/preset-env'] }) return { filename, dependencies, code } }
遍历生成依赖图谱
//遍历生成依赖图谱 const makeDependenciesGraph = (entry) => { const entryModule = moduleAnalyser(entry); const graphArray = [entryModule]; for (let i = 0; i < graphArray.length; i++) { const item = graphArray[i]; const { dependencies } = item; if (dependencies) { for (let j in dependencies) { graphArray.push(moduleAnalyser(dependencies[j])) } } } const graph = {}; graphArray.forEach((item) => { graph[item.filename] = { dependencies: item.dependencies,//{ './word.js': 'src\\word.js' } code: item.code } }) console.log(graphArray,'graph'); return graph; }
生成浏览器可执行代码
const generateCode = (entry) => { const graph = JSON.stringify(makeDependenciesGraph(entry)); //要先把对象转换为字符串,不然在下面的模板字符串中会默认调取对象的toString方法,参数变成[Object object] console.log(graph); //定义require exports 以便浏览器执行 return ` (function(graph){ //第一层的require function require(module){ function localRequire(relativePath){ console.log(relativePath,module) //这里执行第一层的require方法 return require(graph[module].dependencies[relativePath]) } var exports={}; (function (require,exports,code) { eval(code); //执行eval(code)时,code里面还有依赖就继续执行require, //不过这个时候需要把相对路径转成绝对路径,就执行作为require参数名传进来的localRequire,完成路径转换后继续执行第一层的require })(localRequire,exports,graph[module].code); return exports; } require('${entry}') })(${graph}); `; }
}
最后执行 generateCode(‘./src/index.js’); 就可以拿到最后打包的代码了
git https://github.com/dz333333/easy_webpack.git
参考 : https://juejin.im/post/5d00fbbb5188255e780b64b1#heading-3