One.js 文档

前言

One.js 是一个用来开发 App 的前端框架,目标是让使用 HTML5 开发 App 变得更容易,也更容易上手使用。

准备工作

点击这里获取 最新版本。

将获得的文件引入到您的项目中,建议项目的目录结构是以下这样子的,否则有可能无法正常使用:

字体图标

One.js 使用的是 ionic 的字体图标。详情请参见这里

起始页

One.js 是一个“单页无刷新”框架。

在项目的根目录下(与“js”文件夹同级)创建一个名为 index.htm 的文件作为起始页,引入所需的 js 和 css 文件,然后在 body 标签尾部加入应用程序的初始化代码:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width" />
<title>Hello OneJS</title>
<!-- 引入 one.css -->
<link href="css/one.css" rel="stylesheet" />
<!-- 引用 one.js -->
<script src="js/one.js"></script>
<!-- One.js 是基于 require.js 的 -->
<script src="js/require.js"></script>
</head>
<body>
<script type="text/javascript">
// 初始化
$O.init();
</script>
</body>
</html>

$O.init() 是在初始化应用程序,并默认加载 index 窗体(Form)。可以为 $O.init() 方法传递一个配置参数指定加载哪个 Form:

$O.init({
start:'another' // 在初始化完成后加载 “another” 窗体
});

创建Form

One.js 中,每个页面均为一个“窗体”(Form),由一个与窗体名同名的 模板文件 和 一个与窗体名同名的 .js 文件组成。每个窗体的文件也必须放在一个与窗体名同名的文件夹中。

现在,我们来创建一个应用程序初始化后加载的第一个 Form,名为 index

在 js 文件夹的同级目录下创建一个名为 index 的文件夹,并在该文件夹下创建一个名为 index.htm 的模板文件:

<div>这是index模板</div>

默认模板文件的后缀为 .htm,若想使用其它后缀,可在 $O.init() 的参数中指定。例如指定模板文件的后缀为 .html

$O.init({
templateExtension:'.html' // 指定模板文件的后缀为 .html
});

然后在 index 目录下再创建一个名为 index.js 的 js 文件,就是 RequireJS 定义模块的语法:

define({
onRender:function(){} // Form 渲染完成后会执行 onRender() 方法
});

onRender 方法中可对窗体的 DOM 进行操作、以及事件绑定等等。

在浏览器中运行:

对 Form 的 DOM 进行操作及绑定事件

每个 Form 的 js 文件中的 onRender 方法会 Form 渲染后执行。可在此对 DOM 进行操作及绑定事件。

将上个例子中的 index 窗体的模板文件 index.htm 修改为:

<div>
这是index模板
<div>
<!-- 在单页多窗体应用中,一般不推荐用 id 来标识元素,以免引起混淆 -->
<button name="btn">点我</button>
</div>
<div name="div"></div>
</div>

然后将 index 窗体的 js 文件 index.js 修改为:

define({
onRender: function () { // 窗体渲染后会执行 onRender
// 在 one.js 中推荐使用 this.get(selector) 来查找 DOM 元素
// this.get(selector) 会只在当前窗体内查找
var btn = this.get('[name=btn]'),
div = this.get('[name=div]'),
num = 0;
// 在 one.js 中推荐使用 $O.on(ele, eventName, handler) 给元素绑定事件
// 第一个参数为需要绑定事件的元素,
// 第二个参数为事件名,如 tap、click 等,
// 第三个参数为事件处理函数。
// 在 App 开发中推荐使用 tap 事件替代 click 事件
$O.on(btn, 'tap', function () {
num++;
div.html('点击次数:' + num);
});
}
});

在 Form 之间切换

在 App 中,切换到另一个窗体时,新的窗体一般是从右侧滑入的。

将上个例子中的 index 窗体的模板文件 index.htm 修改为:

<div>
这是index模板
<div>
<button name="btn">切换到第二个窗体</button>
</div>
</div>

然后将 index 窗体的 js 文件 index.js 修改为:

define({
onRender: function () {
var btn = this.get('[name=btn]');
$O.on(btn, 'tap', function () {
$O.Form.change('second'); // 切换到一个名为 second 的窗体
});
}
});

