scrat 后端渲染解决方案的模板扩展
scrat后端渲染组件化开发模式通过扩展 nunjucks 模板引擎实现了组件资源管理方案。

用法
const path = require('path');
const nunjucks = require('nunjucks');
const pagelet = require('nujucks-pagelet');
const baseDir = path.join(process.cwd(), './test/fixtures/general');
const env = nunjucks.configure(baseDir, {});
pagelet.configure({
root: baseDir,
manifest: path.join(baseDir, 'manifest.json'),
cache: true
});
pagelet.register(env);
const locals = {};
const html = env.render('test.tpl', locals);
const express = require('express');
const app = express();
app.set('view engine', 'tpl');
env.express(app);
app.use(function(req, res, next) {
res.locals._query = req.query;
res.locals._body = req.body;
var pagelets = req.get('X-Pagelets');
if(pagelets){
res.set('Content-Type', 'application/json');
res.locals._pagelets = pagelets;
}
next();
});
app.get('/', function(req, res) {
res.render('test.tpl', res.locals);
});
资源依赖表
manifest 文件是通过构建工具生成的, 主要描述了资源的依赖关系.
{
"combo": false,
"comboPattern": "/co??%s",
"hash": "f0ab3c",
"res": {
"components/nav/nav.js": {
"uri": "c/nav/nav.js",
"type": "js",
"deps": [
"views/jquery/jquery.js",
"components/nav/nav.css"
]
},
"components/nav/nav.css": {
"uri": "c/nav/nav.css",
"type": "css",
"deps": [
"views/reset/reset.css"
]
},
"components/bar/bar.tpl": {
"uri": "c/bar/bar.tpl",
"type": "tpl",
"deps": [
"nav",
"components/bar/bar.css"
]
},
"components/bar/bar.css": {
"uri": "c/bar/bar.css",
"type": "css"
},
"components/foo/foo.tpl": {
"uri": "c/foo/foo.tpl",
"type": "tpl",
"deps": [
"nav",
"components/foo/foo.css"
]
},
"components/foo/foo.css": {
"uri": "c/foo/foo.css",
"type": "css"
},
"views/jquery/jquery.js": {
"uri": "v/jquery/jquery.js",
"type": "js"
},
"views/reset/reset.css": {
"uri": "v/reset/reset.css",
"type": "css"
},
"views/index/index.tpl": {
"uri": "views/test.tpl",
"type": "tpl",
"deps": [
"foo"
]
}
}
}
新增规则
新增模板标签
require
html
-
功能: 替代原生的 <html>
标签包裹页面主体部分,用于实现资源url输出时替换页面占位
-
闭合: YES
-
参数:
- cdn: 指定pagelet加载时所用的域名,可以是字符串字面量,也可以是模板变量
- doctype: 声明 doctype , 默认为 html
- 任何其他参数都将转换为输出的html标签的属性。
-
示例:
{% html class="abc" data-value="bcd" %}
...
{% endhtml %}
渲染后的html:
<!DOCTYPE html>
<html class="abc" data-value="bcd">
...
</html>
head
-
功能: 替代原生的 <head>
标签包裹页面head部分,用于实现资源CSS输出占位
-
闭合: YES
-
参数: 任何参数都将转换为输出的 head 标签的属性。
-
示例:
{% html%}
{% head class="abc" data-xxx="bcd" %}
...
{% endhead %}
...
{% endhtml %}
渲染后的html:
<html>
<head class="abc" "data-xxx"="bcd">
...
</head>
...
</html>
body
-
功能: 替代原生的 <body>
标签包裹页面body部分,用于实现资源JS输出占位
-
闭合: YES
-
参数: 任何参数都将转换为输出的 body 标签的属性。
-
示例:
{% html%}
{% head %}
...
{% endhead %}
{% body class="main" id="main" "data-ooo"="abc" %}
...
{% endbody %}
{% endhtml %}
渲染后的html:
<html>
<head>
...
</head>
<body class="main" id="main" data-ooo="abc">
...
</body>
</html>
script
-
功能: 替代原生的 <script>
标签,收集页面中散落的 script 脚本统一放到页面尾部输出。
-
闭合: YES
-
参数: 无
-
示例:
{% html%}
{% head %}
...
{% require $id="../lib/jquery.js" %} {# 引用资源 #}
{% endhead %}
{% body %}
...
{% script %}
var header = require('header');
header.init();
{% endscript %}
{% require $id="./index.js" %} {# 引用资源 #}
{% endbody %}
{% endhtml %}
渲染后的html:
<html>
<head>
...
</head>
<body>
...
<script src="/views/lib/jquery.js"></script>
<script src="/views/index/index.js"></script>
<script>
!function(){
var header = require('header');
header.init();
}();
</script>
</body>
</html>
-
注意: 在body闭合标签之前,js输出的顺序是:
- require标签加载的外链js
- script标签收集的内联js
pagelet
-
功能: 页面区域划分,用于 quickling 加载页面
-
闭合: YES
-
注意: pagelet默认会生成一个dom结构,如果希望不生成任何结构,须设置 $tag
属性值为字符串 "none"
-
参数:
- $id: 字符串|模板变量。定义pagelet的id
- $tag: 字符串|模板变量|"none"。要生成的占位标签的标签名,可以不指定,默认是
div
。如果指定为none,框架会输出一个注释来标注pagelet的范围。 - 任何其他参数都将转换为输出的pagelet占位标签的属性。
-
示例:
{% html%}
{% head %}
...
{% endhead %}
{% body %}
...
{% pagelet $id="main" class="main" %}
<ul>
{% pagelet $id="list" $tag="none" %}
{% for item in list %}
<li>{{ loop.index }} - {{ loop.key }}: {{ x }}</li>
{% endfor %}
{% endpagelet %}
</ul>
{% pagelet $id="form" $tag="form" id="my-form" class="form-control" %}
<input type="text" name="username">
{% endpagelet %}
{% endpagelet %}
...
{% endbody %}
{% endhtml %}
渲染后的html:
<html>
<head>
...
</head>
<body>
...
<div class="main" data-pagelet="main">
<ul>
<li>0 - a: 123</li>
<li>0 - b: 456</li>
</ul>
<form id="my-form" class="form-control" data-pagelet="main.form">
<input type="text" name="username">
</form>
</div>
...
</body>
</html>
title
-
功能: 如果使用 quickling 切换页面,你的页面此标签替代原生的 <title>
标签用以收集页面标题数据,这样页面切换后框架可以自动修改页面的title显示。
-
闭合: YES
-
参数: 无
-
示例:
{% html%}
{% head %}
...
{% title %}首页{% endtitle %}
{% endhead %}
{% body %}
...
{% endbody %}
{% endhtml %}
渲染后的html:
<html>
<head>
...
<title>首页</title>
</head>
<body>
...
</body>
</html>
datalet
-
功能: 在pagelet区域内收集模板数据将来在 quickling 加载时可以传递给前端框架。
-
闭合: NO
-
示例:
{% html%}
{% head %}
...
{% title %} 列表页 - 第{{ offset }}页 {% endtitle %}
{% endhead %}
{% body %}
...
{% pagelet $id="main" class="main" %}
{% pagelet $id="list" $tag="ul" %}
{% for item in list %}
<li>{{ loop.index }} - {{ loop.key }}: {{ x }}</li>
{% endfor %}
{% datalet page=offset %}
{% endpagelet %}
{% endpagelet %}
...
{% endbody %}
{% endhtml %}
假设前端获取main.list的pagelet数据:
{
"title": "列表页 - 第5页",
"html": {
"main.list": "..."
},
"data": {
page: 5
},
"js": [ ... ],
"css": [ ... ],
"script": [ ... ]
}
ATF
-
功能: 收集位于 ATF
标签以上的css资源,并内嵌到主文档的head标签中。只会收集使用 require
标签加载的css资源,并不会收集link标签的css资源。用于加快首屏渲染速度的优化。
-
闭合: NO
-
参数: 无
-
示例:
{% html%}
{% head %}
...
{% endhead %}
{% body %}
...
{% require $id="header" %}
{% require $id="./index.css" %}
{% ATF %}
{% require $id="footer" %}
...
{% endbody %}
{% endhtml %}
渲染后的html:
<html>
<head>
...
<style>
.header {...}
.index {...}
</style>
</head>
<body>
...
<link rel="stylesheet" href="/co??c/footer/footer.css" data-defer>
...
</body>
</html>