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

          90行代碼實(shí)現(xiàn)模塊打包器

          共 2191字,需瀏覽 5分鐘

           ·

          2021-09-10 14:40

          作者:卡頌

          來源:SegmentFault 思否社區(qū)


          大家好,我卡頌。

          今天來聊聊如何用90行代碼實(shí)現(xiàn)一個(gè)現(xiàn)代JS模塊打包器。

          我們的打包器雖然迷你,但是實(shí)現(xiàn)了webpack的核心功能。

          而且,我知道你看到大段代碼頭疼,所以這篇文章都是圖。看完感興趣的話,

          這里是完整代碼的倉庫地址:GitHub - BetaSu/minipack: ?? 現(xiàn)代JS模塊打包器的簡易實(shí)現(xiàn)、詳細(xì)教學(xué)

          只有90行代碼哦。

          讓我們愉快的開始吧。

          生成依賴圖

          如果應(yīng)用是個(gè)毛線團(tuán)的話,那么入口文件就是線頭。打包器要做的第一件事是:

          順著線頭開始濾清整條線的走向

          假設(shè)入口文件是entry.js

          // entry.js

          import a from './a.js';
          import b from './b.js';

          console.log(a, b);


          他依賴了a.jsb.js


          em... 有點(diǎn)太簡陋了,讓我們再擴(kuò)展下a.jsb.js

          // a.js
          import c from './c.js';

          // ...


          // b.js
          import d from './d.js';
          import e from './e.js';

          // ...


          所以整個(gè)依賴關(guān)系是這樣:


          打包器會(huì)從入口文件開始,嘗試建立模塊(即js文件)間的依賴關(guān)系,也就是剛才我們講的順著線頭開始濾清整條線的走向。

          模塊間的依賴關(guān)系可以通過分析模塊代碼中的import 聲明語句得知。

          為了能分析import 聲明語句,可以使用babel等編譯工具將模塊代碼分解為AST(抽象語法樹)。

          遍歷AST,類型為ImportDeclaration的節(jié)點(diǎn)就是import聲明語句。

          最后,我們將AST重新轉(zhuǎn)換為可執(zhí)行的目標(biāo)代碼,可能還需要根據(jù)代碼要執(zhí)行的宿主環(huán)境(一般為瀏覽器)對代碼做一些轉(zhuǎn)換。

          比如,瀏覽器不支持import './a.js'這樣的ESM語法,那么我們需要將所有ESM語法轉(zhuǎn)為CJS語法。

          // 源代碼
          import './a.js';

          // 轉(zhuǎn)換后
          require('./a.js');


          所以,對于任一模塊(js文件),會(huì)經(jīng)歷:


          右邊包含目標(biāo)代碼模塊間依賴關(guān)系的數(shù)據(jù)結(jié)構(gòu)被稱為asset

          每個(gè)asset可以通過模塊間依賴關(guān)系找到依賴的模塊,重復(fù)這一過程,生成新的asset,最終形成整個(gè)應(yīng)用所有asset間的依賴關(guān)系:


          應(yīng)用完整的依賴關(guān)系被稱為依賴圖(dependency graph)。

          打包代碼

          接下來,只需要遍歷依賴圖,將所有asset目標(biāo)代碼打包在一起就行。

          所有代碼會(huì)被打包在一個(gè)立即執(zhí)行函數(shù)中:

          (function(modules) {
            // 打包好的代碼
          })(modules)


          modules中保存了所有asset及他們之間的依賴關(guān)系。

          如果你對modules的細(xì)節(jié)感興趣,可以去文末倉庫里翻代碼

          剛才說過,asset目標(biāo)代碼CJS規(guī)范的,類似:

          // entry.js

          require('./a.js');
          require('./b.js');


          這意味著我們需要實(shí)現(xiàn):

          • require方法(用于引入依賴的其他asset的目標(biāo)代碼)

          • module對象(用于保存當(dāng)前asset目標(biāo)代碼執(zhí)行后導(dǎo)出的數(shù)據(jù))


          同時(shí),為了防止不同asset目標(biāo)代碼中的變量互相污染,每個(gè)目標(biāo)代碼需要獨(dú)立的作用域。

          我們將目標(biāo)代碼包裹在函數(shù)中:

          // 我們操作的是字符串模版
          `function (require, module, exports) {
            ${asset.code}
          }`


          所以,最終打包的結(jié)果為:

          (function(modules) {
            function require() {// ...}
            
            require(入口asset的ID)
          })(modules)


          這段字符串被包裹在瀏覽器<script>標(biāo)簽內(nèi),會(huì)依次執(zhí)行:

          1. require(入口assetID),執(zhí)行入口asset目標(biāo)代碼

          2. 目標(biāo)代碼內(nèi)部會(huì)調(diào)用require執(zhí)行其他asset目標(biāo)代碼

          3. 一步步執(zhí)行下去...

          總結(jié)

          打包器的工作原理分為兩步:

          1. 從入口文件開始遍歷,生成依賴圖

          2. 根據(jù)依賴圖,將代碼打包進(jìn)一個(gè)立即執(zhí)行函數(shù)


          這個(gè)打包器還很稚嫩,缺失很多必要的功能,比如:

          • 解決循環(huán)依賴

          • 緩存

          但是瑕不掩瑜嘛~



          點(diǎn)擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開更多互動(dòng)和交流,掃描下方”二維碼“或在“公眾號(hào)后臺(tái)回復(fù)“ 入群 ”即可加入我們的技術(shù)交流群,收獲更多的技術(shù)文章~


          - END -



          瀏覽 53
          點(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>
                  五月丁香色婷 | 一级淫片A片 | PORNY九色视频9l自拍 | 美女操国产 | 亚洲免费观看高清在线观看 |