添加一个名为 second 的窗体:在 js 文件夹的同级目录下创建一个名 second 的文件夹,并在此文件夹下分别创建一个名为 second.htm 的模板文件和一个名为 second.js 的 js 文件。

将模板文件 second.htm 的内容修改为:

<div>
这是second模板
<div>
<button name="btn">返回</button>
</div>
</div>

second.js 的内容修改为:

define({
onRender: function () {
var btn = this.get('[name=btn]');
$O.on(btn, 'tap', function () {
$O.Form.back(); // 返回到上一个窗体
});
}
});

在切换到另一个窗体时,还可传递参数过去。

将上个例子中的 index 窗体的模板文件 index.htm 修改为:

<div>
输入:<input name="txt" type="text" maxlength="10"/>
<div>
<button name="btn">切换到第二个窗体</button>
</div>
</div>

然后将 index 窗体的 js 文件 index.js 修改为:

define({
onRender: function () {
var btn = this.get('[name=btn]'),
txt = this.get('[name=txt]');
$O.on(btn, 'tap', function () {
// $O.Form.change() 的第二个参数为一个 object,
// 其中 params 为需要传递给另一个窗体的参数
$O.Form.change('second', {
params: {
txt: txt.value
}
});
});
}
});

second 窗体的模板文件 second.htm 修改为:

<div>
上个窗体中文本框中输入的值为:<span name="span"></span>
<div>
<button name="btn">返回</button>
</div>
</div>

second.js 的内容修改为:

define({
// onRender 方法接收一个参数,该参数为从上个窗体中传入的参数
onRender: function (params) {
// 在 one.js 中,每个 HTMLElement 都有 html() 方法,用来设置或获取该元素的内容
// 若传参数,则表示是设置元素的内容,若不传参数,则表示是获取元素的内容
this.get('[name=span]').html(params.txt);
var btn = this.get('[name=btn]');
$O.on(btn, 'tap', function () {
$O.Form.back(); // 返回到上一个窗体
});
}
});

页首

在 App 的窗体中一般会有一个“页首”,一般用来放置“标题”和“返回按钮”。你当然可以按照你自己的需求设计任意样式的页首,但 one.js 提供了一种常见的页首的样式。

将上个例子中的 index 窗体的模板文件 index.htm 修改为:

<div>
<!-- o-bar 表示这是一个 bar。bar 是一个横条状的元素 -->
<!-- o-header 表示这是一个页首,必须与 o-bar 结合使用 -->
<!-- o-bar-fresh 定义了这个 bar 的样式风格 -->
<div class="o-bar o-header o-bar-fresh">
<!-- 返回按钮,默认是隐藏的 -->
<button name="back" class="ion-ios-arrow-back"></button>
<!-- 显示标题的 div -->
<div name="title"></div>
<!-- 还可在此加入更多元素,它将显示在“页首”栏的右侧位置 -->
</div>
输入:<input name="txt" type="text" maxlength="10"/>
<div>
<button name="btn">切换到第二个窗体</button>
</div>
</div>

然后将 index 窗体的 js 文件 index.js 修改为:

define({
onRender: function () {
// 用 new $O.Header(ele) 创建一个 Header 的实例
var header = new $O.Header(this.get('.o-header'));
// 设置页首的标题
header.setTitle('第一个窗体');
// 设置页首标题的对齐方式,接受的值还可是 'left'、'right'
header.setTitleAlign('center');
var btn = this.get('[name=btn]'),
txt = this.get('[name=txt]');
$O.on(btn, 'tap', function () {
$O.Form.change('second', {
params: {
txt: txt.value
}
});
});
}
});

如果希望 second 窗体也有同样的“页首”,则在 second 窗体的模板文件 second.htm 中也加入同样的代码:

<div>
<div class="o-bar o-header o-bar-fresh">
<!-- 返回按钮,默认是隐藏的 -->
<button name="back" class="ion-ios-arrow-back"></button>
<!-- 显示标题的 div -->
<div name="title"></div>
</div>
上个窗体中文本框中输入的值为:<span name="span"></span>
<div>
<button name="btn">返回</button>
</div>
</div>

second.js 的内容修改为:

