webpack是静态资源模块化打包工具,通过分析模块间依赖关系,打包出一个或者多个bundle文件
问题思考什么是模块化,js中模块化包含哪些东西?
webpack是如何分析模块间的依赖关系,最终打包出来?
打包中bundle和chunk的区别是什么?
打包过程中的loader和plugin分别有什么作用?
前置知识ES6,CommonJS,AMD,CMD等一一些模块化设计规范,有什么异同点
AST语法树
设计步骤找到入口文件
解析入口文件,提取依赖
递归的创建一个文件间的依赖图,描述所有文件间的依赖关系
把所有的文件打包成一个文件
场景假设(手动实现一个webpack)场景1
入口文件index.js
依赖文件name.js,person.js
目录结构如下
场景关系依赖分析index.js(入口)->person.js(依赖)->name.js(依赖)
按照设计步骤,手动实现myWebpack,文件名为myWebpack.js和src同目录步骤1:找到入口文件
constfs=require("fs");//封装读取文件内容方法functionreadContent(filename){constfileContent=fs.readFileSync(filename,"utf-8");returnfileContent;}//1.找到入口文件constcontent=readContent("./src/index.js");步骤2:这里需要有一些ast语法树的知识,通俗一点讲就是源码的另一种表现形式,通过对象的形式去描述内容,能够清晰的表现文件内容,依赖关系在线ast转换生成器
观察发现其实就是各种属性表示各种js代码(此过程有词法分析,语法分析啥的)
//2.解析入口文件内容,生成AST语法树(这里通过插件@babel/parser先转义成ast语法树)constastTree=require("@babel/parser").parse(content,{//parseinstrictmodeandallowmoduledeclarationssourceType:"module",plugins:[//enablejsxandflowsyntax"jsx","flow",],});console.log("astTree",astTree);3.步骤3:深度遍历AST语法树,获取entry.js依赖
//3.babel-traverse作用是像遍历对象一样对AST进行遍历转译,得到新的AST(通过astTree中的astTree->programNode->bodyNode->ImportDeclarationNode->source->value)constdependencies=[];constdeepTraverseAstTree=require("babel-traverse").default(astTree,{//需要遍历语法树中的属性ImportDeclaration:({node})=>{dependencies.push(node.source.value);console.log("node",node);},});4.步骤4:封装获取所有文件依赖方法,解析入口文件提取依赖
letid=0;functioncreateAsset(filename){constcontent=readContent(filename);constastTree=babylon.parse(content,{sourceType:"module",});constdependencies=[];babelTraverse(astTree,{//需要遍历语法树中的属性ImportDeclaration:({node})=>{dependencies.push(node.source.value);},});return{id:id++,filename,dependencies,};}constmainAsset=createAsset("./src/index.js");console.log("mainAsset",mainAsset);步骤5:递归的创建一个文件间的依赖图,需要一个map表示路径和资源的依赖关系
//递归的创建一个文件间的依赖图,需要一个map表示路径和资源的依赖关系functioncreateGraph(entry){constmainAsset=createAsset(entry);//遍历所有的资源文件constallAssets=[mainAsset];for(constassetofallAssets){constdirname=path.dirname(asset.filename);asset.map={};asset.dependencies.forEach(relativePath=>{//转换成绝对路径constabsolutePath=path.join(dirname,relativePath);constchildAsset=createAsset(absolutePath);asset.map[relativePath]=childAsset.id;allAssets.push(childAsset);});}returnallAssets;}constgraph=createGraph("./src/index.js");console.log("graph",graph);6.步骤6:创建整体的结果代码块,需要接收参数且立即执行,所以定义一个自执行函数包裹,遍历graph拿到所有的module,以上createAsset方法只获取到了id,filename,dependencies这三个属性只能表示模块间的依赖关系,并未拿到真正的code,所以需要安装插件,通过babel将ast转换成code
//编译所有代码,获取模块内容,并返回codeconst{code}=babel.transformFromAst(astTree,null,{presets:["@babel/preset-env"],});7.步骤7:经babel编译打包后数据可以分析得出模块需要
function(require,module,exports){${module.code}}步骤8:封装require函数
functionrequire(id){const[fn,map]=modules[id];functionlocalRequire(relativePath){returnrequire(map[relativePath]);}constmodule={exports:{}}fn(localRequire,module,module.exports)returnmodule.exports;}步骤9:编译源代码,把编译后的代码加入result中
functionbundle(graph){letmodules="";//遍历依赖关系图,拿到将每个模块代码,依赖关系进行组装graph.forEach(module=>{modules+=`${module.id}:[function(require,module,exports){${module.code}},${JSON.stringify(module.map)},],`;});//实现require方法,自执行函数,从入口开始引入执行,然后fn(localRequire,module,module.exports)加载执行每个模块diamanteconstresult=`(function(modules){functionrequire(id){const[fn,map]=modules[id];functionlocalRequire(relativePath){returnrequire(map[relativePath]);}constmodule={exports:{}}fn(localRequire,module,module.exports)returnmodule.exports;}require(0)})({${modules}})`;returnresult;}总结什么是模块化,js中模块化包含哪些东西?模块化,就像积木一样,相互独立,有自己的作用范围,通过向外界定义一些接口和方法和外界交互,这样就遵循了开闭原则,单一职责原则可解耦,提高开发效率,提高复用率前端模块化,现在主要是ES6,和CommonJs规范webpack如何分析模块间的依赖关系?
通过插件babel/parser将代码转成ast语法树
通过插件babel/traverse遍历ast语法树拿到每个模块所有依赖
递归并遍历依赖模块,建立文件间的依赖图打包中bundle和chunk的区别是什么?
chunk是webpack打包过程中modules的集合,是打包过程中的概念
bundle是我们最终打包好的一个或者多个文件
大多数情况下,chunk和bundle是一一对应的,但是也有例外,如果加了source-map,一个entry,一个chunk也会产生两个bundle
打包过程中的loader和plugin分别有什么作用?
loader是文件处理器,将各种非js文件,处理成webpack可识别的js和json文件本质上是将所有类型的文件,转换成引用程序的依赖图,可以直接引用的模块
拓展插件,是运行在webpack打包的各个阶段,都会广播自己的事件,然后插件去监听对应的事件,然后去处理一些事情项目地址
原文:https://juejin.cn/post/7099846010456768525logo设计
创造品牌价值
¥500元起
APP开发
量身定制,源码交付
¥2000元起
商标注册
一个好品牌从商标开始
¥1480元起
公司注册
注册公司全程代办
¥0元起
查
看
更
多