摘要:用來轉(zhuǎn)換內(nèi)容,內(nèi)部調(diào)用了方法進(jìn)行轉(zhuǎn)換,這里簡(jiǎn)單介紹一下的原理將代碼解析成,對(duì)進(jìn)行轉(zhuǎn)譯,得到新的,新的通過轉(zhuǎn)換成,核心方法在中的方法,有興趣可以去了解一下。將函數(shù)傳入?yún)?shù)和歸并,得到。通常我們是用不上的,估計(jì)在某些中可能會(huì)使用到。
什么是Loader?
繼上兩篇文章webpack工作原理介紹(上篇、下篇),我們了解到Loader:模塊轉(zhuǎn)換器,也就是將模塊的內(nèi)容按照需求裝換成新內(nèi)容,而且每個(gè)Loader的職責(zé)都是單一,只會(huì)完成一種轉(zhuǎn)換,所以我們一般對(duì)源文件的處理,也是由多個(gè)Loader以鏈?zhǔn)巾樞驁?zhí)行的方式來進(jìn)行多次裝換,然后得到我們要的結(jié)果。
那么這樣Loader只需要關(guān)心輸入和輸出,Loader其實(shí)是一個(gè)Node.js模塊,該模塊導(dǎo)出的是一個(gè)函數(shù)(意味著,所有node.js的api我們都可以使用),如下:
module.exports = function (source) { // 對(duì)source做一系列的轉(zhuǎn)換 return source; }
下面我們介紹一下webpack提供了哪些供Loader調(diào)用的api,對(duì)Loader有個(gè)比較深刻的理解,然后來分析babel-loader的源碼,看看我們常用的loader是怎么編寫出來的。
獲得Loader的optionsconst loaderUtils = require("loader-utils"); module.exports = function(source) { // 獲取用戶為當(dāng)前Loader傳入的options console.log(loaderUtils.getOptions(this)); return source; }返回其他結(jié)果
如上,我們返回的是轉(zhuǎn)換后的內(nèi)容,但是有些情況下,我們不僅僅需要返回轉(zhuǎn)換后的內(nèi)容,還需要返回一些其他的內(nèi)容,如sourceMap或是AST語(yǔ)法樹,那么這時(shí)候我們可以使用webpack提供的APIthis.callback,當(dāng)使用this.callback了,那么我們就必須需要在Loader函數(shù)返回undefined,以此來讓webpack知道返回的結(jié)果在this.callback中,API詳細(xì)參數(shù)如下:
this.callback( // 無法裝換原內(nèi)容時(shí)的Error err: Error || null, // 裝換后的的內(nèi)容,如上述的source content: string | Buffer, // 用于通過裝換后的內(nèi)容得出原內(nèi)容的Source Map,方便調(diào)試 // 我們了解到,SourceMap我們只是會(huì)在開發(fā)環(huán)境去使用,于是就會(huì)變成可控制的, // webpack也提供了this.sourceMap去告訴是否需要使用sourceMap, // 當(dāng)然也可以使用loader的option來做判斷,如css-loader sourceMap?: SourceMap, // 如果本次轉(zhuǎn)換同時(shí)生成ast語(yǔ)法樹,也可以將這個(gè)ast返回,方便后續(xù)loader需要復(fù)用該ast,這樣可以提高性能 abstractSyntaxTree? AST );同步與異步
看看異步Loader在this.asyncAPI下如何實(shí)現(xiàn),
module.exports = async function (source) { const callback = this.async(); const { err, content, sourceMap, AST } = await Func(); callback(err, content, sourceMap, AST); // 如上訴`this.callback`參數(shù)一樣 }處理二進(jìn)制數(shù)據(jù)
像file-loader這樣的Loader,處理的是二進(jìn)制數(shù)據(jù),那么就需要告訴webpack給loader傳入二進(jìn)制格式的數(shù)據(jù),代碼可以如下:
module.exports = function(source) { if (source instanceof Buffer) { // 一系列操作 return source; //當(dāng)然我本身也可以返回二進(jìn)制數(shù)據(jù)提供給下一個(gè)loader } } moudle.exports.raw = true; //不設(shè)置,就會(huì)拿到字符串
通過moudle.exports.raw = true;告知webpack,自己本身需要二進(jìn)制數(shù)據(jù)。
緩存加速優(yōu)化的最佳點(diǎn),可以使用this.cacheable(Boolen),緩存loader轉(zhuǎn)換后的內(nèi)容,當(dāng)處理文件或依賴文件沒有發(fā)生變化時(shí),使用緩存的轉(zhuǎn)換內(nèi)容,以此提速!
其他API說到學(xué)習(xí),當(dāng)然越系統(tǒng)越好了,api多介紹 ,除了上面常用的api之外,還存在以下常用的api。
babel-loader源碼簡(jiǎn)析this.context: 當(dāng)前處理轉(zhuǎn)換的文件所在的目錄
this.resource: 當(dāng)前處理轉(zhuǎn)換的文件完整請(qǐng)求路徑,包括querystring
this.resourcePath: 當(dāng)前處理轉(zhuǎn)換的文件的路徑
this.resourceQuery: 當(dāng)前處理文件的querystring
this.target: webpack配置的target
this.loadMoudle: 處理文件時(shí),需要依賴其他文件的處理結(jié)果時(shí),可以使用this.loadMoudle(request: string, callback: function(err, source, sourceMap, module))去獲取到依賴文件的處理結(jié)果。
this.resolve: 獲取指定文件的完整路徑,this.resolve(context: string, request: string, callback: function(err, result: string))
this.addDependency: 為當(dāng)前處理文件添加依賴文件,以便依賴文件發(fā)生變化時(shí)重新調(diào)用Loader轉(zhuǎn)換該文件,this.addDependency(file: string)
this.addContextDependency: 為當(dāng)前處理文件添加依賴文件目錄,以便依賴文件目錄里文件發(fā)生變化時(shí)重新調(diào)用Loader轉(zhuǎn)換該文件,this.addContextDependency(dir: string)
this.clearDependencies: 清除當(dāng)前正在處理的文件的所有依賴
this.emitFile: 輸出一個(gè)文件,使用的方法為this.emitFile(name: string, content: Buffer | string, sourceMap: {...})
源碼第一行如下:
let babel; try { babel = require("@babel/core"); } catch (err) { if (err.code === "MODULE_NOT_FOUND") { err.message += " babel-loader@8 requires Babel 7.x (the package "@babel/core"). " + "If you"d like to use Babel 6.x ("babel-core"), you should install "babel-loader@7"."; } throw err; }
babel-loader依賴了@babel/core,這就是安裝babel-loader需要同時(shí)安裝@babel/core(通常會(huì)再安裝babel-preset-env、babel-plugin-transform-runtime、babel-runtime)的原因。我們接下去看,src/index.js整個(gè)文件是不是按照我們前面所講編寫Loader的方法來組織代碼的。
//引入package.json const pkg = require("../package.json"); /* 根據(jù)babel-loader是否配置cacheDirectory屬性來告訴 babel-loader是否緩存loader的執(zhí)行結(jié)果,如果true, 便會(huì)使用cache方法去實(shí)現(xiàn),`cache.js`文件有著read、write、filename(文件命名方法) 以及如何處理緩存的handleCache方法(有則讀,無則寫再讀),有興趣可以去看看。 */ const cache = require("./cache"); /* transfrom.js用來轉(zhuǎn)換內(nèi)容,內(nèi)部調(diào)用了babel.transform方法進(jìn)行轉(zhuǎn)換,這里簡(jiǎn)單介紹一下babel的原理: babylon將es6/es7代碼解析成ast,babel-traverse對(duì)ast進(jìn)行轉(zhuǎn)譯,得到新的ast,新的ast通過 babel-generator轉(zhuǎn)換成es5,核心方法在@babel/core/lib/transformation/index.js中的`runSync` 方法,有興趣可以去了解一下。 */ const transform = require("./transform"); const injectCaller = require("./injectCaller"); const path = require("path"); // 獲取Loader參數(shù)options const loaderUtils = require("loader-utils"); module.exports = makeLoader(); module.exports.custom = makeLoader; function makeLoader(callback) { const overrides = callback ? callback(babel) : undefined; return function(source, inputSourceMap) { // 上面介紹過的api可以得知,這是個(gè)異步Loader,做的是異步裝換的工作 const callback = this.async(); loader .call(this, source, inputSourceMap, overrides) .then(args => callback(null, ...args), err => callback(err)); }; } async function loader(source, inputSourceMap, overrides) { .... }
可以看到確實(shí)和我們Loader編寫方式是一樣的,通過module.exports = makeLoader();導(dǎo)出一個(gè)函數(shù),makeLoader()是一個(gè)高階函數(shù),又返回了一個(gè)函數(shù),通過const callback = this.async();可以知道,這是一個(gè)異步的loader,不難看出最重要的實(shí)現(xiàn)都在這一步函數(shù)loader里面了,那么到底在loader函數(shù)里面究竟做了些什么呢?我們來看看,在閱讀源碼前,最好先看看babel-loader的README,先做個(gè)基本了解.
上面代碼可以看出loader(source, inputSourceMap, overrides)函數(shù)入?yún)⒂腥齻€(gè),分別是source=>待轉(zhuǎn)換的code,inputSourceMap=>上一個(gè)loader處理后的sourceMap,有的話,overrides=>自定義加載器,整塊源碼可以分成幾部分,
let loaderOptions = loaderUtils.getOptions(this) || {};,獲取options,并且獲取當(dāng)前處理轉(zhuǎn)換的文件的路徑this.resourcePath。
判斷是否自定義加載器轉(zhuǎn)換,這里會(huì)進(jìn)行一系列對(duì)options.customize進(jìn)行判斷,options.customize一個(gè)相對(duì)路徑,loader函數(shù)參數(shù)overrides為空時(shí)起效,執(zhí)行let override = require(loaderOptions.customize);,有了override之后,后續(xù)邏輯(如轉(zhuǎn)換、獲取option)override都會(huì)進(jìn)行介入處理。
將函數(shù)傳入?yún)?shù)和LoaderOptions歸并,得到programmaticOptions。
調(diào)用babel.loadPartialConfig可以拿到babel配置并賦值給config變量,其實(shí)就是為了允許系統(tǒng)輕松操作和驗(yàn)證用戶的配置,此功能解決了插件和預(yù)設(shè)
生成cacheIdentifier
判斷options.cacheDirectory是否需要緩存Loader轉(zhuǎn)換內(nèi)容,如為true,調(diào)用cache.js的module.export Cache方法(上面已做介紹)
config.babelrc不為空,則有.babelrc文件,依賴.babelrc文件變化,使用this.addDependency(config.babelrc);
metadataSubscribers 訂閱元數(shù)據(jù),主要作用是訂閱一些編譯過程中的一些元數(shù)據(jù),訂閱以后這些元數(shù)據(jù)將會(huì)被添加 到webpack的上下文中。通常我們是用不上的,估計(jì)在某些babel-plugin中可能會(huì)使用到。
最后將處理后的結(jié)果返回
小結(jié)每一個(gè)Loader其實(shí)返回值就是一個(gè)Function,而且就是把帶轉(zhuǎn)換內(nèi)容傳入,得到轉(zhuǎn)換后的內(nèi)容,做的事情就是這樣,這篇文章先對(duì)Loader的基本概念進(jìn)行介紹,并且了解webpack為L(zhǎng)oader的編寫提供一些常用的API,最后通過簡(jiǎn)析babel-loader的源碼,我覺得應(yīng)該差不多知道如何去寫一個(gè)簡(jiǎn)單的Loader了,原文地址-個(gè)人博客。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/109116.html
摘要:模塊化原理簡(jiǎn)析的核心原理一切皆模塊在中,,靜態(tài)資源文件等都可以視作模塊便于管理,利于重復(fù)利用按需加載進(jìn)行代碼分割,實(shí)現(xiàn)按需加載。模塊化原理以為例,分析構(gòu)建的模塊化方式。 webpack模塊化原理簡(jiǎn)析 1.webpack的核心原理 一切皆模塊:在webpack中,css,html.js,靜態(tài)資源文件等都可以視作模塊;便于管理,利于重復(fù)利用; 按需加載:進(jìn)行代碼分割,實(shí)現(xiàn)按需加載。 2...
摘要:需要得到最后一個(gè)產(chǎn)生的處理結(jié)果。這個(gè)處理結(jié)果應(yīng)該是或者被轉(zhuǎn)換為一個(gè),代表了模塊的源碼。另外還可以傳遞一個(gè)可選的結(jié)果格式為對(duì)象。在異步模式中,必須調(diào)用,來指示等待異步結(jié)果,它會(huì)返回回調(diào)函數(shù),隨后必須返回并且調(diào)用該回調(diào)函數(shù)。 準(zhǔn)備工作 安裝 Node.js, 建議安裝LTS長(zhǎng)期支持版本 mkdir webpack and cd webpack and npm init -y npm ...
摘要:本文首發(fā)于的技術(shù)博客實(shí)用至上,非經(jīng)作者同意,請(qǐng)勿轉(zhuǎn)載。只是最近學(xué)習(xí)生態(tài),用起來轉(zhuǎn)換之余,也不免碰到諸多用上的教程案例,因此便稍作學(xué)習(xí)。在當(dāng)前的瀏覽器市場(chǎng)下,想在生產(chǎn)環(huán)境用上,是必不可少的。 本文首發(fā)于Array_Huang的技術(shù)博客——實(shí)用至上,非經(jīng)作者同意,請(qǐng)勿轉(zhuǎn)載。原文地址:https://segmentfault.com/a/1190000006992218如果您對(duì)本系列文章感興...
摘要:三集成所需要的依賴和在或加載模塊時(shí),對(duì)代碼進(jìn)行預(yù)處理,語(yǔ)法轉(zhuǎn)化為語(yǔ)法。到目前位置,用于開發(fā)應(yīng)用的環(huán)境已經(jīng)配置好了。 本系列主要學(xué)習(xí)webpack的配置。webpack自己間接的用過不少次,但是自己配置卻沒多少次,所以特地寫寫文章,學(xué)習(xí)webpack的配置,有不恰當(dāng)?shù)牡胤?,歡迎指正。這次配置 babel 。 若你對(duì)webpack的概念還不了解,先查看相應(yīng)文檔webpack中文文檔 一、初...
摘要:四用于對(duì)模塊的源代碼進(jìn)行轉(zhuǎn)換。對(duì)于圖片等都不能識(shí)別,所有需要引入對(duì)應(yīng)的對(duì)對(duì)應(yīng)格式的文件進(jìn)行轉(zhuǎn)換以便來識(shí)別。支持鏈?zhǔn)秸{(diào)用,調(diào)用順序由下到上,由右到左五插件目的在于解決無法實(shí)現(xiàn)的其他事。 一.entry entry是webpack打包的入口配置,entry對(duì)應(yīng)的值可以是字符串,數(shù)組,對(duì)象;單入口可以使用字符串、數(shù)組、對(duì)象,多入口配置則必須使用對(duì)象的方式 二.output output是we...
閱讀 2379·2021-11-23 09:51
閱讀 5852·2021-09-22 15:39
閱讀 3403·2021-09-02 15:15
閱讀 3556·2019-08-30 15:54
閱讀 2414·2019-08-30 15:53
閱讀 1456·2019-08-30 14:04
閱讀 2510·2019-08-29 18:33
閱讀 2477·2019-08-29 13:08