define({
onRender: function (params) {
// 用 new $O.Header(ele) 创建一个 Header 的实例
var header = new $O.Header(this.get('.o-header'));
// 设置标题
header.setTitle('第二个窗体');
// “返回按钮”默认是隐藏的,用 showBack() 将其显示出来
header.showBack();
// 设置点击“返回按钮”后执行的方法
header.setBackEventHandler(function () {
$O.Form.back();
}); this.get('[name=span]').html(params.txt);
var btn = this.get('[name=btn]');
$O.on(btn, 'tap', function () {
$O.Form.back();
});
}
});

在这个例子中,虽然两个窗体的页首是一样的,但还是在两个窗体的模板页中分别写入了相同的代码。在 one.js 中提供了另一种方式可以让多个窗体使用同一个“页首”代码。

在项目中新建一个名为 header 的文件夹,其与 js 文件夹是同级目录,并在该文件夹下创建一个名为 header.htm 的模板文件:

<div class="o-bar o-header o-bar-fresh">
<button name="back" class="ion-ios-arrow-back"></button>
<div name="title"></div>
</div>

并在该文件夹下创建一个名为 index.js 的文件:

define({
// 在这个示例中这里不需要任何代码
});

这样我们的通用“页首”就定义好了。

将上个例子中的 index 窗体的模板文件 index.htm 恢复为原样:

<div>
输入:<input name="txt" type="text" maxlength="10"/>
<div>
<button name="btn">切换到第二个窗体</button>
</div>
</div>

然后将 index 窗体的 js 文件 index.js 修改为:

define({
// 定义当前窗体使用上面定义的“页首”
// 这里的值是一个指向前面定义的“页首”的路径,
// 第一个单词 'root' 表示是从 js 文件夹同级目录开始
header: 'root/header',
onRender: function () {
// 设置页首的标题
this.header.setTitle('第一个窗体');
// 设置页首标题的对齐方式
this.header.setTitleAlign('center');
var btn = this.get('[name=btn]'),
txt = this.get('[name=txt]');
$O.on(btn, 'tap', function () {
$O.Form.change('second', {
params: {
txt: txt.value
}
});
});
}
});

second 窗体的模板文件 second.htm 中也恢复为原状:

<div>
上个窗体中文本框中输入的值为:<span name="span"></span>
<div>
<button name="btn">返回</button>
</div>
</div>

second.js 的内容修改为:

define({
// 与 index 窗体使用的“页首”是同一个
header: 'root/header',
onRender: function (params) {
// 设置标题
this.header.setTitle('第二个窗体');
this.get('[name=span]').html(params.txt);
var btn = this.get('[name=btn]');
$O.on(btn, 'tap', function () {
$O.Form.back();
});
}
});

second 窗体中我们并没有明确地用代码将“页首”的“返回按钮”显示出来,但它仍然可以显示。使用这种方式定义的页首,它会自动判断是否应该显示“返回按钮”。

小技巧:定义自己的“页首”风格
在上面的例子中,“页首”使用的是 o-bar-fresh 风格。如果需要使用自定义的样式,可以这样做:
在页面中定义一个名为 .o-bar-my 的样式,在此定义背景色、字体色等。
还可定义一个名为 .o-bar-my [name=back] 的样式以设置“返回按钮”的样式。
然后用 .o-bar-my 替换掉“页首”代码中的 o-bar-fresh

母版页

如果多个窗体有一部分内容是相同的,可以不用在每个窗体中重复写同样的代码,可以使用“母版页”。

定义一个母版页,凡是使用了这个母版页的窗体都将会拥有母版页中的内容。

首先定义一个名为 master 的母版页。在 js 文件夹的同级目录下创建一个名为 master 的文件夹,在该文件夹下创建一个名为 master.htm 的模板文件:

<div>这是母版页的一部分</div>
<!-- 母版页中的 place 标签是一个“占位块”, -->
<!-- 表示这里将由使用了该母版页的窗体中的内容填充 -->
<place name="place1"></place>
<div>这是母版页的另一部分</div>

并在该文件夹下创建一个名为 master.js 的文件:

define({
// 在这个示例中这里不需要任何代码
});

将上个例子中的 index 窗体的模板文件 index.htm 修改为:

<!-- 此处 place 中的内容将会替换母版页中同名的 place -->
<place name="place1">
<div>这是index窗体</div>
<button name="btn">切换到第二个窗体</button>
</place>

然后将 index 窗体的 js 文件 index.js 修改为:

