在服务端为Qjs填充默认的值,例如
输入:
<div q-text="name"></div>
{"name": "Qjs"}
直出:
<div q-text="name">Qjs</div>
编译阶段处理directives, 输出阶段处理filters对数据取值渲染模板获取到html
编译:
<div q-text="name">Qjs</div>
<div q-class="red: isRed"></div>
处理directives编译成underscore模板:
<div q-text="name"><%= name %></div>
<div q-class="red: isRed" class="<% if (isRed) { %>red<% } %>"></div>
输出:
处理filters, 使用模板函数对数据做渲染
编译阶段虚拟dom的大框架:
编译过程基本都在cheerio
建立的虚拟dom上操作, 因此解析Qjs语法等都可以直接复用Qjs的, 只需重写一套directives
q-text
var $ = require('cheerio');
directives.text = function(exp) {
$(this.el).html('{{- __filterValue(__obj, {}) }}');
}
<div q-text="list | length"></div>
获得的模板
<div q-text="list | length"><%- __filterValue(__obj, {"arg":null,"name":"list","filters":[["length"]]}) %></div>
q-show
var $ = require('cheerio');
directives.show = function(exp) {
$(this.el).css('__tplstrings1', 0);
}
var tpl = dom.toString().replace('__tplstrings1', '<% if (__filterValue()) { %>display:block;<% } %>');
先添加一个无用的style,之后替换成想要的
子模块:
q-vm在编译阶段只简单地植入一个变量
<div q-vm="head_module"><%= _vm[0] %></div>
在输出阶段,先获取子模块的html, 而后放入数据中渲染
var output = compiled(_.extend({}, data, {
_vm: '<div>head_module</div>'
}});
filters异常:
filter复用浏览器端的, 但某些filter是无法正常运行的, 例如
filters.width = function(val) {
return Match.min(val, $(window).width());
};
因此必须考虑无法兼容的问题:
function __filterValue(data, exp) {
var root = data[exp.name];
var name, args;
for (var i = 0; i < exp.filters.length; i++) {
try {
name = exp.filters[i][0];
args = [].concat(exp.filters[i]);
args[0] = root;
root = filters[name].apply(data, args);
} catch(ex) {
console.warn('filter failed: ' + name);
return root;
}
}
return root;
}
var fs = require('fs');
var Promise = require('promise');
// QLoader 会自动处理模块依赖关系
var QLoader = require('q-tpl/loader');
// 编译
var q = QLoader.compile({
root: 'root',
getQ: function(id) {
if (id === 'root') {
return {
raw: [
'<div q-text="list | length"></div>',
'<div q-vm="submodule"></div>'
].join(''),
filters: {
length: function(list) {
return list.length;
}
},
data: function(loader) {
return Promise.resolve({
list: [23, 3, 22]
});
}
};
} else if (id === 'submodule') {
return {
raw: '<h1 q-text="text"></h1>',
data: function(loader) {
return Promise.resolve({
text: 'hello world submodule'
});
}
};
}
}
});
module.exports = function(req, res) {
// 用户请求,输出
var loader = new Loader(req);
q(loader).done(function(html) {
res.write(html);
});
};
github: https://github.com/feix760/Q.tpl