官网原文:https://www.fusetools.com/learn/fusejs
FuseJS简介
FuseJS 是一个用来给跨平台App写业务逻辑的JavaScript框架。
提示:目前FuseJS还不支持输出WebGL。
起步
FuseJS代码有两种用法,一是可以嵌入在UX标记文件里的<JavaScript>
标签内,二是外链JavaScript文件,就像这样:
<JavaScript File="SomeCode.js" />
代码嵌入的例子:
<JavaScript>
console.log("Hello, FuseJS!");
</JavaScript>
模块 Modules
FuseJS执行的是CommonJS模块化系统的规范,每个代码文件或嵌入的代码段都是一个模块。
如果想要模块内的东西为外部可见,需要使用module.exports
构造器:
<JavaScript>
module.exports = {
exportedSymbol: "Hello, rest of the world!"
};
</JavaScript>
模块内没有通过module.exports
输出的定义数据,则在外部不能直接访问:
<JavaScript>
var data = [1, 2, 3];
var invisible = "I'm invisible";
module.exports = {
data: data
};
</JavaScript>
有时对其它JavaScript模块和UX代码的调用需要隐藏执行细节,这就起作用了。
导入模块
每个代码文件或嵌入的代码块都是一个模块。在JavaScript
标签内加上一个模块并赋予ux:Global
属性,其它模块就可以通过require
导入该模块,如下例所示:
<JavaScript File="parse-1.5.0.min.js" ux:Global="Parse" />
同一个项目内的其它模块想要导入上例模块时,代码如下:
var Parse = require("Parse").Parse;
备注:目前只通过JS文件路径的话,还不能直接引用该模块,我们正在解决,请耐心等待!
具体示例:TODO App with Parse backend example
设计与动力
FuseJS的关键设计目标是使代码短小、干净并且只专注于App的实用功能,而那些UX导向的功能,如Layout、数据呈现、动画和手势回馈都留给UX声明标记和原生UI组件去解决。
在Fuse中,JavaScript写业务逻辑而UX标记负责呈现,这么分开的做法有如下明显的益处:
- 性能 - 所有对性能要求高的任务都交给原生代码和原生UI组件搞定
- 简单 - 声明代码易写、易读、易理解,甚至对编程不胜了解的人也能做到(易学)
- 少犯错 - 少的声明意味着出事儿的机率少
- 可视化工具 - Fuse有
inspector
、timelines
等“拖放即可”的工具用来编辑UX标记。
- 可视化工具 - Fuse有
注意Fuse有非常多的声明API(就是设计好的UX标记)可以代替JavaScript代码能做的任务,比如可控制的动画效果。
很多其他JavaScript框架将命令式的UI代码、动画和高性能要求的任务统统混入JavaScript,因此有些FuseJS的新手开始的时候也试着在Fuse中这么做,技术上的确有可能,但我们并不鼓励这么做。我们推荐新手花些时间研究Fuse官网的示例,找找用新方式做事的感觉。
用"view"(UX标记)和"logic"(JavaScript)分开的方式净化代码,这么做可以大大减少代码量,且更容易维护,还可以让UX设计师与程序员有效的分工合作。
如果你需要写高性能要求的业务逻辑,我们推荐你用原生代码写,或者用Uno,而不要用Javascript。
Observables(观察量?)
Observables
(还是不翻译了,Observable是Fuse中一种特别的变量,用来处理数据变化的。)
Observables
是一个FuseJS应用中的关键成分,主要处理数据绑定、反应式(Reactive)编程和异步编程。
一个Observable
代表了一个可被观察的值,在Fuse里,Observables
主要用于在JavaScript代码与UX标记之间做数据绑定。
Observables
可以保存一个单独的值,也可以是一个0或多个元素的值的列表。当Observable
里的值发生变化,它的subscribers(订户)就会收到通知。
Observables
支持反应式(Reactive)编程和异步编程,极大简化了数据驱动的UI编程。
如何导入
var Observable = require('FuseJS/Observable');
基础用法
要创建一个Observable
,首先需要调用Observable
函数,初始值可以是0到多个。
-
Observable(<initial values>)
- 构造函数
示例:
var emptyObservable = Observable();
var isSomethingEnabled = Observable(true);
如果Observable
的初始值为空,则其.value === undefined
、.length === 0
。
Observable
的值
如果Observable
只有一个值,我们可以用.value
属性来get
或set
这个值:
var someString = Observable('foobar');
Console.Log(someString.value); // prints 'foobar'
someString.value = 'barfoo'; // sets the value, notifies subscribers.
当用.value
属性设定了一个值后,所有订户都会被通知。
Observable
值列表
如果使用Observable
保存一个值列表(多个值),我们可以用.add(item)
和.remove(item)
方法来操控这些值。
下例是用.length
属性查询列表里值的数量:
var friends = Observable('Jake', 'Jane', 'Joe');
Console.Log(friends.length); // prints 3
friends.add('Gina');
Console.Log(friends.length); // prints 4
Observable
函数
当用一个函数作为唯一的参数来初始化一个Observable
时,它的.value
的值是评估该函数后计算生成的。
当函数估值时,所有反应式依赖(Reactive dependencies)都会用所有其它关联Observables
的值自动生成。
示例:
var firstName = Observable('John');
var lastName = Observable('Doe');
var fullName = Observable(function() {
return firstName.value + ' ' + lastName.value;
})
上例中,如果firstName
或lastName
发生变化,fullName
就会随之自动更新。
对,就这么神奇。
成员 Members
值的操作符 Value operators
value
该属性可以取得或设置当前Observable
的值。
.value
属性实际上相当于getAt(0)
和replaceAt(0)
的简写,通常是用于单一值的Observerable
,虽然这不一定是必须的。
if (isSomethingEnabled.value) {
doSomething();
}
isSomethingEnabled.value = false;
值列表的操作符 List operators
length
该属性返回Observable
值的总数。
var fruits = Observable('Orange', 'Apple', 'Pear');
Console.Log(fruits.length); //output: 3
getAt(index)
该属性返回给定索引位置的值。
var seasons = Observable('Summer', 'Fall', 'Winter', 'Spring');
Console.Log(seasons.getAt(2)); //output: 'Winter'
add(value)
往Observables
的值列表里添加值。
var colors = Observable('Red', 'Green');
colors.add('Blue');
remove(value)
从Observable
的值列表里删除首个 remove
给出的值。
var shapes = Observable('Round', 'Square', 'Rectangular');
shapes.remove('Rectangular');
tryRemove(value)
试着从Observable
的值列表里删除首个tryRemove
给出的值,如果成功,返回true
, 否则,返回false
。
var shapes = Observable("Round", "Square", "Rectangular");
if(shapes.tryRemove("Rectangular")) {
Console.Log("success");
}
removeAt(index)
removeAt(index)
删除给定索引位置的值。
var shapes = Observable('Round', 'Square', 'Rectangular');
shapes.removeAt(2);
removeWhere(func)
用一个函数遍历值列表里的值,删除满足条件(func返回true的)的所有的值。
var hotPlaces = Observable(
{name: "Oslo", temperature: 30},
{name: "New York", temperature: 24},
{name: "California", temperature: 27},
{name: "Sydney", temperature: 10}
).removeWhere(function(place){
return place.temperature < 20;
}); //Removes Sydney from the list
forEach(func)
对Observerable
值列表里的每个值调用一遍给定的函数。
var numbers = Observable(10, 2, 50, 3);
numbers.forEach(function(number) {
Console.Log(number + " is a nice number!");
});
replaceAt(index, value)
把给定索引位置的值替换为新值。
var ingredients = Observable('sugar', 'milk', 'potato');
ingredients.replaceAt(2, 'flour'); //Replaces 'potato' with 'flour'
replaceAll(array)
把Observable
里的所有值全部替换为给定数组里的值。
var colors = Observable("Red", "Green", "Blue");
colors.replaceAll(["Orange", "Cyan", "Pink"]);
clear()
清除Observable
里所有的值。
var colors = Observable("Red", "Green");
colors.clear();
indexOf(value)
在Observable
里找到首个给定的值,返回其索引位置。
var seasons = Observable("Summer", "Fall", "Winter", "Spring");
var index = seasons.indexOf("Winter"); // 2
contains(value)
如果变量里存在给定的值,返回true
。
Observable seasons = Observable("Summer", "Fall", "Winter", "Spring");
var winterExists = seasons.contains("Winter"); // true
refreshAll(newValues, compareFunc, updateFunc, mapFunc)
一共四个参数,第一个newValues
用于更新Observable
里所有项,基于什么条件要看后面的函数,第二个compareFunc
函数用于比较两个项是否相等,如果发现相等则应用第三个函数updateFunc
, 将现有项替换为新值,最后一个mapFunc
函数将新发现的项映射到新的对象中。
var items = Observable({
{id: 1, text: "one" },
{id: 2, text: "two" },
{id: 3, text: "tres" },
})
var newItems = [
{id: 3, text: "three" },
{id: 4, text: "four" },
{id: 5, text: "five" }
]
items.refreshAll(newItems,
//Compare on ID
function(oldItem, newItem){
return oldItem.id == newItem.id;
},
// Update text
function(oldItem, newItem){
oldItem.text.value = newItem.text;
},
// Map to object with an observable version of text
function(newItem){
return { id:newItem.id, Observable(newItem.text)
}
);