本文由作者郑海波授权网易云社区发布。
前言我这里就不介绍 requirejs 了,简而言之:requirejs 是支持 AMD 规范的模块加载器,事实上它也是 AMD 的最直接推动者。
现在可供挑选的开源模块解决方案很多,比如 component、cjs+browserify、umd 等等,但是无疑类似 requirejs 这类加载系统是现在最成熟和可靠的解决方案,所以 regularjs 第一步就是提供对 requirejs 的插件支持。
requirejs 的插件体系 requirejs 的源码内部预留了 hook,使得你可以创建插件来增强这个模块系统,并且这个插件可以做到影响到你的 OPTIMIZER 阶段,一些资源可以被处理为标准的 AMD 模块。
插件普遍被用来
预编译
加载非 js 文本
lint 或 test 后置或前置的操作 等等
example 比如它本身是不支持加载文本信息的,但是你可以通过 text! 插件来加载。
require([‘text!foo.html’, ‘jquery’], function(foo, $){
$(‘#anchor’).html(foo);
})需要注意的是由于文本无法用 script 标签进行加载,所以 text 内部是通过 XHR 来载入的,即它会受到同源策略的影响。
优化 OPTIMIZER
由于 requirejs 同时提供工具 (npm:requirejs) 可以静态打包优化 AMD,刚才的那个 text!foo.html 会同时被 text 插件转换为类似下面的 AMD 模块结构
define(‘text!foo.html’,[],function () {
return ‘<h2> 早上好 \n</h2>’;
});requirejs 的插件其实是一个实现的特定接口的标准 AMD 模块,它在定义时与其它业务模块并无区别。
例如官方 text 插件的源文件
define([‘module’], function (module) {‘use strict’; var text = {
load: function(){}
….
} return text
})其中 load 等接口是插件必须实现的,
对于各个接口描述我就不细究了,大家可以参考官网
顺便列举一些有用的 requirejs 插件
text 插件(最常用插件) 如果你的文本内容无需在打包优化阶段做处理,几乎都可以使用这个插件来完成加载
json 插件 比楼上多做了一步 JSON.parse.
amd-loader(好东西): 注意不要 requirejs 本身弄混了,因为 requirejs 本身不是基于 xhr 的,这个插件主要是提供完善的 xhr 支持来加载文本内容。一句化即它是[插件的 loader 插件],作者事后才发现有这么一个插件 … 绕了不少弯路。具体例子可以查看 es6
handlebars 用来加载 handlebar 的插件
其实由于 amd 等模块系统占据了开发中的模块入口这一环,其实在开发中可以有无限的可能性,这也是常规大公司都会自造一个轮子来最优配置的缘由之一,事实上 requirejs 目前的插件系统已经有足够的灵活性来定制自己的策略。
实现 requirejs-regular 的过程背景
首先我们先理清我们的需求, 与常规的的模板预编译类似,我们的插件主要为了实现两个功能。
在开发阶段,我们希望能加载 js 文件一样,加载我们的模板文件,这带来的几个好处
这使得我们不必将模板零散的填充到页面的 script 或 textarea 标签中
依赖系统唯一化, 模板依赖集成进了模块依赖中
在优化阶段(即 requirejs 提供的 OPTIMIZER 的上线打包功能),我们的模板字符串可以被预处理为序列化的 AST 对象,这样就不会发生浏览器端的解析,效率更高。
实现
一个插件模块会同时跑在浏览器端 (开发环境) 和 node 端(为线上或测试环境的打包优化工具),所以你的插件模块必须可以同时跑在浏览器端和 node 端,这个几乎是整个开发环境最麻烦的一部分
regular.js 的单文件虽然是 umd 模块可以支持 amd 环境,但是由于依赖的 dom。所以首先要将 parser 部分 (不依赖 dom) 打成一个 AMD 模块,由于 regularjs 本身就是基于 commonjs 的模块构建,将其中一部分打成 AMD 模块是分分钟的事情,这里我们使用 webpack 来打包成 regular-parser.js,简单起见我们随 regularjs 模块一同发布到 bower 上
我们还要解决模板的加载问题,插件内部的加载问题也要手动解决,即你至少要实现 loader 接口和 get 接口。这里我们完全可以偷个懒,直接使用!text 插件。
即插件会依赖这两个模块
define([‘text’, ‘regular-parser’], function(text, parser){//blalalalala…
return{
load: load,
write: write
}
})然后我们只需要实现两个接口:
load
var buildMap = {};function load(name, req, onLoad, config){
text.load(name, req, function(data,r){
onLoad(
(buildMap[name] = parser.parse(data, false))
);
}, config);
}这里直接使用了 text 插件的纯文本加载,需要注意的是这个 onLoad 接口,传入参数相当于模块的内容,我们这里预 parse 了这段文本内容。即你通过 rgl!template.html 最终会获得解析后的 AST 数据。
其实对于 regularjs 来讲在浏览器端有无进行模块系统层面的预解析并无意义,关键是在打包优化阶段。这里的 buildMap 主要是为了保存这段内容用于打包使用。
write 实现 write 接口主要是为了在打包优化阶段改写相关模块
var tpl = function(str, data){return str.replace(/{{(w+)}}/g, function(all, name){return data[name] || “”
})
}var template =’define(“{{pn}}!{{mn}}”,function(){ return {{ast}} });n’;function write(pn, mn, writeModule){if(buildMap[mn]){
writeModule(
tpl(template,{
pn: pn,
mn: mn,
ast: parser.parse(buildMap[mn])
})
)
}}此时这个插件必须依赖于两个模块,即必须同时保证 text 和 regular-parser 模块同时存在,类似的方案可以查看 hogan,它必须保证环境中有 hogan 和 text 才可以运行. 熟悉 requirejs 打包过程的同学也知道,除了 loader 端的配置,我们在 build 的打包文件也需要一并将这些依赖模块剔除,因为上线时是不需要这些插件的。所以这将大大增加配置成本,其实解决方案也很简单,就是使用 [webpack] 再将其打包成一个 standlone 的 AMD 模块即可,具体可以参考这里的 gulpfile。
大功告成
使用就非常简单了,和你使用 requirejs-text 差不多,
1. 首先下载 rgl.js,最简单的就是 bower 安装
bower install regularjs-regular –savesave 参数是安装后并写入到 bower.json 中,这个和 npm 一致
2. 配置
require.config({paths : { “rgl”: ‘../../bower_components/regularjs-regular/rgl’,
// 同时载入我们的 regularjs 来使用这些模板 “regularjs”: ‘../../bower_components/regularjs/dist/regular’
}});3. 使用
require([‘rgl!./foo.html’, ‘regularjs’], function(tpl, Regular){var Foo = Regular.extend({
template: tpl
}) new Foo({}).$inject(“#app”)
});4. 打包
模板文件 <h2>{{message}}</h2> 经过插件处理后会打包成
define(“rgl!foo.html”,function(){return [{“type”:”element”,”tag”:”h2″,”attrs”:[],”children”:[{“type”:”expression”,”body”:”_c_._sg_(‘message’, _d_[‘message’])”,”constant”:false,”setbody”:”_c_._ss_(‘message’,_p_,_d_, ‘=’)”}]}] }); 即上线后就不会有 parse 了,比如 PO 主目前正在开发的项目在初期就有几十个模板文件,build 成单文件后的运行时开销还是应该尽量避免.
tip:build.js 记得通过 stubModules 配置项目删除掉这个插件模块,具体看 demo 的 build.js。
对于 NEJ 的使用者 NEJ 的新模块系统支持上述类似的 regular 模板加载了
网易杭州的同事,事实上你已经可以在 NEJ 的新模块系统中(完全兼容老版本)通过 regular!path/to/template.html 的方式来加载你的 regular 模板了,打包之后模板将会被预解析,同时新版 NEJ 也支持 text! 加载纯文本内容, 详询 @飞锅。新版本的加载系统,支持类似 AMD 的注入写法,并且兼容老版本的模块写法,亲测好用哈。
免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐
更多网易技术、产品、运营经验分享请访问网易云社区。
文章来源:网易云社区