【W(wǎng)ebpack】Webpack核心原理

這篇主要就講一下【打包】(bundle是打包拒啰,bundler是打包器)

現(xiàn)有問題

a.js.png

b.js.png

index.js.png

上面三個(gè)文件的代碼都不能直接運(yùn)行在瀏覽器中,因?yàn)闉g覽器不支持直接運(yùn)行帶有import和export關(guān)鍵字的代碼市埋,所以就引出了下面的問題

問題一:很多瀏覽器不認(rèn)識(shí)import和export,只有現(xiàn)代瀏覽器(chrome恕刘、firefox缤谎、edge等),通過<script type="module" >來支持import和export褐着;

問題二:雖然通過<script type="module" >可以支持import和export坷澡,但是不兼容IE8~15,而且可能會(huì)導(dǎo)致文件請(qǐng)求過多含蓉;

平穩(wěn)的兼容策略:把關(guān)鍵字轉(zhuǎn)譯成普通代碼频敛;且把所有文件打包成一個(gè)文件项郊;
下面就來講一下如何來實(shí)現(xiàn)上面這個(gè)策略

編譯import和export關(guān)鍵字

項(xiàng)目中新增了bundler_1.ts,可以拿它和deps_4.ts做一下比較斟赚,看哪里做了改動(dòng)着降;
主要添加了下面幾行代碼,通過babel把code轉(zhuǎn)譯一下:

 const { code: es5Code } = babel.transform(code, {
    presets: ['@babel/preset-env']
  })

運(yùn)行一下拗军,取其中的a.js的結(jié)果看一下:


image.png
image.png

可以看出區(qū)別:
①import關(guān)鍵字沒有了鹊碍,變成了require;
②export關(guān)鍵字也沒有了食绿,變成了exports['default']
注意:這時(shí)這里的code是字符串

把所有代碼打包成一個(gè)文件

那么這個(gè)文件應(yīng)該是什么樣的呢侈咕,首先它應(yīng)該包含所有的模塊,其次它還要可以執(zhí)行所有的模塊器紧;
這時(shí)就有三個(gè)問題我們要來解決一下:
1耀销、depRelation目前是對(duì)象,我們要把它變成數(shù)組(為什么變成數(shù)組呢铲汪,因?yàn)閿?shù)組的第一項(xiàng)就是入口呀熊尉,而對(duì)象沒有第一項(xiàng)這么一個(gè)概念);
2掌腰、code目前是字符串狰住,我們要把它變成函數(shù);
3齿梁、完善execude函數(shù)(execude函數(shù)就是用來執(zhí)行入口文件的)

把depRelation從對(duì)象變成數(shù)組

引入bundler_2.ts催植,把它與bundler_1.ts做一下對(duì)比;node -r ts-node/register ./bundler_2.ts運(yùn)行bundler_2.ts勺择;

bundler_2.png

bundler_1.png

把code從字符串變成函數(shù)

步驟
1创南、把code字符串外面包一個(gè)function(require, modules, exports){....};
2省核、再把這個(gè)code寫到文件里稿辙,注意不要讓引號(hào)出現(xiàn)在文件中;

完善execude函數(shù)

