Webpack原理解析?

如题所述

第1个回答  2024-09-04
Webpack是什么?

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/7099846010456768525

logo设计

创造品牌价值

¥500元起

APP开发

量身定制,源码交付

¥2000元起

商标注册

一个好品牌从商标开始

¥1480元起

公司注册

注册公司全程代办

¥0元起

    官方电话官方服务
      官方网站八戒财税知识产权八戒服务商企业需求数字市场
相似回答
大家正在搜