webpack-loader的編寫(xiě)(附源碼)

先說(shuō)下webpack打包原理

  • 識(shí)別入口文件
  • 通過(guò)逐層識(shí)別模塊依賴(lài)。(Commonjs、amd或者es6的import摹量,webpack都會(huì)對(duì)其進(jìn)行分析。來(lái)獲取代碼的依賴(lài))
  • webpack做的就是分析代碼馒胆。轉(zhuǎn)換代碼缨称,編譯代碼,輸出代碼
    最終形成打包后的代碼

這些都是webpack的一些基礎(chǔ)知識(shí)祝迂,對(duì)于理解webpack的工作機(jī)制很有幫助睦尽。

步入正題,loader

什么是lodaer呢型雳,其實(shí)就是一個(gè)個(gè)函數(shù)当凡,loader是文件加載器山害,能夠加載資源文件,并對(duì)這些文件進(jìn)行一些處理宁玫,諸如編譯粗恢、壓縮等,最終一起打包到指定的文件中

  • loader是文件加載器欧瘪,能夠加載資源文件眷射,并對(duì)這些文件進(jìn)行一些處理,諸如編譯佛掖、壓縮等妖碉,最終一起打包到指定的文件中
  • 第一個(gè)執(zhí)行的loader接收源文件內(nèi)容作為參數(shù),其他loader接收前一個(gè)執(zhí)行的loader的返回值作為參數(shù)芥被。最后執(zhí)行的loader會(huì)返回此模塊的JavaScript源碼

手寫(xiě)一個(gè)loader

需求:
1.處理.txt文件
2.對(duì)字符串做反轉(zhuǎn)操作
3.首字母大寫(xiě)
4.對(duì)打印出來(lái)的111進(jìn)行重寫(xiě)
例如:abcdefg轉(zhuǎn)換后為Gfedcba, console.log(111)=>console.log(222)

let's go!~

  • 首先創(chuàng)建兩個(gè)loader(這里以本地loader為例)
    為什么要?jiǎng)?chuàng)建兩個(gè)laoder欧宜?理由后面會(huì)介紹


    image.png

    image.png

    看,loader結(jié)構(gòu)是不是很簡(jiǎn)單拴魄,接收一個(gè)參數(shù)冗茸,并且return一個(gè)內(nèi)容就ok了。
    然后創(chuàng)建一個(gè)txt文件,里面隨便寫(xiě)點(diǎn)什么匹中,abcdef都行
    現(xiàn)在開(kāi)始配置webpack,


    image.png

    我們?cè)谌肟谖募袑?dǎo)入這個(gè)腳本
    為什么這里需要導(dǎo)入呢夏漱,我們不是配置了webapck處理所有的.txt文件么?
    因?yàn)閣ebpack會(huì)做過(guò)濾顶捷,如果不引用該文件的話挂绰,webpack是不會(huì)對(duì)該文件進(jìn)行打包處理的,那么你的loader也不會(huì)執(zhí)行(好像有點(diǎn)廢話- - 服赎!)
    image.png

    這個(gè)就是入口文件啦葵蒂,我稍微做了點(diǎn)操作,目的是為了展示在頁(yè)面中重虑。
    那你可能有疑問(wèn)了践付,html怎么才能夠展示呢,很簡(jiǎn)單缺厉,把打包之后dist中的js引入html就OK
    然后允許webpack就好了永高。webpack會(huì)自動(dòng)去找webpack.config.js。
    現(xiàn)在回答為什么要寫(xiě)兩個(gè)loader芽死?
    看到執(zhí)行的順序沒(méi)乏梁,我們的配置的是這樣的次洼。

 use: [
    './loader/uppercase-loader.js',
    './loader/reverse-loader.js'
]

正如前文所說(shuō)关贵, 處理一個(gè)文件可以使用多個(gè)loader,loader的執(zhí)行順序是和本身的順序是相反的卖毁。
我們也可以自己寫(xiě)loader解析自定義模板揖曾,像vue-loader是非常復(fù)雜的落萎,它內(nèi)部會(huì)寫(xiě)大量的對(duì).vue文件的解析,然后會(huì)生成對(duì)應(yīng)的html炭剪、js和css练链。
我們這里只是講述了一個(gè)最基礎(chǔ)的用法,如果有更多的需要奴拦,可以查看《loader官方文檔》

