简要介绍了 xmlplus 的下载安装和基本的开发模版,另外还给出了一个小游戏示例以及框架对浏览器和设备的支持情况描述。
如果你已经安装了 npm 客户端,可以通过 npm 安装 xmlplus:
$ npm install xmlplus
或者,你也可以通过 git 和 npm 使用如下的命令来安装:
$ git clone https://github.com/qudou/xmlplus.git && cd xmlplus && npm install
下面给出的是项目的基本组织结构:
xmlplus/
├── xmlplus.js
├── patch/
├── docs/
│ ├── getting-started/
│ ├── docs/
│ ├── components/
│ └── api/
└── example/
├── getting-started/
├── docs/
├── components/
└── api/
在根目录 xmlplus/ 下,xmlplus.js 是源文件,patch/ 下的两个文件是适用于 IE9+ 的补丁文件。目录 docs/ 和目录 example/ 包含同名的子级目录。目录 docs/ 包含框架的文档文件,example/ 包含与文档相关的配套示例代码。
由于 xmlplus 是一个既可以在浏览器端运行,又可以在服务端运行的框架,所以这里将给出两套基本的开发模板,分别对应两端的运行环境。
下面首先给出的是能够在服务端直接运行的开发模版。你可以在此基础上修正或者扩展相关功能。假定你已经通过 npm 安装了 xmlplus 软件包,那么服务端的模板非常简单,仅需包含有如下内容的一个文件就可以了。
// 02-01
const xmlplus = require("xmlplus");
xmlplus("xp", function (xp, $_) {
$_().imports({
Index: {
fun: function (sys, items, opts) {
console.log("hello, world");
}
}
});
}).startup("//xp/Index");
现在假定有一个包含了如上内容的文件,其名称为 index.js,那么你可以在安装了 Node.js 的环境中执行命令 node index 来运行上述的模板示例。
另外,请注意示例开头的一个注释 02-01。通过该注释内容,你可以定位到软件包目录 /example/docs/02-templete/01/ 以获取该源码。注释中的 02 即章节序,01 就是示例所在目录的名称。下面的给出示例包含了类似的注释,其意义与此处一致。
在浏览器端,你需要准备三个文件,其中第一个是你已下载的软件包中的 xmlplus.js 文件。另外你需要创建一个名为 index.js 的文件,该文件包含如下内容。
// 02-02
xmlplus("xp", function (xp, $_) {
$_().imports({
Index: {
css: "#text { color: red; }",
xml: "<h1 id='text'>hello, world</h1>",
fun: function (sys, items, opts) {
sys.text.css("font-size", "28px");
}
}
});
});
除此以外,你还需要一个 HTML 文件,该文件包含的内容如下所示。这里命名该文件为 index.html。
<!-- 02-02 -->
<!DOCTYPE html>
<html>
<head>
<script src="xmlplus.js"></script>
<script src="index.js"></script>
</head>
<body>
<i:Index xmlns:i="//xp"></i:Index>
</body>
</html>
确保上述的三个文件位于同一个目录,通过浏览器打开 index.html,你应该可以看到红色的、字体大小为 28 个像素的 hello, world 文本。
井字棋(TicTacToe)是一个老少皆宜的小游戏,我们来看看如何设计与实现它。下面会涉及不少与 xmlplus 相关的知识点,当然你不需要都看懂,有个大概感觉就好了。当你学完《文档》的相关内容后,再来看看这一节,也许会有新的收获。
我们从最简单的地方开始,先实现游戏界面网格的方块组件。方块组件非常简单,仅需要一个 div 元素和一些样式就好了,具体请看下面的代码:
// 03-01
Square: {
css: "#square { line-height: 48px; width: 48px; height: 48px; text-align: center; }\
#square { margin-right: -1px; margin-top: -1px; padding: 0; border: 1px solid #999;}",
xml: "<div id='square'/>",
}
这个方块组件的长宽都是 48px,另外设置的值均为 -1 的 margin-right 和 margin-top 是为布局服务的,用于防止出现重复的边框。
此组件是一个简单的 JSON 对象,其中 xml 子项叫做视图项,css 子项叫做样式项,它们之间通过视图项中的 div 元素的 id 值联系起来。
这里我们利用上面实现的 9 个方块组件组合成一个网格组件。具体请看下面的代码:
// 03-01
Board: {
css: "#board { width: 148px; height: 148px; margin: 0 auto; }\
#board div { float: left; font-size: 36px; font-weight: bold; }",
xml: "<div id='board'>\
<Square id='0'/><Square id='1'/><Square id='2'/>\
<Square id='3'/><Square id='4'/><Square id='5'/>\
<Square id='6'/><Square id='7'/><Square id='8'/>\
</div>",
fun: function (sys, items, opts) {
let curr, locked;
sys.board.on("click", "//Square", function() {
if (locked || this.text() != '') return;
curr = this.text(curr && curr.text() == 'O' ? 'X' : 'O');
curr.notify("board-change", [parseInt(this + '')]);
});
this.watch("game-start", e => {
locked = false;
sys.board.kids().call("text", '');
});
this.watch("winner", e => locked = true);
}
}
观察该组件的视图项部分,每一个方块都被按顺序编了号。再看此网格组件的样式项部分可知,该组件的长宽均被设置为 148px,这是足够的,因为组件 Square 的 margin-right 和 margin-top 均被设置为 -1。
注意该组件内部比方块组件多出了一个名为 fun 的子项,该子项叫做函数项,函数项包含了实现一些组件对象的初始化代码。此组件的函数项包含了方块的点击事件的响应代码以及对两个消息的侦听代码。对于具体的细节,可以先不理解。在读完本节内容后,你应该特别留意各组件对象之间是如何通过事件与消息之间传递来协作完成任务的。
为了简化网格组件的内部逻辑,我们把胜负的判断独立出一个组件,该组件与网格组件之间的信息交互由系统的消息通信机制来实现。
// 03-01
Winner: {
fun: function (sys, items, opts) {
let squares = Array(9).fill(null);
const lines = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6],[1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]];
this.watch("board-change", (e, i) => {
squares[i] = e.target.text();
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c])
return this.notify("winner", squares[a]);
}
}).watch("game-start", e => squares = Array(9).fill(null));
}
}
该组件对胜负的判断非常的机械化,但却是有效的。它的做法是遍历三横三纵和两对角线,看看是否有连成一线的,如果有的话派发一个消息后就返回。
消息显示面板的作用主要是给出游戏过程中的一些文字提示,以及提供一个可以重新开始游戏的按钮。
// 03-01
Info: {
xml: "<div id='info'>\
<div id='next'>Next player: X</div>\
<a id='start' href='javascript:void(0)'>Game start</a>\
</div>",
fun: function (sys, items, opts) {
this.watch("board-change", (e, i) => {
let o = e.target.text() == 'O' ? 'X' : 'O';
sys.next.text("Next player: " + o);
}, 1);
this.watch("winner", (e, winner) => {
sys.next.text("Winner: " + winner)
});
sys.start.on("click", e => this.notify("game-start"));
this.watch("game-start", e => sys.next.text('Next player: X'));
}
}
该组件内部同样是通过系统的消息通信机制与其它组件进行通信。当用户点击游戏开始按钮时,该组件会派发一个游戏开始的消息;当接收到判断胜负的组件发来的消息时,则会显示胜负的结果;另外该组件还侦听了网格组件的变化以及时给出下一个落子用户的提示。
有了上面实现的网格组件、胜负判断组件以及消息面板组件,我们通过简单的组合就可以得到我们预期的可工作的 TicTacToe 了。
// 03-01
TicTacToe: {
css: "#index { text-align: center; margin: 20px; }",
xml: "<div id='index'>\
<Board id='board'/>\
<Winner id='winner'/>\
<Info id='info'/>\
</div>",
map: { msgscope: true }
}
注意该组件内部多出了一个 map 子项,该子项叫映射项,它主要包含一个与组件相关的配置内容。上面包含为真值的 msgscope 用于隔离消息作用域,它可以防止消息之间污染。具体内容可以在 消息与通信 中找到。
对于服务端,xmlplus 支持所有版本的 node.js 运行环境。所以这里只讨论其在浏览器端的表现。xmlplus 在最新的桌面和移动浏览器上有最佳的表现。然而,在一些较老旧的浏览器上跑 xmlplus 可能会失败。
xmlplus 支持主流的浏览器以及平台。在 Windows 平台,其支持 Internet Explorer 9-11 / Microsoft Edge。请看下面列出的详细信息。
一般而言, xmlplus 支持各个主流平台的最新版本的浏览器。
| Chrome | Firefox | Safari | Android Browser & WebView | Microsoft Edge | |
|---|---|---|---|---|---|
| Android | 支持 | 支持 | N/A | Android v4.0+ 支持 | N/A |
| iOS | 支持 | 支持 | 支持 | N/A | N/A |
| Windows 10 Mobile | N/A | N/A | N/A | N/A | 支持 |
相应的,对于大部分的最新版本的桌面浏览器,xmlplus 也是支持的。
| Chrome | Firefox | Internet Explorer | Microsoft Edge | Opera | Safari | |
|---|---|---|---|---|---|---|
| Mac | 支持 | 支持 | N/A | N/A | 支持 | 支持 |
| Windows | 支持 | 支持 | 支持, IE9+ | 支持 | 支持 | 不支持 |
前面提到 xmlplus 对 Internet Explorer 9-11 提供支持,不过这需要额外引入两个文件,xpath.js 以及 xmldom.js。
这两个文件可在安装包的目录 src/patch/ 中找到。也就是说,如果你想在 Internet Explorer 9-11 中运行基于 xmlplus 的应用,你需要导入三个 js 文件。
<script src='xpath.js'></script>
<script src='xmldom.js'></script>
<script src='xmlplus.js'></script>
IE 浏览器注定会成为过去。如果你还在使用它,强烈建议你换成体验性更好的新版本的 Chrome、Firefox、Oprea、Safari 或者 Microsoft Edge。