function execute(key) {
  // 如果已經(jīng) require 過气忠,就直接返回上次的結(jié)果
  if (modules[key]) { return modules[key] }
  // 找到要執(zhí)行的項(xiàng)目
  var item = depRelation.find(i => i.key === key)
  // 找不到就報(bào)錯(cuò)邻储,中斷執(zhí)行
  if (!item) { throw new Error(`${item} is not found`) }
  // 把相對(duì)路徑變成項(xiàng)目路徑
  var pathToKey = (path) => {
    var dirname = key.substring(0, key.lastIndexOf('/') + 1)
    var projectPath = (dirname + path).replace(/\.\//g, '').replace(/\/\//, '/')
    return projectPath
  }
  // 創(chuàng)建 require 函數(shù)
  var require = (path) => {
    return execute(pathToKey(path))
  }
  // 初始化當(dāng)前模塊
  modules[key] = { __esModule: true }
  // 初始化 module 方便 code 往 module.exports 上添加屬性
  var module = { exports: modules[key] }
  // 調(diào)用 code 函數(shù),往 module.exports 上添加導(dǎo)出屬性
  // 第二個(gè)參數(shù) module 大部分時(shí)候是無用的旧噪,主要用于兼容舊代碼
  item.code(require, module, module.exports)
  // 返回當(dāng)前模塊
  return modules[key]
}

手動(dòng)構(gòu)造dist.js文件

最后dist.js文件的主體結(jié)構(gòu)應(yīng)該如下

var depRelation = [
    {key: 'index.js', deps: ['a.js', 'b.js'], code: function...},
    {key: 'a.js', deps: ['b.js'], code: function...},
    {key: 'b.js', deps: ['a.js'], code: function...},
]
var modules = {}
execute(depRelation[0].key)
function execute(key){
  var require = ...
  var module = ...
  item.code(require, module, module.exports )
....

}

運(yùn)行dist.js吨娜,可以得到和index.js一樣的結(jié)果


dist.png

如何得到最終文件dist.js

我們已經(jīng)知道了dist.js內(nèi)容是什么了,但是該怎么去得到它呢舌菜,也就是說怎么自動(dòng)生成dist.js文件呢萌壳?
答:拼湊出字符串,再把這些字符串寫入到文件中即可,如下:

var code = ''
code += content
writeFileSync('dist.js', code)

編寫bundler_3.ts袱瓮,把它與bundler_2.ts做一下對(duì)比看哪里做了改動(dòng)缤骨;bundler_3.ts就是打包器,可以自動(dòng)生成最終文件尺借,運(yùn)行bundler_3.ts绊起,得到dist_2.js(只是把名字變了下),node ./dist_2.js燎斩,得到結(jié)果與index.js一樣虱歪;
并把我們通過打包器自動(dòng)生成的dist_2.js與我們手動(dòng)寫出來的dist.js作對(duì)比,發(fā)現(xiàn)內(nèi)容是一樣的栅表。
所以bundler_3.ts里的內(nèi)容就是一個(gè)簡(jiǎn)易打包器笋鄙,也就是webpack的核心內(nèi)容!怪瓶!

具體步驟

1萧落、需要得到一個(gè)depRelation來收集依賴,它是一個(gè)數(shù)組洗贰,具體內(nèi)容如下:

depRelation = [
    {key: 'index.js', deps: ['a.js', 'b.js'], code: `index.js代碼內(nèi)容的字符串`}, 
    {key: 'a.js', deps: ['b.js'], code: `a.js代碼內(nèi)容的字符串`},
    {key: 'b.js', deps: ['a.js'], code: `b.js代碼內(nèi)容的字符串`},
]

2找岖、得到execude函數(shù),函數(shù)具體內(nèi)容可見上面敛滋,它接受一個(gè)參數(shù)许布,參數(shù)為depRelation[0].key,相當(dāng)于depRelation[0].key(其實(shí)就是index.js)就是一個(gè)入口文件绎晃;

3蜜唾、編寫generateCode函數(shù),它是打包器的核心箕昭,主要通過字符串拼接把我們手寫出的dist.js里的內(nèi)容灵妨,通過writeFileSync('dist2.js', generateCode())輸出到指定文件里面去;這個(gè)過程中需要把depRelation中每一項(xiàng)中的code由字符串變成函數(shù)落竹;

4、最后生成的dist2.js就是打包出來的文件啦货抄;

不過目前這個(gè)簡(jiǎn)易打包器還有不少問題:
1述召、生成的代碼中有不少重復(fù)的函數(shù);
2蟹地、目前只能引入和運(yùn)行JS文件积暖;
3、只能理解import怪与,無法理解require夺刑;
4、不支持插件;
5遍愿、不支持配置入口文件和dist的文件名存淫;
但本篇文章主旨在于理解打包器是怎么打包的,所以這些問題后面一一再來梳理沼填;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末桅咆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子坞笙,更是在濱河造成了極大的恐慌岩饼,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件薛夜,死亡現(xiàn)場(chǎng)離奇詭異籍茧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)梯澜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門寞冯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人腊徙,你說我怎么就攤上這事简十。” “怎么了撬腾?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵螟蝙,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我民傻,道長(zhǎng)胰默,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任漓踢,我火速辦了婚禮牵署,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘喧半。我一直安慰自己奴迅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布挺据。 她就那樣靜靜地躺著取具,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扁耐。 梳的紋絲不亂的頭發(fā)上暇检,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音婉称,去河邊找鬼块仆。 笑死构蹬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的悔据。 我是一名探鬼主播庄敛,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蜜暑!你這毒婦竟也來了铐姚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤肛捍,失蹤者是張志新(化名)和其女友劉穎隐绵,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拙毫,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡依许,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缀蹄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片峭跳。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖缺前,靈堂內(nèi)的尸體忽然破棺而出蛀醉,到底是詐尸還是另有隱情,我是刑警寧澤衅码,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布拯刁,位于F島的核電站,受9級(jí)特大地震影響逝段,放射性物質(zhì)發(fā)生泄漏垛玻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一奶躯、第九天 我趴在偏房一處隱蔽的房頂上張望帚桩。 院中可真熱鬧,春花似錦嘹黔、人聲如沸账嚎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽醉锄。三九已至,卻和暖如春浙值,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背檩小。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工开呐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓筐付,卻偏偏與公主長(zhǎng)得像卵惦,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瓦戚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容