做爲近段時間風頭正盛的打包工具,webpack基本佔領了前端圈。相信你都很差意思說不知道webpack。
有興趣的同窗能夠參考下我很早以前的webpack簡介 .
確實webpack萬事萬物皆模塊的思路真是極大的方便了咱們的開發,將css,圖片等文件都能打包的功能離不開形形色色的loader。
對於一個事情要知其然更要知其因此然,抱着這個心態咱們一塊兒來看下loader的相關知識及如何開發。css
對於一個新事物最好的學習方法,我認爲是其官方文檔。對於loader,將其官方文檔看一遍,就知道如何開發最簡單的loader了。
只是其官方文檔是英文的,我就順手翻譯了一下,一方面加深本身理解。另外一方面爲其餘同窗提供個參考。
我相信看完文檔你就知道如何開發一個loader了。html
loader是一個對面暴露一個方法的node包.當遇到某些資源須要被轉換時調用該方法。前端
只有一個loader來處理某個文件時,該loader被調用時只有一個參數,這個參數是該文件的內容轉化以後的字符串。node
loader在function執行時能夠經過this context來訪問laoder API 以便更高效的開發。webpack
一個僅僅須要一個值的同步loader能夠簡單的return 本身。其餘狀況下,loader能夠經過this.callback(err, values...)返回一系列的值。error一樣傳遞給this.callback或者在loader中拋出。git
loader指望返回1-2個值,第一個是處理以後做爲string或者buffer返回的js代碼。第二個是SourceMap或者js 對象github
當多個loader被鏈式調用時,只有最後一個loader得到資源文件。
同時只有第一個loader被指望返回1-2個值(即上面提到的JavaScript和SourceMap)。
其餘loader接收值由上一個loader傳遞。web
換句話說,鏈式loader執行順序從右至左或者自下而上。
舉個栗子:下面這段代碼的執行順序就是自下而上 foo-loader==>bar-loadernpm
module: { loaders: [ { test: /\.js/, loaders: [ 'bar-loader', 'foo-loader' ] } ] }
若是你的文件夾不在該目錄下須要在config下面增長一項配置:
即默認訪問node_modules,你的文件夾不在的話就須要手動在配置文件里加上了。編程
resolveLoader: { modules: ['node_modules', path.resolve(__dirname, 'loaders')] }
ps:通過自身實踐發現這樣寫是錯的,不須要經過path去解析,直接將文件目錄寫入便可。
通常來講loader都會發布到npm上進行管理,這種情況不用擔憂,可是開發階段若是要自行測試,就面對這種狀況了。
例如,我手寫的myloader在loaders下面,例子以下。
resolveLoader:{ modules: ['node_modules','loader'] }
就這麼簡單就是個普通的loader
module.exports = function(source,map){ this.cacheable && this.cacheable() this.value = source return '/*copy@ xiaoxiangdaiyu*/'+JSON.stringify(source) }
loader須要遵循如下事項。
如下事項按優先級排列,第一條具備最高優先級。
loaders能夠被鏈式調用,爲每一步建立一個loader而非一個loader作全部事情
也就是說,在非必要的情況下沒有必要將他們轉換爲js。
例如:經過查詢字符串將一個字符串模板轉化爲html。
若是你寫了個loader作了全部事情那麼你違背了loader的第一條要求。
你應該爲每個task建立一個loader而且經過管道來使用它們
loader產出的module應該和遵循和普通的module同樣的設計原則。
舉個例子,下面這樣設計是很差的,沒有模塊化,依賴全局狀態
require("any-template-language-loader!./xyz.atl"); var html = anyTemplateLanguage.render("xyz");
大部分loaders是cacheable,因此應該標明是否cacheable。
只須要在loader裏面調用便可
// Cacheable identity loader module.exports = function(source) { this.cacheable(); return source; };
若是該loader引用了其餘資源(例如文件系統), 必須聲明它們。這些信息用來是緩存的loader失效而且從新編譯它們
var path = require("path"); module.exports = function(source) { this.cacheable(); var callback = this.async(); var headerPath = path.resolve("header.js"); this.addDependency(headerPath); fs.readFile(headerPath, "utf-8", function(err, header) { if(err) return callback(err); callback(null, header + "\n" + source); }); };
不少語言都提供了一些規範來聲明依賴,例如css中的 @import 和 url(...)。這些依賴應該被模塊系統所解析。
若是語言僅僅接受相對urls(如css中url(file) 老是表明./file),使用~來講明成模塊依賴.
url(file) -> require("./file") url(~module) -> require("module")
extract common code 我感受仍是翻譯成上面的標題比較好。其實全部語言都遵循該思想,即封裝
不要寫出來不少每一個模塊都在使用的代碼,在loader中建立一個runtime文件,將公共代碼放在其中
不要把絕對路徑寫入到模塊代碼中。它們將會破壞hash的過程當項目的根目錄發生改變的時候。應該使用loader-utils的 stringifyRequest方法來絕對路徑轉化爲相對路徑。
例子:
var loaderUtils = require("loader-utils"); return "var runtime = require(" + loaderUtils.stringifyRequest(this, "!" + require.resolve("module/runtime")) + ");";
使用peerDependency容許應用開發者去在package.json裏說明依賴的具體版本。這些依賴應該是相對開放的容許工具庫升級而不須要從新發布loader版本。簡而言之,對於peerDependency依賴的庫應該是鬆耦合的,當工具庫版本變化的時候不須要從新變動loader版本。
有些狀況下,loader須要某些可編程的對象可是不能做爲序列化的query參數被方法解析。例如less-loader經過具體的less-plugin提供了這種可能。這種狀況下,loader應該容許擴展webpack的options對象去得到具體的option。爲了不名字衝突,基於loader的命名空間來命名是很必要的。
// webpack.config.js module.exports = { ... lessLoader: { lessPlugins: [ new LessPluginCleanCSS({advanced: true}) ] } };
至此,如何開發一個webpack loader 我相信你們已經知道了,若是還不太清楚的話,能夠移步w-loader查看。
另外,對於我這種英語渣渣來講,翻譯起來確實難度蠻大的。此處拋磚引玉,但願你們共同探討學習。