<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          手寫webpack核心原理,再也不怕面試官問我webpack原理

          共 12847字,需瀏覽 26分鐘

           ·

          2020-08-20 04:59

          一、核心打包原理

          1.1 打包的主要流程如下

          1. 需要讀到入口文件里面的內(nèi)容。
          2. 分析入口文件,遞歸的去讀取模塊所依賴的文件內(nèi)容,生成AST語法樹。
          3. 根據(jù)AST語法樹,生成瀏覽器能夠運(yùn)行的代碼

          1.2 具體細(xì)節(jié)

          1. 獲取主模塊內(nèi)容
          2. 分析模塊
            • 安裝@babel/parser包(轉(zhuǎn)AST)
          3. 對模塊內(nèi)容進(jìn)行處理
            • 安裝@babel/traverse包(遍歷AST收集依賴)
            • 安裝@babel/core和@babel/preset-env包 (es6轉(zhuǎn)ES5)
          4. 遞歸所有模塊
          5. 生成最終代碼

          二、基本準(zhǔn)備工作

          我們先建一個(gè)項(xiàng)目

          項(xiàng)目目錄暫時(shí)如下:

          ?

          已經(jīng)把項(xiàng)目放到 「github」:https://github.com/Sunny-lucking/howToBuildMyWebpack。可以卑微地要個(gè)star嗎

          ?

          我們創(chuàng)建了add.js文件和minus.js文件,然后 在index.js中引入,再將index.js文件引入index.html。

          代碼如下:

          add.js

          export?default?(a,b)=>{
          ??return?a+b;
          }

          minus.js

          export?const?minus?=?(a,b)=>{
          ????return?a-b
          }

          index.js

          import?add?from?"./add"
          import?{minus}?from?"./minus";

          const?sum?=?add(1,2);
          const?division?=?minus(2,1);

          console.log(sum);
          console.log(division);

          index.html


          "en">

          ????"UTF-8">
          ????Title





          現(xiàn)在我們打開index.html。你猜會(huì)發(fā)生什么???顯然會(huì)報(bào)錯(cuò),因?yàn)闉g覽器還不能識別import語法

          不過沒關(guān)系,因?yàn)槲覀儽緛砭褪且獊斫鉀Q這些問題的。

          三、獲取模塊內(nèi)容

          好了,現(xiàn)在我們開始根據(jù)上面核心打包原理的思路來實(shí)踐一下,第一步就是 實(shí)現(xiàn)獲取模塊內(nèi)容。

          我們來創(chuàng)建一個(gè)bundle.js文件。

          //?獲取主入口文件
          const?fs?=?require('fs')
          const?getModuleInfo?=?(file)=>{
          ????const?body?=?fs.readFileSync(file,'utf-8')
          ????console.log(body);
          }
          getModuleInfo("./src/index.js")

          目前項(xiàng)目目錄如下

          我們來執(zhí)行一下bundle.js,看看時(shí)候成功獲得入口文件內(nèi)容

          哇塞,不出所料的成功。一切盡在掌握之中。好了,已經(jīng)實(shí)現(xiàn)第一步了,且讓我看看第二步是要干嘛。

          哦?是分析模塊了

          四、分析模塊

          分析模塊的主要任務(wù)是 將獲取到的模塊內(nèi)容 解析成AST語法樹,這個(gè)需要用到一個(gè)依賴包@babel/parser

          npm?install?@babel/parser

          ok,安裝完成我們將@babel/parser引入bundle.js,

          //?獲取主入口文件
          const?fs?=?require('fs')
          const?parser?=?require('@babel/parser')
          const?getModuleInfo?=?(file)=>{
          ????const?body?=?fs.readFileSync(file,'utf-8')
          ????const?ast?=?parser.parse(body,{
          ????????sourceType:'module'?//表示我們要解析的是ES模塊
          ????});
          ????console.log(ast);
          }
          getModuleInfo("./src/index.js")

          我們?nèi)タ聪翤babel/parser的文檔:

          可見提供了三個(gè)API,而我們目前用到的是parse這個(gè)API。

          它的主要作用是 parses the provided code as an entire ECMAScript program,也就是將我們提供的代碼解析成完整的ECMAScript代碼。

          再看看該API提供的參數(shù)

          我們暫時(shí)用到的是sourceType,也就是用來指明我們要解析的代碼是什么模塊。

          好了,現(xiàn)在我們來執(zhí)行一下 bundle.js,看看AST是否成功生成。

          成功。又是不出所料的成功。

          不過,我們需要知道的是,當(dāng)前我們解析出來的不單單是index.js文件里的內(nèi)容,它也包括了文件的其他信息。而它的內(nèi)容其實(shí)是它的屬性program里的body里。如圖所示

          我們可以改成打印ast.program.body看看

          //?獲取主入口文件
          const?fs?=?require('fs')
          const?parser?=?require('@babel/parser')
          const?getModuleInfo?=?(file)=>{
          ????const?body?=?fs.readFileSync(file,'utf-8')
          ????const?ast?=?parser.parse(body,{
          ????????sourceType:'module'?//表示我們要解析的是ES模塊
          ????});
          ????console.log(ast.program.body);
          }
          getModuleInfo("./src/index.js"

          執(zhí)行

          看,現(xiàn)在打印出來的就是 index.js文件里的內(nèi)容(也就是我們在index.js里寫的代碼啦).

          五、收集依賴

          現(xiàn)在我們需要 遍歷AST,將用到的依賴收集起來。什么意思呢?其實(shí)就是將用import語句引入的文件路徑收集起來。我們將收集起來的路徑放到deps里。

          前面我們提到過,遍歷AST要用到@babel/traverse依賴包

          npm?install?@babel/traverse

          現(xiàn)在,我們引入。

          const?fs?=?require('fs')
          const?path?=?require('path')
          const?parser?=?require('@babel/parser')
          const?traverse?=?require('@babel/traverse').default
          const?getModuleInfo?=?(file)=>{
          ????const?body?=?fs.readFileSync(file,'utf-8')
          ????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
          ????????}
          ????})
          ????console.log(deps);


          }
          getModuleInfo("./src/index.js")

          我們來看下官方文檔對@babel/traverse的描述

          好吧,如此簡略

          不過我們不難看出,第一個(gè)參數(shù)就是AST。第二個(gè)參數(shù)就是配置對象

          我們看看我們寫的代碼

          traverse(ast,{
          ????ImportDeclaration({node}){
          ????????const?dirname?=?path.dirname(file)
          ????????const?abspath?=?'./'?+?path.join(dirname,node.source.value)
          ????????deps[node.source.value]?=?abspath
          ????}
          })

          配置對象里,我們配置了ImportDeclaration方法,這是什么意思呢?我們看看之前打印出來的AST。

          ImportDeclaration 方法代表的是對type類型為ImportDeclaration的節(jié)點(diǎn)的處理。

          這里我們獲得了該節(jié)點(diǎn)中source的value,也就是node.source.value,

          這里的value指的是什么意思呢?其實(shí)就是import的值,可以看我們的index.js的代碼。

          import?add?from?"./add"
          import?{minus}?from?"./minus";

          const?sum?=?add(1,2);
          const?division?=?minus(2,1);

          console.log(sum);
          console.log(division);

          可見,value指的就是import后面的 './add' 和 './minus'

          然后我們將file目錄路徑跟獲得的value值拼接起來保存到deps里,美其名曰:收集依賴。

          ok,這個(gè)操作就結(jié)束了,執(zhí)行看看收集成功了沒?

          oh my god。又成功了。

          六、ES6轉(zhuǎn)成ES5(AST)

          現(xiàn)在我們需要把獲得的ES6的AST轉(zhuǎn)化成ES5的AST,前面講到過,執(zhí)行這一步需要兩個(gè)依賴包

          npm?install?@babel/core?@babel/preset-env

          我們現(xiàn)在將依賴引入并使用

          const?fs?=?require('fs')
          const?path?=?require('path')
          const?parser?=?require('@babel/parser')
          const?traverse?=?require('@babel/traverse').default
          const?babel?=?require('@babel/core')
          const?getModuleInfo?=?(file)=>{
          ????const?body?=?fs.readFileSync(file,'utf-8')
          ????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
          ????????}
          ????})
          ????
          ????新增代碼
          ????const?{code}?=?babel.transformFromAst(ast,null,{
          ????????presets:["@babel/preset-env"]
          ????})
          ????console.log(code);

          }
          getModuleInfo("./src/index.js")

          我們看看官網(wǎng)文檔對@babel/core 的transformFromAst的介紹

          害,又是一如既往的簡略。。。

          簡單說一下,其實(shí)就是將我們傳入的AST轉(zhuǎn)化成我們在第三個(gè)參數(shù)里配置的模塊類型。

          好了,現(xiàn)在我們來執(zhí)行一下,看看結(jié)果

          我的天,一如既往的成功。可見 它將我們寫const 轉(zhuǎn)化成var了。

          好了,這一步到此結(jié)束,咦,你可能會(huì)有疑問,上一步的收集依賴在這里怎么沒啥關(guān)系啊,確實(shí)如此。收集依賴是為了下面進(jìn)行的遞歸操作。

          七、遞歸獲取所有依賴

          經(jīng)過上面的過程,現(xiàn)在我們知道getModuleInfo是用來獲取一個(gè)模塊的內(nèi)容,不過我們還沒把獲取的內(nèi)容return出來,因此,更改下getModuleInfo方法

          const?getModuleInfo?=?(file)=>{
          ????const?body?=?fs.readFileSync(file,'utf-8')
          ????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
          ????????}
          ????})
          ????const?{code}?=?babel.transformFromAst(ast,null,{
          ????????presets:["@babel/preset-env"]
          ????})
          ????//?新增代碼
          ????const?moduleInfo?=?{file,deps,code}
          ????return?moduleInfo
          }

          我們返回了一個(gè)對象 ,這個(gè)對象包括「該模塊的路徑(file)」「該模塊的依賴(deps)」「該模塊轉(zhuǎn)化成es5的代碼」

          該方法只能獲取一個(gè)模塊的的信息,但是我們要怎么獲取一個(gè)模塊里面的依賴模塊的信息呢?

          沒錯(cuò),看標(biāo)題,,你應(yīng)該想到了就算遞歸。

          現(xiàn)在我們來寫一個(gè)遞歸方法,遞歸獲取依賴

          const?parseModules?=?(file)?=>{
          ????const?entry?=??getModuleInfo(file)
          ????const?temp?=?[entry]
          ????for?(let?i?=?0;i????????const?deps?=?temp[i].deps
          ????????if?(deps){
          ????????????for?(const?key?in?deps){
          ????????????????if?(deps.hasOwnProperty(key)){
          ????????????????????temp.push(getModuleInfo(deps[key]))
          ????????????????}
          ????????????}
          ????????}
          ????}
          ????console.log(temp)
          }

          講解下parseModules方法:

          1. 我們首先傳入主模塊路徑
          2. 將獲得的模塊信息放到temp數(shù)組里。
          3. 外面的循環(huán)遍歷temp數(shù)組,此時(shí)的temp數(shù)組只有主模塊
          4. 里面再獲得主模塊的依賴deps
          5. 遍歷deps,通過調(diào)用getModuleInfo將獲得的依賴模塊信息push到temp數(shù)組里。

          目前bundle.js文件:

          const?fs?=?require('fs')
          const?path?=?require('path')
          const?parser?=?require('@babel/parser')
          const?traverse?=?require('@babel/traverse').default
          const?babel?=?require('@babel/core')
          const?getModuleInfo?=?(file)=>{
          ????const?body?=?fs.readFileSync(file,'utf-8')
          ????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
          ????????}
          ????})
          ????const?{code}?=?babel.transformFromAst(ast,null,{
          ????????presets:["@babel/preset-env"]
          ????})
          ????const?moduleInfo?=?{file,deps,code}
          ????return?moduleInfo
          }

          //?新增代碼
          const?parseModules?=?(file)?=>{
          ????const?entry?=??getModuleInfo(file)
          ????const?temp?=?[entry]
          ????for?(let?i?=?0;i????????const?deps?=?temp[i].deps
          ????????if?(deps){
          ????????????for?(const?key?in?deps){
          ????????????????if?(deps.hasOwnProperty(key)){
          ????????????????????temp.push(getModuleInfo(deps[key]))
          ????????????????}
          ????????????}
          ????????}
          ????}
          ????console.log(temp)
          }
          parseModules("./src/index.js")

          按照目前我們的項(xiàng)目來說執(zhí)行完,應(yīng)當(dāng)是temp 應(yīng)當(dāng)是存放了index.js,add.js,minus.js三個(gè)模塊。,執(zhí)行看看。

          牛逼!!!確實(shí)如此。

          不過現(xiàn)在的temp數(shù)組里的對象格式不利于后面的操作,我們希望是以文件的路徑為key,{code,deps}為值的形式存儲(chǔ)。因此,我們創(chuàng)建一個(gè)新的對象depsGraph。

          const?parseModules?=?(file)?=>{
          ????const?entry?=??getModuleInfo(file)
          ????const?temp?=?[entry]?
          ????const?depsGraph?=?{}?//新增代碼
          ????for?(let?i?=?0;i????????const?deps?=?temp[i].deps
          ????????if?(deps){
          ????????????for?(const?key?in?deps){
          ????????????????if?(deps.hasOwnProperty(key)){
          ????????????????????temp.push(getModuleInfo(deps[key]))
          ????????????????}
          ????????????}
          ????????}
          ????}
          ????//?新增代碼
          ????temp.forEach(moduleInfo=>{
          ????????depsGraph[moduleInfo.file]?=?{
          ????????????deps:moduleInfo.deps,
          ????????????code:moduleInfo.code
          ????????}
          ????})
          ????console.log(depsGraph)
          ????return?depsGraph
          }

          ok,現(xiàn)在存儲(chǔ)的就是這種格式啦

          八、處理兩個(gè)關(guān)鍵字

          我們現(xiàn)在的目的就是要生成一個(gè)bundle.js文件,也就是打包后的一個(gè)文件。其實(shí)思路很簡單,就是把index.js的內(nèi)容和它的依賴模塊整合起來。然后把代碼寫到一個(gè)新建的js文件。

          我們把這段代碼格式化一下

          //?index.js
          "use?strict"
          var?_add?=?_interopRequireDefault(require("./add.js"));
          var?_minus?=?require("./minus.js");
          function?_interopRequireDefault(obj)?{?return?obj?&&?obj.__esModule???obj?:?{?"default":?obj?};?}
          var?sum?=?(0,?_add["default"])(1,?2);
          var?division?=?(0,?_minus.minus)(2,?1);
          console.log(sum);?console.log(division);
          //?add.js
          "use?strict";
          Object.defineProperty(exports,?"__esModule",?{??value:?true});
          exports["default"]?=?void?0;
          var?_default?=?function?_default(a,?b)?{??return?a?+?b;};
          exports["default"]?=?_default;

          但是我們現(xiàn)在是不能執(zhí)行index.js這段代碼的,因?yàn)闉g覽器不會(huì)識別執(zhí)行require和exports。

          不能識別是為什么?不就是因?yàn)闆]有定義這require函數(shù),和exports對象。那我們可以自己定義。

          我們創(chuàng)建一個(gè)函數(shù)

          const?bundle?=?(file)?=>{
          ????const?depsGraph?=?JSON.stringify(parseModules(file))
          ????
          }

          我們將上一步獲得的depsGraph保存起來。

          現(xiàn)在返回一個(gè)整合完整的字符串代碼。

          怎么返回呢?更改下bundle函數(shù)

          const?bundle?=?(file)?=>{
          ????const?depsGraph?=?JSON.stringify(parseModules(file))
          ????return?`(function?(graph)?{
          ????????????????function?require(file)?{
          ????????????????????(function?(code)?{
          ????????????????????????eval(code)
          ????????????????????})(graph[file].code)
          ????????????????}
          ????????????????require(file)
          ????????????})(depsGraph)`

          ????
          }

          我們看下返回的這段代碼

          ?(function?(graph)?{
          ????????function?require(file)?{
          ????????????(function?(code)?{
          ????????????????eval(code)
          ????????????})(graph[file].code)
          ????????}
          ????????require(file)
          ????})(depsGraph)

          其實(shí)就是

          1. 把保存下來的depsGraph,傳入一個(gè)立即執(zhí)行函數(shù)。
          2. 將主文件路徑傳入require函數(shù)執(zhí)行
          3. 執(zhí)行reuire函數(shù)的時(shí)候,又立即執(zhí)行一個(gè)立即執(zhí)行函數(shù),這里是把code的值傳進(jìn)去了
          4. 執(zhí)行eval(code)。也就是執(zhí)行code這段代碼

          我們再來看下code的值

          //?index.js
          "use?strict"
          var?_add?=?_interopRequireDefault(require("./add.js"));
          var?_minus?=?require("./minus.js");
          function?_interopRequireDefault(obj)?{?return?obj?&&?obj.__esModule???obj?:?{?"default":?obj?};?}
          var?sum?=?(0,?_add["default"])(1,?2);
          var?division?=?(0,?_minus.minus)(2,?1);
          console.log(sum);?console.log(division);

          沒錯(cuò)執(zhí)行這段代碼的時(shí)候,又會(huì)用到require函數(shù)。此時(shí)require的參數(shù)為add.js的路徑,哎,不是絕對路徑,需要轉(zhuǎn)化成絕對路徑。因此寫一個(gè)函數(shù)absRequire來轉(zhuǎn)化。怎么實(shí)現(xiàn)呢?我們來看下代碼

          (function?(graph)?{
          ????function?require(file)?{
          ????????function?absRequire(relPath)?{
          ????????????return?require(graph[file].deps[relPath])
          ????????}
          ????????(function?(require,code)?{
          ????????????eval(code)
          ????????})(absRequire,graph[file].code)
          ????}
          ????require(file)
          })(depsGraph)

          實(shí)際上是實(shí)現(xiàn)了一層攔截。

          1. 執(zhí)行require('./src/index.js')函數(shù)
          2. 執(zhí)行了
          (function?(require,code)?{
          ????eval(code)
          })(absRequire,graph[file].code)
          1. 執(zhí)行eval,也就是執(zhí)行了index.js的代碼。
          2. 執(zhí)行過程會(huì)執(zhí)行到require函數(shù)。
          3. 這時(shí)會(huì)調(diào)用這個(gè)require,也就是我們傳入的absRequire
          4. 而執(zhí)行absRequire就執(zhí)行了return require(graph[file].deps[relPath])這段代碼,也就是執(zhí)行了外面這個(gè)require

          在這里return require(graph[file].deps[relPath]),我們已經(jīng)對路徑轉(zhuǎn)化成絕對路徑了。因此執(zhí)行外面的require的時(shí)候就是傳入絕對路徑。

          1. 而執(zhí)行require("./src/add.js")之后,又會(huì)執(zhí)行eval,也就是執(zhí)行add.js文件的代碼。

          是不是有點(diǎn)繞?其實(shí)是個(gè)遞歸。

          這樣就將代碼整合起來了,但是有個(gè)問題,就是在執(zhí)行add.js的code時(shí)候,會(huì)遇到exports這個(gè)還沒定義的問題。如下所示

          //?add.js
          "use?strict";
          Object.defineProperty(exports,?"__esModule",?{??value:?true});
          exports["default"]?=?void?0;
          var?_default?=?function?_default(a,?b)?{??return?a?+?b;};
          exports["default"]?=?_default;

          我們發(fā)現(xiàn) 這里它把exports當(dāng)作一個(gè)對象來使用了,但是這個(gè)對象還沒定義,因此我們可以自己定義一個(gè)exports對象。

          (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)

          我們增添了一個(gè)空對象 exports,執(zhí)行add.js代碼的時(shí)候,會(huì)往這個(gè)空對象上增加一些屬性,

          //?add.js
          "use?strict";
          Object.defineProperty(exports,?"__esModule",?{??value:?true});
          exports["default"]?=?void?0;
          var?_default?=?function?_default(a,?b)?{??return?a?+?b;};
          exports["default"]?=?_default;

          比如,執(zhí)行完這段代碼后

          exports?=?{
          ??__esModule:{??value:?true},
          ??defaultfunction?_default(a,?b)?{??return?a?+?b;}
          }

          然后我們把exports對象return出去。

          var?_add?=?_interopRequireDefault(require("./add.js"));

          可見,return出去的值,被_interopRequireDefault接收,_interopRequireDefault再返回default這個(gè)屬性給_add,因此_add = function _default(a, b) { return a + b;}

          現(xiàn)在明白了,為什么ES6模塊 引入的是一個(gè)對象引用了吧,因?yàn)閑xports就是一個(gè)對象。

          至此,處理;兩個(gè)關(guān)鍵詞的功能就完整了。

          const?fs?=?require('fs')
          const?path?=?require('path')
          const?parser?=?require('@babel/parser')
          const?traverse?=?require('@babel/traverse').default
          const?babel?=?require('@babel/core')
          const?getModuleInfo?=?(file)=>{
          ????const?body?=?fs.readFileSync(file,'utf-8')
          ????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
          ????????}
          ????})
          ????const?{code}?=?babel.transformFromAst(ast,null,{
          ????????presets:["@babel/preset-env"]
          ????})
          ????const?moduleInfo?=?{file,deps,code}
          ????return?moduleInfo
          }
          const?parseModules?=?(file)?=>{
          ????const?entry?=??getModuleInfo(file)
          ????const?temp?=?[entry]
          ????const?depsGraph?=?{}
          ????for?(let?i?=?0;i????????const?deps?=?temp[i].deps
          ????????if?(deps){
          ????????????for?(const?key?in?deps){
          ????????????????if?(deps.hasOwnProperty(key)){
          ????????????????????temp.push(getModuleInfo(deps[key]))
          ????????????????}
          ????????????}
          ????????}
          ????}
          ????temp.forEach(moduleInfo=>{
          ????????depsGraph[moduleInfo.file]?=?{
          ????????????deps:moduleInfo.deps,
          ????????????code:moduleInfo.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')

          console.log(content);

          來執(zhí)行下,看看效果

          確實(shí)執(zhí)行成功。接下來,把返回的這段代碼寫入新創(chuàng)建的文件中

          //寫入到我們的dist目錄下
          fs.mkdirSync('./dist');
          fs.writeFileSync('./dist/bundle.js',content)

          至此,我們的手寫webpack核心原理就到此結(jié)束了。

          我們參觀下生成的bundle.js文件

          發(fā)現(xiàn)其實(shí)就是將我們早期收集的所有依賴作為參數(shù)傳入到立即執(zhí)行函數(shù)當(dāng)中,然后通過eval來遞歸地執(zhí)行每個(gè)依賴的code。

          現(xiàn)在我們將bundle.js文件引入index.html看看能不能執(zhí)行

          成功。。。。。驚喜。。

          感謝您也恭喜您看到這里,我可以卑微的求個(gè)star嗎!!!

          https://github.com/Sunny-lucking/howToBuildMyWebpack

          ?

          作者:陽光是sunny

          原文地址:https://juejin.im/post/6854573217336541192

          ?

          最后

          1.看到這里了就點(diǎn)個(gè)在看支持下吧,你的在看是我創(chuàng)作的動(dòng)力。

          2.關(guān)注公眾號程序員成長指北「帶你一起學(xué)Node」

          3.我是kaola?,可以添加我的微信【ikoala520】,拉你進(jìn)技術(shù)交流群一起學(xué)習(xí)。


          “在看轉(zhuǎn)發(fā)”是最大的支持

          瀏覽 46
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  看尻屄| 人人爽,人人操 | 中文字幕永久 | 青青草视频首页 | 四虎最新网站 |