了解了基本模式后媒鼓,我們先不急著開(kāi)發(fā)。所謂磨刀不誤砍柴工错妖,我們先看看開(kāi)發(fā)一個(gè) loader 需要注意些什么绿鸣,這樣可以少走彎路,提高開(kāi)發(fā)質(zhì)量暂氯。下面是 webpack 提供的幾點(diǎn)指南潮模,它們按重要程度排序,注意其中有些點(diǎn)只適用特定情況痴施。

獲得 Loader 的 options

  const loaderUtils = require('loader-utils');
    module.exports = function(source) {
    // 獲取到用戶(hù)給當(dāng)前 Loader 傳入的 options
    const options = loaderUtils.getOptions(this);
    return source;
};

返回其它結(jié)果

上面的 Loader 都只是返回了原內(nèi)容轉(zhuǎn)換后的內(nèi)容擎厢,但有些場(chǎng)景下還需要返回除了內(nèi)容之外的東西。
例如以用 babel-loader 轉(zhuǎn)換 ES6 代碼為例辣吃,它還需要輸出轉(zhuǎn)換后的 ES5 代碼對(duì)應(yīng)的 Source Map动遭,以方便調(diào)試源碼。
為了把 Source Map 也一起隨著 ES5 代碼返回給 Webpack齿尽,可以這樣寫(xiě):

  module.exports = function(source) {
  // 通過(guò) this.callback 告訴 Webpack 返回的結(jié)果
  this.callback(null, source, sourceMaps);
  // 當(dāng)你使用 this.callback 返回內(nèi)容時(shí)沽损,該 Loader 必須返回 undefined,
  // 以讓 Webpack 知道該 Loader 返回的結(jié)果在 this.callback 中循头,而不是 return 中 
  return;
};

其中的 this.callback 是 Webpack 給 Loader 注入的 API绵估,以方便 Loader 和 Webpack 之間通信。
this.callback 的詳細(xì)使用方法如下:

  this.callback(
    // 當(dāng)無(wú)法轉(zhuǎn)換原內(nèi)容時(shí)卡骂,給 Webpack 返回一個(gè) Error
    err: Error | null,
    // 原內(nèi)容轉(zhuǎn)換后的內(nèi)容
    content: string | Buffer,
    // 用于把轉(zhuǎn)換后的內(nèi)容得出原內(nèi)容的 Source Map国裳,方便調(diào)試
    sourceMap?: SourceMap,
    // 如果本次轉(zhuǎn)換為原內(nèi)容生成了 AST 語(yǔ)法樹(shù),可以把這個(gè) AST 返回全跨,
    // 以方便之后需要 AST 的 Loader 復(fù)用該 AST缝左,以避免重復(fù)生成 AST,提升性能
    abstractSyntaxTree?: AST
);

Source Map 的生成很耗時(shí)浓若,通常在開(kāi)發(fā)環(huán)境下才會(huì)生成 Source Map渺杉,其它環(huán)境下不用生成,以加速構(gòu)建挪钓。
為此 Webpack 為 Loader 提供了 this.sourceMap API 去告訴 Loader 當(dāng)前構(gòu)建環(huán)境下用戶(hù)是否需要 Source Map是越。
如果你編寫(xiě)的 Loader 會(huì)生成 Source Map,請(qǐng)考慮到這點(diǎn)碌上。

同步與異步

Loader 有同步和異步之分倚评,上面介紹的 Loader 都是同步的 Loader浦徊,因?yàn)樗鼈兊霓D(zhuǎn)換流程都是同步的,轉(zhuǎn)換完成后再返回結(jié)果天梧。
但在有些場(chǎng)景下轉(zhuǎn)換的步驟只能是異步完成的盔性,例如你需要通過(guò)網(wǎng)絡(luò)請(qǐng)求才能得出結(jié)果,如果采用同步的方式網(wǎng)絡(luò)請(qǐng)求就會(huì)阻塞整個(gè)構(gòu)建呢岗,導(dǎo)致構(gòu)建非常緩慢冕香。
在轉(zhuǎn)換步驟是異步時(shí),你可以這樣:

module.exports = function(source) {
    // 告訴 Webpack 本次轉(zhuǎn)換是異步的后豫,Loader 會(huì)在 callback 中回調(diào)結(jié)果
    var callback = this.async();
    someAsyncOperation(source, function(err, result, sourceMaps, ast) {
        // 通過(guò) callback 返回異步執(zhí)行后的結(jié)果
        callback(err, result, sourceMaps, ast);
    });
};

