english | 简体中文
一个移动端框架,支持页面的多种形式切换,页面转场,页面传值,通知等,适用于开发单页面应用(SPA),混合开发(Hybrid),Cordova开发。
期初刚接触Hybrid开发的时候公司选用的是jQueryMobile+Cordova的组合来开发混合应用,在用jQueryMobile的时候遇到了很多问题如管理类和Dom之间总是不能很好的有机结合在一起,当初的想法是如果在浏览器端每个局部页面和其管理类能像Android中的Activity一样就好了,所以灵感就来了,CtMobile的实现完全借助于Android中的Activity来实现。
CtMoble中有三个重要的感念,分别是Page,Router,BorasdCast. 其中Page用来管理页面的创建,初始化,销毁的整个生命周期,Router管理这个框架的路由跳转,BorasdCast用来管理通知和页面之间的数据的通信交互。
页面之间切换支持多种过度效果
$ npm install ctmobile --save
<script src="https://unpkg.com/ctmobile@^2/umd/ctmobile.min.js"></script>
<div ct-data-role="page" id="index"></div>
具有ct-data-role="page"属性的元素代表一个基本的页面, id属性唯一标识这个页面,需要注意的是具有ct-data-role="page"属性的元素必须为body的子元素,不能是任意级别的元素。还需要注意的是html中至少含有一个Page的结构来代表第一个显示的页面内容
import CtMobile from "ctmobile";
const Router = {
index: {
url: "/static/html/index.html",
component: import(/* webpackChunkName: "index" */ "../pages/index"),
},
info: {
url: "/static/html/info.html",
component: import(/* webpackChunkName: "info" */ "../pages/info"),
},
about: {
url: "/static/html/about.html",
component: import(/* webpackChunkName: "about" */ "../pages/about"),
},
};
const App = CtMobile.CtMobileFactory.create({
supportCordova: false,
linkCaptureReload: false,
router: Router,
});
详细参数解释请参考属性配置。
在初始化应用的代码中需要配置router选项,router是一个对象,对象的键需要和基本结构中id属性的值保持一致,值为一个对象,有两个属性url和component
component: import(/* webpackChunkName: "about" */ "../pages/about")
import CtMobile from 'ctmobile';
export default class extends CtMobile.Page {
constructor(ctmobile, id) {
super(ctmobile, id);
}
/**
* @override
*/
pageCreate(){
console.log('页面初始化');
}
/**
* @override
*/
pageShow() {
console.log('page的DOM显示时调用');
}
/**
* @override
*/
pageBeforeDestory(){
console.log('page的DOM销毁之前调用');
}
}
编写一个类继承自Page类即可完成一个页面的定义,其中构造函数会有两个参数,ctmobile和id,其中ctmobile代表整个App的实例,id代表Page基本机构中的id属性值。 其中pageCreate,pageShow和pageBeforeDestory是Page的生命周期函数,更多生命周期函数请参考Page的生命周期
跳转到一个新页面可以有两种方式
<a ct-pageId="info">跳转到info页面</a>
在a标签中使用ct-pageId属性就可以跳转到一个新的页面,其中ct-pageId的值为Page基本机构中id的值。
this.getCtMobile().startPage("/static/html/info.html?pageId=info");
需要注意的是html路径后会有一个pageId的参数,参数值是Page基本结构中id的值
字符串方式
<a ct-pageId="about" ct-parameter="&a=1&b=2"></a>
this.getCtMobile().startPage("/static/html/info.html?pageId=info&a=1&b=2");
内存方式 通过调用Page类的setRequest方法进行参数传递,在目标页面调用Page类的getRequest方法获取参数,使用内存方式的好处是可以在页面之间传递任何数据类型的数据,缺点是如果直接刷新此页的话不会保存上一回的数据,不像字符串方式可以永久保留参数的值
A.js
<!-- 向B.html传递参数 -->
this.setRequest('requestCode',{a:1,b:2});
this.ctmobile.startPage("/static/html/b.html?pageId=b");
B.js
pageAfterShow() {
<!-- 获取A.html传递过来的参数 -->
const parameter = JSON.stringify(this.getRequest());
console.log('parameter',parameter);
}
需要注意的是需要在pageAfterShow的回调中调用getRequest方法,只要pageAfterShow函数被调用,之后在任何地方在调用getRequest方法都可以获取到参数。
页面的基本结构中加入ct-page-mode="result"或者ct-page-mode="singleInstanceResult"属性
举个例子,当前有两个页面index.html,PopUpDialog.html两个页面。index.html中有个弹出按钮,点击按钮弹出PopUpDialog页面
index.html定义
<div ct-data-role="page" id="index">
<a ct-pageId="PopUpDialog">弹出PopUpDialog</a>
<div class="resultText">PopUpDialog的返回值<div>
</div>
index.js定义
import CtMobile from 'ctmobile';
import $ from 'jquery';
export default class extends CtMobile.Page{
constructor(ctmobile,id){
super(ctmobile,id);
}
/**
* override
*/
pageCreate() {
}
/**
* PopUpDialog返回时触发
* override
*/
pageResult(e, resultCode, bundle) {
console.log("resultCode", resultCode, "bundle", JSON.stringify(bundle));
alert(`resultCode:${resultCode}\r\nbundle:${JSON.stringify(bundle)}`);
}
}
PopUpDialog的html定义
<div ct-data-role="page" id="PopUpDialog" ct-data-mode="result">
<button class="result">返回</button>
</div>
或者
<div ct-data-role="page" id="PopUpDialog" ct-data-mode="singleInstanceResult">
<button class="result">返回</button>
</div>
PopUpDialog.js定义
import CtMobile from 'ctmobile';
import $ from 'jquery';
export default class extends CtMobile.Page {
constructor(ctmobile,id){
super(ctmobile,id);
}
/**
* override
*/
pageCreate() {
const $btnEL = this.getPageJO().find(' .result');
$btnEl.on('click' , () => {
this.setResult('PopUpDialog', {a: 1, b: 2});
this.over();
});
}
}
index.html需要做的是在index.js中重写pageResult方法,此方法在PopUpDialog返回或手动调用finish方法后被触发,pageResult的有三个参数e,resultCode,bundle,其中resultCode用来区分不同的来源,bundle是被带回来的值。 PopUpDialog.html需要做的是在PopUpDialog.js中调用this.setResult(resultCode,bundle);方法来设置返回的值,在调用this.finish();方法后页面关闭。
带有返回值的页面使用场景一般分为两种
在多对一的情况下可以通过setRequest方法把父页面的标志传递过去。
在一对多的情况下可以通过pageResult方法的requestCode区分不同来源。
在页面的基本结构中设置ct-data-mode属性值即可,框架一共支持5中启动模式
多例模式就是通过配置或者api跳转到此页面的时候都会建立一个新的实例,所谓新的实例就是Dom和Dom对应的Page类都会是新的。
和Android中single一样,举个例子,加入有如下的页面开发顺序 : index.html -> a.html -> b.html -> c.html -> d.html -> b.html 如果把b.html的ct-data-mode设置为single,那么执行上述页面顺序后, 历史栈中当前是 index.html -> a.html -> b.html 也是删除了c.html和d.html,删除的同事也会调用相应的生命周期函数。 但是如果在b.html中点击返回那么b.html还是会销毁的。
完全单例就是在任何时候都不会被销毁且只有一个实例存在。
和result一样只是实例不会被销毁。
Page一共有10个生命周期函数
在页面的基本结构中设置ct-data-transition属性值即可,框架一共支持13种页面的过度效果
借鉴了Android中Borasdcast概念,为Page之间的数据传递提供了一系列功能,广播分为有序和无序,可以通过配置和api两种方式实现广播。
<div ct-page-role="page"
id="index"
ct-data-intentfilter-action="actionCode"
ct-data-intentfilter-categorys="c1,c2"
ct-data-intentfilter-priority="0"
></div>
import CtMobile from 'ctmobile';
export default class extends CtMobile.Page {
constructor(ctmobile,id){
super(ctmobile,id);
}
/**
* @override
*/
pageReceiver(intent) {
console.log(intent);
}
}
import CtMobile from 'ctmobile';
export default class extends CtMobile.Page {
constructor(ctmobile,id){
super(ctmobile,id);
}
onRegisterReceiver(intent) {
console.log(JSON.stringify(intent));
}
/**
* @override
*/
pageCreate() {
this.onRegisterReceiver = this.onRegisterReceiver.bind(this);
// 注册borasdcast
this.ctmobile.registerReceiver({
action: 'actionCode',
priority: 0,
categorys: ['c1','c2']
}, this.onRegisterReceiver);
}
}
this.ctmobile.sendBroadcast({
action: 'actionCode',
categorys: ['c1','c2'],
bundle: {
a: 1,
b: 2
}
});
this.ctmobile.sendOrderedBroadcast({
action: 'actionCode',
categorys: ['c1','c2'],
bundle: {
a: 1,
b: 2
}
});
<div ct-page-role="page"
id="index"
ct-data-intentfilter-priority="0"
></div>
使用api注册设置priority
// 注册borasdcast
this.ctmobile.registerReceiver({
action: 'actionCode',
priority: 0,
categorys: ['c1','c2']
}, this.onRegisterReceiver);
在有序广播的回调函数中会有2个参数intent和opt,其中intent是通知传递的参数,opt是个对象,其中有2个方法,putExtras和next,其中putExtras设置向下传递的参数,这些参数是合并在一起的。只有调用next方法才向下进行传递。
在注册广播的时候除了Action之外,还可以定义多个category,categorys可以认为是一个二级标题,作用是用来对Action进行细粒度的定义。
<a ct-pageId="a" ct-reload="true">a.html</a>
this.ctmobile.startPage('/static/html/a.html?pageId=a',{
reload:true
});
比如index.html -> a.html,那么历史栈中只有a.html
a标签不交由框架处理 有些时候我们不希望让框架来处理a标签的行为,此时就可以在a标签上加入ct-data-ajax="false"
ajax内容预加载
<div ct-data-role="page" id="index">
<a ct-pageId="a" ct-data-preload="true">into a.html</a>
<a ct-pageId="b" ct-data-preload="true">into b.html</a>
<a ct-pageId="c" ct-data-preload="true">into c.html</a>
<a ct-pageId="d" ct-data-preload="true">into d.html</a>
<a ct-pageId="e" ct-data-preload="true">into e.html</a>
</div>
框架会在初始化的时候就加载a-e.html的内容 如果a-e.html中还有需要预加载的页面,那框架还会进行预加载 每个页面只会被预加载一次,如果预加载完了以后就不会在被预加载了。
<div ct-data-role="page" id="about">
<header>
<a class="ct-back-icon" ct-data-rel="back"></a>
</header>
</div>
属性(property) | 取值 | 说明 |
---|---|---|
ct-data-role | page | 有此属性的元素代表一个页面 |
ct-data-rel | boolean | true的时候带有此属性的元素点击可以执行返回操作 |
ct-pageId | string | 用在标签上代表要加载页面的id |
ct-parameter | string | 用在标签上代表要传递的参数 |
ct-data-transition | slideleft | 从右到左(overlay) |
slideright | 从左到右(overlay) | |
slideup | 从下到上(overlay) | |
slidedown | 从上到下(overlay) | |
wxslideleft | 类似于微信的从右到左 | |
wxslideright | 类似于微信的从左到右 | |
wxslideup | 类似于微信的从下到上 | |
wxslidedown | 类似于微信的从上到下 | |
pushslideleft | 从右到左(push) | |
pushslideright | 从左到右(push) | |
pushslideup | 从下到上(push) | |
pushslidedown | 从上到下(push) | |
material(缺省) | Android Material的风格 | |
ct-data-mode | standard(缺省) | 多例 |
single | 单例(当点击返回时,会销毁) | |
singleInstance | 完全单例(不会被销毁) | |
result | 带有返回值的(可以向父页面带回返回值) | |
singleInstanceResult | 带有返回值的完全单例(不会被销毁,可以向父页面带回返回值) | |
ct-data-ajax | boolean | 是否交由框架处理a标签的跳转 |
ct-data-preload | boolean | 是否提前预加载a标签的href属性的页面 |
ct-reload | boolean | 是否改变window.history.length的数量 |
ct-data-intentfilter-action | string | 如果页面要订阅通知时的标识 |
ct-data-intentfilter-categorys | [string1 string2 …] | 订阅时的过滤参数 |
ct-data-intentfilter-priority | number 0(缺省) | 发送有序广播时的优先级,默认值是0 |
checkou后进入demo目录
$ npm install
$ npm run devDll
$ npm start
在浏览器上输入localhost:8000即可访问到demo的主页面
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
1. 开源生态
2. 协作、人、软件
3. 评估模型