define({
// 定义当前窗体的母版页
// 这里的值是所使用的母版页的路径,
// 第一个单词 'root' 表示是从 js 文件夹同级目录开始
extend: 'root/master',
onRender: function () {
var btn = this.get('[name=btn]');
$O.on(btn, 'tap', function () {
$O.Form.change('second');
});
}
});

second 窗体的模板文件 second.htm 修改为:

<!-- 此处 place 中的内容将会替换母版页中同名的 place -->
<place name="place1">
<div>这是second窗体</div>
<button name="btn">返回</button>
</place>

second.js 的内容修改为:

define({
// 定义当前窗体的母版页
// 这里的值是所使用的母版页的路径,
// 第一个单词 'root' 表示是从 js 文件夹同级目录开始
master: 'root/master',
onRender: function () {
var btn = this.get('[name=btn]');
$O.on(btn, 'tap', function () {
$O.Form.back();
});
}
});

Tabs Form

index 窗体的模板文件 index.htm 删掉,因为 Tabs Form 不需要模板文件,然后将 index.js 修改为:

define({
header:'root/header',
tabs:{ // 当前窗体是一个 Tabs Form
tabs: [ // 每个“选项”的定义
{
label: '首页', // 标题
icon: 'ion-ios-home-outline', // 图标
iconActive: 'ion-ios-home', // 选中状态时的图标
target: 'default' // 选中后显示的窗体
},
{
label: '秒杀',
icon: 'ion-ios-alarm-outline',
iconActive: 'ion-ios-alarm',
target: 'snapped'
},
{
label: '购物车',
icon: 'ion-ios-cart-outline',
iconActive: 'ion-ios-cart',
target: 'cart'
}
]
}
});

然后分别定义 default、snapped、cart 窗体:

default.htm
<div>
这是首页
<div>
<button name="btn">切换到第二个窗体</button>
</div>
</div>
default.js
define({
onRender: function () {
var btn = this.get('[name=btn]');
$O.on(btn, 'tap', function () {
$O.Form.change('second');
});
},
onActive: function () { // 在“选项卡”切换到当前窗体后执行此方法
this.header.setTitle('首页');
this.header.setTitleAlign('center');
}
});
snapped.htm
<div>这是秒杀</div>
snapped.js
define({
onActive: function () {
this.header.setTitle('秒杀');
this.header.setTitleAlign('center');
}
});
cart.htm
<div>这是购物车</div>
cart.js
define({
onActive: function () {
this.header.setTitle('购物车');
this.header.setTitleAlign('center');
}
});

然后创建 second 窗体:

second.htm
<div>
<div>这是第二个窗体</div>
<button name="btn">返回</button>
</div>
second.js
define({
header: 'root/header',
onRender: function () {
this.header.setTitle('第二个窗体');
var btn = this.get('[name=btn]');
$O.on(btn, 'tap', function () {
$O.Form.back();
});
}
});

还可以设置 Tabs 的切换方式。

index 窗体的 index.js 修改为:

define({
header:'root/header',
tabs:{
effect: 'slide', // 从右侧滑入的切换方式
tabs: [
{
label: '首页',
icon: 'ion-ios-home-outline',
iconActive: 'ion-ios-home',
target: 'default'
},
{
label: '秒杀',
icon: 'ion-ios-alarm-outline',
iconActive: 'ion-ios-alarm',
target: 'snapped'
},
{
label: '购物车',
icon: 'ion-ios-cart-outline',
iconActive: 'ion-ios-cart',
target: 'cart'
}
]
}
});

还可设置是否允许用拖动的方式切换,将 index 窗体的 index.js 修改为:

define({
header:'root/header',
tabs:{
effect: 'slide', // 从右侧滑入的切换方式
allowDrag: true, // 允许以拖动的方式切换 tabs: [
{
label: '首页',
icon: 'ion-ios-home-outline',
iconActive: 'ion-ios-home',
target: 'default'
},
{
label: '秒杀',
icon: 'ion-ios-alarm-outline',
iconActive: 'ion-ios-alarm',
target: 'snapped'
},
{
label: '购物车',
icon: 'ion-ios-cart-outline',
iconActive: 'ion-ios-cart',
target: 'cart'
}
]
}
});

最后,来个广告

若你觉得此文不错,请分享,若认为尚需改进,请点讚。

结束语