[size=large][align=center][b]Pure JS (5.1):在服务器端使用 JQuery DOM 和 JQuery Template[/b][/align][/size]
JQuery Template 是著名的客户端 JS 模板引擎,对于没有 SEO 需求的 Web 应用,甚至只用 JQuery Template 就可以完成所有的页面渲染。
但这种方式并不适用于所有 Web 应用,如 博客、新闻类的网站,是必须考虑 SEO 的;由于搜索引擎通常不会执行 JavaScript,所以最好在第一次返回页面时就包含希望被搜索到的数据。
一种常见的模式是第一次返回时使用服务器端 JS 进行初始渲染,而后续修改时使用客户端 JS 对界面进行修改。使用这种模式时,甚至可以在服务器端和客户端共用部分渲染代码。但前提是服务器端界面渲染代码风格与客户端一致。
因此,我在 Java DOM 操作的基础上,对 JQuery 和 JQuery Template 进行了修改,仅保留了 JQuery DOM 查询、DOM 操作,以及 JQuery Template 页面渲染函数和 Tag,不包括 data、events等。
这篇文章将通过测试案例说明已经完成的功能,下一篇文章将结合简单的例子介绍其使用方式,最后将介绍背后的实现细节。
[size=medium][align=center][b]测试案例[/b][/align][/size]
附件中的 PureJS 工程包含了这里提到的测试案例(注意工程路径中不能包含空格等特殊字符,否则 DOMParser会报错)。
下图是所有的测试案例(com.purejs.test.render.RenderTestSuite):
[align=center][img]http://dl.iteye.com/upload/picture/pic/93574/53f760d2-ca8c-3dbe-a2f3-2460f6329e14.png[/img][/align]
RenderTestSuite 的代码如下:
这个 test suite 共包含 185 项测试,下面将选择其中的重点进行说明。
[size=medium][align=center][b]JQuery Selectors[/b][/align][/size]
我们可以在这里看到 JQuery Selectors 的 API:[url]http://api.jquery.com/category/selectors/[/url]
这个部分的测试位于 JQuerySelectorsTest,共 50 项测试。实际 JS 文件位于 scripts/test/render/selectors 中。
ID Selector 是最基本的 selector 之一, id.js 代码如下:
name() 函数返回测试案例的名称,excute 函数执行被测试的代码,verify 函数验证结果。
pure.render 接受三个参数:
1. partial:可选,为 true 表示只对 HTML 片段进行渲染,不会补齐 DOCType,HTML标记,HEAD 等
2. name:模板名称
3. 渲染函数,参数是 JQuery 对象
返回值是一个字符串,及渲染后的页面代码。渲染结果是“线性输出”的,不保证缩进,Tag 将转为大写。不过这并不影响浏览器的解析。
这里对于如下的输入:
context.elems 将选取如下的片段:
[size=medium][align=center][b]JQuery Manipulation[/b][/align][/size]
我们可以在这里看到 JQuery Tranversing 的 API:[url]http://api.jquery.com/category/manipulation/[/url]
这个部分的测试主要位于 JQueryManipulationTest,共 23 项测试;部分测试放在 JQueyCSSTest 和 JQuryAttributes 分类中。
$.append() 是很常用的 Manipulation 函数,append.js 文件内容如下:
excute 函数创建了一个 id 为 test 的 input 元素,并添加到 id 为 c 的 div 中。
对于以下输入:
将得到如下结果:
[size=medium][align=center][b]JQuery Template[/b][/align][/size]
我们可以在这里看到 JQury Template 的 API:[/url]http://api.jquery.com/category/plugins/templates/[/url]
这个部分的测试放在 TemplateTest 中,共 20 项测试。
tmpl.js 中包含了最基本的测试:
excute 函数将数据 movies 注入到模板 movie-tmpl 中,并添加到 id 为 b 的 div 中。
对于如下输入:
将得到如下结果:
[size=medium][align=center][b]Child Page[/b][/align][/size]
Child Page 的测试实际上是包含在 TemplateTest 中的。我们可以用两种方式实现页面组装:
方式一:
1.对子页面进行渲染,渲染目标为一个临时的 div,渲染完成后将 body 的内容替换为这个临时的div。
2.将子页面渲染结果插入到父页面中的指定位置
[b]childPage.js[/b]
输入:
[b]test.html[/b]
[b]child.html[/b]
输出:
方式二:
1.将子页面的模板添加到父页面中
2.在父页面中统一进行渲染
[b]childTemplate.js[/b]
输入:
test.html (同上,略)
[b]child-tmpl.html[/b]
输出:(同上,略)
JQuery Template 是著名的客户端 JS 模板引擎,对于没有 SEO 需求的 Web 应用,甚至只用 JQuery Template 就可以完成所有的页面渲染。
但这种方式并不适用于所有 Web 应用,如 博客、新闻类的网站,是必须考虑 SEO 的;由于搜索引擎通常不会执行 JavaScript,所以最好在第一次返回页面时就包含希望被搜索到的数据。
一种常见的模式是第一次返回时使用服务器端 JS 进行初始渲染,而后续修改时使用客户端 JS 对界面进行修改。使用这种模式时,甚至可以在服务器端和客户端共用部分渲染代码。但前提是服务器端界面渲染代码风格与客户端一致。
因此,我在 Java DOM 操作的基础上,对 JQuery 和 JQuery Template 进行了修改,仅保留了 JQuery DOM 查询、DOM 操作,以及 JQuery Template 页面渲染函数和 Tag,不包括 data、events等。
这篇文章将通过测试案例说明已经完成的功能,下一篇文章将结合简单的例子介绍其使用方式,最后将介绍背后的实现细节。
[size=medium][align=center][b]测试案例[/b][/align][/size]
附件中的 PureJS 工程包含了这里提到的测试案例(注意工程路径中不能包含空格等特殊字符,否则 DOMParser会报错)。
下图是所有的测试案例(com.purejs.test.render.RenderTestSuite):
[align=center][img]http://dl.iteye.com/upload/picture/pic/93574/53f760d2-ca8c-3dbe-a2f3-2460f6329e14.png[/img][/align]
RenderTestSuite 的代码如下:
package com.purejs.test.render;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
JQueryAttributesTest.class,
JQueryCssTest.class,
JQueryInternalsTest.class,
JQueryManipulationTest.class,
JQueryMiscellaneousTest.class,
JQueryPropertiesTest.class,
JQuerySelectorsTest.class,
JQueryTraversingTest.class,
JQueryUtilitiesTest.class,
TemplateTest.class
})
public class RenderTestSuite {
}
这个 test suite 共包含 185 项测试,下面将选择其中的重点进行说明。
[size=medium][align=center][b]JQuery Selectors[/b][/align][/size]
我们可以在这里看到 JQuery Selectors 的 API:[url]http://api.jquery.com/category/selectors/[/url]
这个部分的测试位于 JQuerySelectorsTest,共 50 项测试。实际 JS 文件位于 scripts/test/render/selectors 中。
ID Selector 是最基本的 selector 之一, id.js 代码如下:
importPackage(org.junit);
function name() {
return "ID";
}
function execute() {
pure.render("test", function($){
context.elems = $("#b");
});
}
function verify() {
var elems = context.elems;
Assert.assertEquals(1, elems.length, 0);
Assert.assertEquals("b", elems[0].id);
}
name() 函数返回测试案例的名称,excute 函数执行被测试的代码,verify 函数验证结果。
pure.render 接受三个参数:
1. partial:可选,为 true 表示只对 HTML 片段进行渲染,不会补齐 DOCType,HTML标记,HEAD 等
2. name:模板名称
3. 渲染函数,参数是 JQuery 对象
返回值是一个字符串,及渲染后的页面代码。渲染结果是“线性输出”的,不保证缩进,Tag 将转为大写。不过这并不影响浏览器的解析。
这里对于如下的输入:
<div id="a" name="pre-middle-aend" words="word xxx">
<div id="b" name="pre" words="xxxx word" class="clz">
<h1 id="h1">John</h1>
</div>
</div>
context.elems 将选取如下的片段:
<DIV class="clz" id="b" name="pre" words="xxxx word">
<H1 id="h1">John</H1>
</DIV>
[size=medium][align=center][b]JQuery Manipulation[/b][/align][/size]
我们可以在这里看到 JQuery Tranversing 的 API:[url]http://api.jquery.com/category/manipulation/[/url]
这个部分的测试主要位于 JQueryManipulationTest,共 23 项测试;部分测试放在 JQueyCSSTest 和 JQuryAttributes 分类中。
$.append() 是很常用的 Manipulation 函数,append.js 文件内容如下:
importPackage(org.junit);
function name() {
return "Append";
}
function execute() {
context.html = pure.render("test", function($) {
$("#c").append($("<input id='test' />"));
context.elems = $("#c :last-child");
});
}
function verify() {
Assert.assertEquals("test", context.elems[0].id);
}
excute 函数创建了一个 id 为 test 的 input 元素,并添加到 id 为 c 的 div 中。
对于以下输入:
<div id="c" name="xxx-middle-cend" words="xxx xx">
<!-- 省略部分元素 -->
<input id="input-text" value="some text"
style="width:30px;padding:10px;border:1px solid;margin:1px 2px 3px 4px" />
</div>
将得到如下结果:
<DIV id="c" name="xxx-middle-cend" words="xxx xx">
<!-- 省略部分元素 -->
<INPUT id="input-text" value="some text"
style="width:30px;padding:10px;border:1px solid;margin:1px 2px 3px 4px"/>
<INPUT id="test"/>
</DIV>
[size=medium][align=center][b]JQuery Template[/b][/align][/size]
我们可以在这里看到 JQury Template 的 API:[/url]http://api.jquery.com/category/plugins/templates/[/url]
这个部分的测试放在 TemplateTest 中,共 20 项测试。
tmpl.js 中包含了最基本的测试:
importPackage(org.junit);
function name() {
return "Tmpl";
}
function execute() {
var movies = [
{ Name: "The Red Violin", ReleaseYear: "1998" },
{ Name: "Eyes Wide Shut", ReleaseYear: "1999" },
{ Name: "The Inheritance", ReleaseYear: "1976" }
];
context.html = pure.render("test", function($) {
$("#movie-tmpl" ).tmpl(movies).appendTo("#b");
context.result = [ $("#b p").length, $("#b p b").html() ];
});
}
function verify() {
var result = context.result;
Assert.assertEquals(3, result[0], 0);
Assert.assertEquals("The Red Violin", result[1]);
}
excute 函数将数据 movies 注入到模板 movie-tmpl 中,并添加到 id 为 b 的 div 中。
对于如下输入:
<div id="a" name="pre-middle-aend" words="word xxx">
<div id="b" name="pre" words="xxxx word" class="clz">
<h1 id="h1">John</h1>
</div>
</div>
<script id="movie-tmpl" type="text/x-jquery-tmpl">
<p><b>${Name}</b> (${ReleaseYear})</p>
</script>
将得到如下结果:
<DIV id="a" name="pre-middle-aend" words="word xxx">
<DIV class="clz" id="b" name="pre" words="xxxx word">
<H1 id="h1">John</H1>
<P><B>The Red Violin</B> (1998)</P>
<P><B>Eyes Wide Shut</B> (1999)</P>
<P><B>The Inheritance</B> (1976)</P>
</DIV>
</DIV>
[size=medium][align=center][b]Child Page[/b][/align][/size]
Child Page 的测试实际上是包含在 TemplateTest 中的。我们可以用两种方式实现页面组装:
方式一:
1.对子页面进行渲染,渲染目标为一个临时的 div,渲染完成后将 body 的内容替换为这个临时的div。
2.将子页面渲染结果插入到父页面中的指定位置
[b]childPage.js[/b]
importPackage(org.junit);
function name() {
return "Child Page";
}
function execute() {
var htmlData = { htmlBody: '<b>HTML</b>' },
textData = { textBody: 'Text' };
var childHtml = pure.render(true, "child", function($) {
var $html = $("#child-html-tmpl" ).tmpl(htmlData),
$text = $("#child-text-tmpl" ).tmpl(textData)
var $target = $("#target").append($html).append($text);
$("body").html($target.html());
});
context.html = pure.render("test", function($) {
$("#b").append(childHtml);
context.elems = $("#b p b");
});
}
function verify() {
var elems = context.elems;
Assert.assertEquals(2, elems.length, 0);
Assert.assertEquals("HTML", pure.html(elems[0]));
Assert.assertEquals("Text", pure.html(elems[1]));
}
输入:
[b]test.html[/b]
<div id="a" name="pre-middle-aend" words="word xxx">
<div id="b" name="pre" words="xxxx word" class="clz">
<h1 id="h1">John</h1>
</div>
</div>
[b]child.html[/b]
<div id="target"></div>
<script id="child-html-tmpl" type="text/x-jquery-tmpl">
<p>{{html htmlBody}}</p>
</script>
<script id="child-text-tmpl" type="text/x-jquery-tmpl">
<p><b>${textBody}</b></p>
</script>
输出:
<DIV id="a" name="pre-middle-aend" words="word xxx">
<DIV class="clz" id="b" name="pre" words="xxxx word">
<H1 id="h1">John</H1>
<P><B>HTML</B></P>
<P><B>Text</B></P>
</DIV>
</DIV>
方式二:
1.将子页面的模板添加到父页面中
2.在父页面中统一进行渲染
[b]childTemplate.js[/b]
importPackage(org.junit);
function name() {
return "Child Template";
}
function execute() {
var htmlData = { htmlBody: '<b>HTML</b>' },
textData = { textBody: 'Text' };
var childTmpl = pure.render(true, "child-tmpl");
context.html = pure.render("test", function($) {
$("body").append(childTmpl);
var $html = $("#child-html-tmpl" ).tmpl(htmlData),
$text = $("#child-text-tmpl" ).tmpl(textData)
$("#b").append($html).append($text);
context.elems = $("#b p b");
});
}
function verify() {
var elems = context.elems;
Assert.assertEquals(2, elems.length, 0);
Assert.assertEquals("HTML", pure.html(elems[0]));
Assert.assertEquals("Text", pure.html(elems[1]));
}
输入:
test.html (同上,略)
[b]child-tmpl.html[/b]
<script id="child-html-tmpl" type="text/x-jquery-tmpl">
<p>{{html htmlBody}}</p>
</script>
<script id="child-text-tmpl" type="text/x-jquery-tmpl">
<p><b>${textBody}</b></p>
</script>
输出:(同上,略)