JSBox-Component教程——写一个TabLayout
什么是JSBox?
JSBox 是一个可以用来运行 JavaScript
脚本的 iOS 应用,你可以通过他来执行标准的 JavaScript 脚本。
这种执行不是指跑在浏览器上,而是执行在一个完全原生的环境,效率很高。
并且提供了很多 iOS 原生的接口,这意味着你可以通过他做很多事情,包括但不限于:
写一个用来查询汇率的脚本;
写一个用于计算小费的脚本;
通过接口实现一个小小的应用,提供丰富的界面;
写一个文本收藏工具,用于收藏常用的文字;
写一个下载 Twitter 视频的小工具;
——引用自少数派:JSBox: 一个创造工具的工具
什么是JSBox-Component?
JSBox-Component是一个在JSBox中实现ui组件化的库,可以将页面拆分为不同的组件从而复用。
此教程将带你快速上手JSBox-Component
这个库,提高开发效率😎
结果展示
{
type: TabLayout,
props: {
index: 1,
tabs: [
{
title: "Home",
icon: "102"
},
{
title: "Favorite",
icon: "061"
},
{
title: "Settings",
icon: "002"
}
]
},
layout(make, view) {
make.bottom.right.left.equalTo(view.super.safeArea);
make.height.equalTo(50);
},
events: {
onTabChanged: (index, tab) => {
console.log(index, tab);
}
}
}
通过以上组件对象
在屏幕上创建一个TabLayout
,并拥有以下功能:
传入一个
tabs
属性指定每一个tab的文本和图标可以通过传入
index
属性指定一开始选中的tab可以通过传入
tabColor
和tabSelectedColor
属性指定tab未选中和选中时的颜色通过
onTabChanged
事件可以监听当前选中tab是否改变通过更改
index
属性可以改变当前选中的tab
创建项目
首先从Release中下载最新版本的component.js
创建一个空白项目,新建一个lib
文件夹,把component.js
放到里面
接着创建一个components
文件夹,里面存放该项目的自定义组件
jsbox-tablayout-demo
│
│ main.js
│ config.json
│
├─assets
│
├─components
│
├─lib
│ component.js
│
├─scripts
│
└─strings
编写组件(显示部分)
在components
目录下新建TabLayout.js
首先从component.js
导入defineComponent
方法,这个方法可以用来定义一个组件
const { defineComponent } = require("../lib/component");
接着导出这个组件
const { defineComponent } = require("../lib/component");
module.exports = defineComponent();
现在就可以开始编写组件了
给defineComponent
传入一个对象,可以包含以下属性:
name
name
属性就是这个组件的名字,和文件名保持一致即可
props
props
属性定义这个组件接收哪些属性值,这里就是index
, tabs
, tabColor
, tabSelectedColor
这四项,后面跟上这些属性的默认值。
index
默认为0,代表默认第一个tab被选中
events
events
是一个数组,包含了该组件的事件名,这里就只有onTabChanged
事件。
methods
methods
定义了组件在页面上的实例可以被调用的方法,这里只有一个changeTab
方法,用来改变选中的tab,具体的函数之后再写。
watch
watch
可以监听属性值的变化,这里暂时用不着。
const { defineComponent } = require("../lib/component");
module.exports = defineComponent({
name: "TabLayout",
props: {
index: 0, // 默认选中第一项
tabs: [],
tabColor: $color("gray"), // 默认未选中色为灰色
tabSelectedColor: $color("black") // 默认选中色为黑色
},
events: ["onTabChanged"],
methods: {
changeTab() {
// 留位置给之后写具体逻辑
}
},
});
编写render方法
render
方法是一个组件的核心,它将输入的组件对象
转换成一个JSBox原生控件对象
并返回,这样才能够在页面上渲染出来组件。
首先我们写一个普通的TabLayout
,是这样子的
{
type: "matrix",
props: {
columns: 1,
itemHeight: 60,
spacing: 0,
scrollEnabled: false,
bgcolor: $color("clear"),
template: [
{
type: "image",
props: { id: "icon", bgcolor: $color("clear") },
layout(make, view) {
make.centerX.equalTo(view.super);
make.width.height.equalTo(25);
make.top.inset(7);
}
},
{
type: "label",
props: { id: "title", font: $font(10) },
layout(make, view) {
make.centerX.equalTo(view.prev);
make.bottom.inset(13);
}
}
],
data: [{
icon: {
icon: $icon(102, $color("black"), $size(50, 50))
},
title: {
text: "首页",
textColor: $color("black),
}
}]
}
}
其实就是一个matrix
,每个格子有一个image
和label
控件,用于显示图标和文本,现在让我们把这个matrix
封装进render
方法里面。
首先每行的格子数也就是columns
属性要变为传入的tabs
属性的长度。
可以通过this
访问到这个组件对象,用this.props.tabs
就可以拿到tabs
这个属性的值了,如果有传入则会得到传入的值,没有就会得到前面定义的默认值。
render() {
return {
type: "matrix",
props: {
columns: this.props.tabs.length,
itemHeight: 60,
spacing: 0,
scrollEnabled: false,
bgcolor: $color("clear"),
template: [
...
],
data: ...
}
}
}
然后将tabs
转换为matrix
的data
属性
render() {
// map函数内this的指向会改变,因此先把要props提取出来
const props = this.props;
const data = props.tabs.map(function (item, index) {
const color = index === props.index ? props.tabSelectedColor : props.tabColor;
return {
icon: {
icon: $icon(
item.icon,
color,
$size(50, 50)
)
},
title: {
text: item.title,
textColor: color,
}
};
});
return {
type: "matrix",
props: {
columns: 1,
itemHeight: 60,
spacing: 0,
scrollEnabled: false,
bgcolor: $color("clear"),
template: [
{
type: "image",
props: { id: "icon", bgcolor: $color("clear") },
layout(make, view) {
make.centerX.equalTo(view.super);
make.width.height.equalTo(25);
make.top.inset(7);
}
},
{
type: "label",
props: { id: "title", font: $font(10) },
layout(make, view) {
make.centerX.equalTo(view.prev);
make.bottom.inset(13);
}
}
],
data: data
}
}
}
至此组件的显示就完成了。
渲染组件
在main.js
中,从component.js
导入render
函数,再导入刚刚编写的TabLayout
组件。
const { render } = require("./lib/component");
const TabLayout = require("./components/TabLayout");
使用render
函数创建一个页面,使用方法和$ui.render
一致,不同的是这个函数能够识别自定义组件。
在views
中写入组件对象,组件的type
设置为刚导入的TabLayout
,再编写一下属性值和布局。
render({
views:[{
type: TabLayout,
props: {
index: 1,
tabs: [
{
title: "Home",
icon: "102"
},
{
title: "Favorite",
icon: "061"
},
{
title: "Settings",
icon: "002"
}
]
},
layout(make, view) {
make.bottom.right.left.equalTo(view.super.safeArea);
make.height.equalTo(50);
}
}]
});
运行,就会看见页面上出现了刚刚编写的组件。
改变上面代码中的index
和tabs
的值,重启脚本,组件也会对应发生变化。
优化组件(交互部分)
下面让我们来实现组件的交互,即点击tab会切换,并且可以通过外部代码控制此组件。
组件内部状态管理
可以看到,该组件的所有变化都是通过index
定义的,只要index
改变,组件的显示就应该跟着改变。因此index
属性表示了该组件的内部状态。
当用户点击tab,或是外部代码控制组件切换tab,实质上应改变组件内部的index
属性值,组件应该监听index
的变化从而在页面上做出变化。
开始实现
给matrix
添加一个点击事件。触发时应将index
变为被点击的tab的index
。
events: {
didSelect(_, indexPath) {
this.props.index = indexPath.item;
}
}
编写changeTab
事件,设置index
为传入的值。
methods: {
changeTab(tabIndex) {
this.props.index = tabIndex;
}
}
再添加一个watch
属性,监听index
的变化。当属性发生变化时会触发对应的函数,传入新值和旧值。
通过this.view
拿到该组件在页面上的实例,将新的index
对应的tab的颜色变为tabSelectedColor
的颜色值,将旧的tab颜色变为tabColor
的值。
接着再通知onTabChanged
事件,告诉它当前选中tab已更改。
watch: {
index(newIndex, oldIndex) {
const data = this.view.data;
data[newIndex].title.textColor = this.props.tabSelectedColor;
data[newIndex].icon.icon = $icon(
this.props.tabs[newIndex].icon,
this.props.tabSelectedColor,
$size(50, 50)
);
data[oldIndex].title.textColor = this.props.tabColor;
data[oldIndex].icon.icon = $icon(
this.props.tabs[oldIndex].icon,
this.props.tabColor,
$size(50, 50)
);
this.view.data = data;
this.events.onTabChanged(newIndex, this.props.tabs[newIndex]);
}
}
至此整个组件的编写工作就结束了。
现在再运行一遍项目,发现点击tab会切换了。
通过外部代码修改选中tab
现在我们来实现一个页面创建3秒后使第二个tab被选中的效果。
要实现此效果就是要改变组件的内部状态,即index
属性。
首先给组件加上属性id
叫"tablayout1"
,然后导入get
函数。
get函数的用法
get
函数的用法和$("id")
一致,都是通过id获取组件在页面上的实例,但此函数可以识别自定义组件。
获取实例后可以直接修改属性值,也可以调用组件methods
里的方法。
const { render, get } = require("./lib/component");
setTimeout(() => {
// 直接修改属性值
get("tablayout1").index = 1;
// 调用方法实现
// get("tablayout1").changeTab(1);
}, 3000);
Q&A
哪里可以获取this
对象?
methods
属性内watch
属性内render
方法内render
方法返回的控件对象的events
内
this
对象包括哪些属性?
props, events, methods, view
如何手动转换一个组件
使用trans
函数
const { trans } = require("./lib/component");
const TabLayout = require("./components/TabLayout");
$ui.render({
views: [
trans({
type: TabLayout,
props: {
index: 1,
tabs: [
{
title: "Home",
icon: "102"
}
]
},
layout(make, view) {
make.bottom.right.left.equalTo(view.super.safeArea);
make.height.equalTo(50);
}
})
]
});
注意事项
render
方法的返回结果必须是一个控件对象,如果组件是由多个控件组成的,请包裹至一个view控件中views
属性的值会自动加到根控件的views
后面
结语
至此,你已经对jsbox-component
有了一个全面的了解,其他的一些特性和用法可以到components文件夹下查看示例组件。
上一次使用JSBox这个软件还是在三年前,三年前我为JSBox写过一个名为AndStyle的库,也是可以使用各种组件。但由于当时能力有限,对前端不甚了解,因此存在诸多不完善的地方。这回终于把从前一直想实现但无从下手的功能开发出来了,也算了结一个遗憾。
本教程的全部代码在components/TabLayout.js
- 0
- 0
-
分享