做爲近段時間風頭正盛的打包工具,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-loader npm
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查看。另外,對於我這種英語渣渣來講,翻譯起來確實難度蠻大的。此處拋磚引玉,但願你們共同探討學習。