處理二進(jìn)制數(shù)據(jù)

在默認(rèn)的情況下暂筝,Webpack 傳給 Loader 的原內(nèi)容都是 UTF-8 格式編碼的字符串。
但有些場(chǎng)景下 Loader 不是處理文本文件硬贯,而是處理二進(jìn)制文件焕襟,例如 file-loader,就需要 Webpack 給 Loader 傳入二進(jìn)制格式的數(shù)據(jù)饭豹。
為此鸵赖,你需要這樣編寫(xiě) Loader:

module.exports = function(source) {
    // 在 exports.raw === true 時(shí),Webpack 傳給 Loader 的 source 是 Buffer 類(lèi)型的
    source instanceof Buffer === true;
    // Loader 返回的類(lèi)型也可以是 Buffer 類(lèi)型的
    // 在 exports.raw !== true 時(shí)拄衰,Loader 也可以返回 Buffer 類(lèi)型的結(jié)果
    return source;
};
// 通過(guò) exports.raw 屬性告訴 Webpack 該 Loader 是否需要二進(jìn)制數(shù)據(jù) 
module.exports.raw = true;

以上代碼中最關(guān)鍵的代碼是最后一行 module.exports.raw = true;它褪,沒(méi)有該行 Loader 只能拿到字符串。

緩存加速

在有些情況下翘悉,有些轉(zhuǎn)換操作需要大量計(jì)算非常耗時(shí)茫打,如果每次構(gòu)建都重新執(zhí)行重復(fù)的轉(zhuǎn)換操作,構(gòu)建將會(huì)變得非常緩慢妖混。
為此老赤,Webpack 會(huì)默認(rèn)緩存所有 Loader 的處理結(jié)果,也就是說(shuō)在需要被處理的文件或者其依賴(lài)的文件沒(méi)有發(fā)生變化時(shí)制市,
是不會(huì)重新調(diào)用對(duì)應(yīng)的 Loader 去執(zhí)行轉(zhuǎn)換操作的抬旺。
如果你想讓 Webpack 不緩存該 Loader 的處理結(jié)果,可以這樣:

module.exports = function(source) {
  // 關(guān)閉該 Loader 的緩存功能
  this.cacheable(false);
  return source;
};

還有一些loader API就不一一說(shuō)了祥楣,想了解的同學(xué)开财,上面都有鏈接的,直接去官網(wǎng)看就好了误褪。
下面附上git地址:
git源碼地址
希望對(duì)你有所幫助哦~
最后手寫(xiě)不易责鳍,點(diǎn)個(gè)贊吧??

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市兽间,隨后出現(xiàn)的幾起案子历葛,更是在濱河造成了極大的恐慌,老刑警劉巖渡八,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啃洋,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡屎鳍,警方通過(guò)查閱死者的電腦和手機(jī)宏娄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)逮壁,“玉大人孵坚,你說(shuō)我怎么就攤上這事】” “怎么了卖宠?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)忧饭。 經(jīng)常有香客問(wèn)我扛伍,道長(zhǎng),這世上最難降的妖魔是什么词裤? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任刺洒,我火速辦了婚禮,結(jié)果婚禮上吼砂,老公的妹妹穿的比我還像新娘逆航。我一直安慰自己,他們只是感情好渔肩,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布因俐。 她就那樣靜靜地躺著,像睡著了一般周偎。 火紅的嫁衣襯著肌膚如雪抹剩。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天蓉坎,我揣著相機(jī)與錄音吧兔,去河邊找鬼。 笑死袍嬉,一個(gè)胖子當(dāng)著我的面吹牛境蔼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伺通,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼箍土,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了罐监?” 一聲冷哼從身側(cè)響起吴藻,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弓柱,沒(méi)想到半個(gè)月后沟堡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體侧但,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年航罗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了禀横。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡粥血,死狀恐怖柏锄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情复亏,我是刑警寧澤趾娃,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站缔御,受9級(jí)特大地震影響抬闷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜耕突,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一饶氏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧有勾,春花似錦疹启、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至雇逞,卻和暖如春荤懂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背塘砸。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工节仿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人掉蔬。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓廊宪,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親女轿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子箭启,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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