如何开发webpackloader
关于webpack
作为近段时间风头正盛的打包⼯具,webpack基本占领了前端圈。相信你都不好意思说不知道webpack。
有兴趣的同学可以参考下我很早之前的 .
确实webpack万事万物皆模块的思路真是极⼤的⽅便了我们的开发,将css,图⽚等⽂件都能打包的功能离不开形形⾊⾊的loader。
对于⼀个事情要知其然更要知其所以然,抱着这个⼼态我们⼀起来看下loader的相关知识及如何开发。
学习⽅法
对于⼀个新事物最好的学习⽅法,我认为是其官⽅⽂档。对于loader,将其官⽅⽂档看⼀遍,就知道如何开发最简单的loader了。
只是其官⽅⽂档是英⽂的,我就顺⼿翻译了⼀下,⼀⽅⾯加深⾃⼰理解。另⼀⽅⾯为其他同学提供个参考。
我相信看完⽂档你就知道如何开发⼀个loader了。
什么是loader
loader是⼀个对⾯暴露⼀个⽅法的node包.当遇到某些资源需要被转换时调⽤该⽅法。
简单情况
只有⼀个loader来处理某个⽂件时,该loader被调⽤时只有⼀个参数,这个参数是该⽂件的内容转化之后的字符串。
loader在function执⾏时可以通过this context来访问laoder API 以便更⾼效的开发。
⼀个仅仅需要⼀个值的同步loader可以简单的return ⾃⼰。其他情况下,loader可以通过this.callback(err, )返回⼀系列的值。error同样传递给this.callback或者在loader中抛出。
loader期望返回1-2个值,第⼀个是处理之后作为string或者buffer返回的js代码。第⼆个是SourceMap或者js 对象
复杂情况:
当多个loader被链式调⽤时,只有最后⼀个loader获得资源⽂件。
同时只有第⼀个loader被期望返回1-2个值(即上⾯提到的JavaScript和SourceMap)。
其他loader接收值由上⼀个loader传递。
换句话说,链式loader执⾏顺序从右⾄左或者⾃下⽽上。
举个栗⼦:下⾯这段代码的执⾏顺序就是⾃下⽽上 foo-loader==>bar-loader
module: {
loaders: [
{
test: /\.js/,
loaders: [
'bar-loader',
'foo-loader'
]
}
]
}
注意:当前weboack只会在nodemodules⽂件夹下⾯搜索你指定的loader
如果你的⽂件夹不在该⽬录下需要在config下⾯增加⼀项配置:
即默认访问node_modules,你的⽂件夹不在的话就需要⼿动在配置⽂件⾥加上了。
resolveLoader: {
modules: ['node_modules', solve(__dirname, 'loaders')]
}
温馨提⽰
ps:经过⾃⾝实践发现这样写是错的,不需要通过path去解析,直接将⽂件⽬录写⼊即可。
⼀般来说loader都会发布到npm上进⾏管理,这种状况不⽤担⼼,但是开发阶段如果要⾃⾏测试,就⾯对这种情况了。
例如,我⼿写的myloader在loaders下⾯,例⼦如下。
resolveLoader:{
modules: ['node_modules','loader']
}
Examples
就这么简单就是个普通的loader
this.cacheable && this.cacheable()
this.value = source
return '/*copy@ xiaoxiangdaiyu*/'+JSON.stringify(source)
}
开发指南
loader需要遵循以下事项。
以下事项按优先级排列,第⼀条具有最⾼优先级。
⼀、单⼀任务
loaders可以被链式调⽤,为每⼀步创建⼀个loader⽽⾮⼀个loader做所有事情
也就是说,在⾮必要的状况下没有必要将他们转换为js。
例如:通过查询字符串将⼀个字符串模板转化为html。
如果你写了个loader做了所有事情那么你违背了loader的第⼀条要求。
你应该为每⼀个task创建⼀个loader并且通过管道来使⽤它们
jade-loader: 转换模板为⼀个module
apply-loader: 创建⼀个module并通过查询参数来返回结果
html-loade: 创建⼀个处理html并返回⼀个string的模块
⼆、创建moulde话的模块,即正常的模块
loader产出的module应该和遵循和普通的module⼀样的设计原则。
举个例⼦,下⾯这样设计是不好的,没有模块化,依赖全局状态
require("any-template-language-loader!./xyz.atl");
var html = der("xyz");
三、尽量表明该loader是否可以缓存
⼤部分loaders是cacheable,所以应该标明是否cacheable。
只需要在loader⾥⾯调⽤即可
// Cacheable identity loader
this.cacheable();
return source;
};
四、不要在运⾏和模块之间保存状态
⼀个loader相对于其他编译后的模块应该是独⽴的。除⾮其可以⾃⼰处理这些状态
⼀个loader相对于同⼀模块之前的编译过程应该是独⽴的。
五、标明依赖
如果该loader引⽤了其他资源(例如⽂件系统),必须声明它们。这些信息⽤来是缓存的loader失效并且重新编译它们 var path = require("path");
this.cacheable();
var callback = this.async();
var headerPath = solve("header.js");
this.addDependency(headerPath);
if(err) return callback(err);
callback(null, header + "\n" + source);
});
};
六、解析依赖
很多语⾔都提供了⼀些规范来声明依赖,例如css中的 @import 和 url(...)。这些依赖应该被模块系统所解析。
下⾯是两种解决⽅式:
1、将它们转化成require
2、⽤solve⽅法来解析路径
下⾯是两个⽰例
1、css-loader: 将依赖转化成require,即⽤require来替换@import和 url(...),解析对其他样式⽂件的依赖
2、less-loader: 不能像css-loader那样做,因为所有的less⽂件需要⼀起编译来解析变量和mixins。因此其通过⼀个公共的路径逻辑来
扩展less编译过程。这个公共的逻辑使⽤solve来解析带有module系统配置项的⽂件。例如aliasing, custom module directories 等。
如果语⾔仅仅接受相对urls(如css中url(file) 总是代表./file),使⽤~来说明成模块依赖.
url(file) -> require("./file")
url(~module) -> require("module")
七、抽离公共代码
extract common code 我感觉还是翻译成上⾯的标题⽐较好。其实所有语⾔都遵循该思想,即封装
不要写出来很多每个模块都在使⽤的代码,在loader中创建⼀个runtime⽂件,将公共代码放在其中
⼋、避免写⼊绝对路径
cacheable不要把绝对路径写⼊到模块代码中。它们将会破坏hash的过程当项⽬的根⽬录发⽣改变的时候。应该使⽤loader-utils的 stringifyRequest⽅法来绝对路径转化为相对路径。
例⼦:
var loaderUtils = require("loader-utils");
return "var runtime = require(" +
loaderUtils.stringifyRequest(this, "!" + solve("module/runtime")) +
");";
九、使⽤peerDependencies来指明依赖的库
使⽤peerDependency允许应⽤开发者去在package.json⾥说明依赖的具体版本。这些依赖应该是相对开放的允许⼯具库升级⽽不需要重新发布loader版本。简⽽⾔之,对于peerDependency依赖的库应该是松耦合的,当⼯具库版本变化的时候不需要重新变更loader版本。
⼗、可编程对象作为查询项
有些情况下,loader需要某些可编程的对象但是不能作为序列化的query参数被⽅法解析。例如less-loader通过具体的less-plugin提供了这种可能。这种情况下,loader应该允许扩展webpack的options对象去获得具体的option。为了避免名字冲突,基于loader的命名空间来命名是很必要的。
// fig.js
...
lessLoader: {
lessPlugins: [
new LessPluginCleanCSS({advanced: true})
]
}
};
结束语
⾄此,如何开发⼀个webpack loader 我相信⼤家已经知道了,如果还不太清楚的话,可以移步查看。
另外,对于我这种英语渣渣来说,翻译起来确实难度蛮⼤的。此处抛砖引⽟,希望⼤家共同探讨学习。
此⽂为原创⽂章,转载请注明出处!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论