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