从零上手 Rax
引子
这篇文章针对初学 Rax 的同学,主要介绍 Rax 是什么,开发体验的梳理,以及遇到 Rax 的开发问题如何寻求解决。本文配合 Rax 的 0.5 发布版本推出,结合新版 Rax 对于旧版的教程内容做出部分修正。
1、什么是 Rax
Rax 是一个源自淘宝的开源项目 https://github.com/alibaba/rax 作为 Weex 的上层 DSL 在集团内有广泛的应用。
除了跨容器、高性能、轻量等特点,Rax 是一个基于 React 写法的跨容器的 js 框架,你会用 React,那么你会很快上手 Rax。
当然与 React 也有部分区别,如下:
- 没有 createClass() 方法
- Rax setState() 是同步的, React setState 是异步的
- findDOMNode() 方法可以接收字符串类型的 id
- PropTypes 只是 React 的接口兼容
下面是一个最简单的示例,在学习本文的过程中可以通过 JSPlayground 进行练习,体验 Rax 的功能。
2、基础知识储备
If you use React, you already know how to use Rax.
JSX
熟悉 React 的同学一定对 JSX 不陌生,Rax 的 DSL 语法是基于 React JSX 语法而创造。与 React 不同,在 Rax 中 JSX 是必选的,它不支持通过其它方式创建组件,所以学习 JSX 是使用 Rax 的必要基础。
JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。 它只是一种语法糖。
它是类似 HTML 标签的表达式:
<View style={styles.text}>
<Text style={styles.title}>
hello
</Text>
</View>
JSX 只是 JavaScript 语法的一个语法映射。 JSX 表达式执行函数调用,我们可以看成他就是调用 createElement() 方法的快捷方式。 Babel 内置的支持 JSX 语法的编译。
扩展阅读:
生命周期
Rax 的生命周期与 React 中的概念是相同的
- 渲染阶段: componentWillMount、render、componentDidMount
- 存在阶段: componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、componentDidUpdate
推荐使用 ES6 类方式创建组件, 每个组件都需要有一个 render 方法,用来接收数据然后返回要显示在页面上的内容。
class Hello extends Component {
render() {
return <Text>Hello {this.props.name}</Text>;
}
}
扩展阅读
通用事件
点击事件
对于简单的 Touch 事件,我们使用 Touchable 组件, 通过给它绑定 onPress 事件,来监听他的点击事件。移动设备中为了区分点击与长按操作,通常点击事件会有 300 毫秒的延迟。
<Touchable onPress={() => console.log('pressed')}>
<Text>Touch</Text>
</Touchable>
长按事件
weex 的 <input>
和 <switch>
组件目前不支持 click 事件,使用的时候需要注意。
Appear
Appear 可以让我们在元素出现的时候做一些事情,比如曝光埋点。在 Web 上 Rax 的 framework 同样提供了 Appear 事件用来抹平与 Weex 的差异。
<View onAppear={(ev) => {
console.log('appear');
}} onDisappear={(ev) => {
console.log('disappear');
}}>
<Text>Hello</Text>
</View>
使用时需要注意,Appear 事件需要绑定在滚动容器内的元素上。
滚动事件
目前提供的滚动容器 ScrollView、RecyclerView、ListView、WaterFall 均支持一下滚动事件与属性
- onScroll:滚动实时触发事件
- onEndReachedThreshold:滚动区域还剩 loadmoreoffset 的长度时触发
- onEndReached:滚动到容器底部触发事件
输入事件
TextInput 是唤起用户输入的基础组件。当定义 multiline 输入多行文字时其功能相当于 textarea。
<TextInput
placeholder="Enter text to see events"
autoFocus multiline
onFocus={() => console.log('onFocus')}
onBlur={() => console.log('onBlur')}
onInput={() => console.log('onInput')}
/>
复杂手势
与 React Native 中的 PanResponder 相同的手势支持外,Rax 还提供了 Weex 上性能表现更好的 binding 手势支持(未来开源)。
Page 事件
Weex 通过 viewappear 和 viewdisappear 事件提供了简单的页面状态管理能力,需要注意的是目前该事件 Web 不支持。
viewappear 事件会在页面就要显示或配置的任何页面动画被执行前触发,例如,当调用 navigator 模块的 push 方法时,该事件将会在打开新页面时被触发。viewdisappear 事件会在页面就要关闭时被触发。
与组件的 appear 和 disappear 事件不同的是,viewappear 和 viewdisappear 事件关注的是整个页面的状态,所以它们必须绑定到页面的根元素上
let weexDocument = typeof __weex_document__ === 'object' ?
__weex_document__ : typeof document === 'object' ?
document : {};
if (isWeex && weexDocument && weexDocument.body) {
setNativeProps(findDOMNode(weexDocument.body), {
style: {backgroundColor: 'yellow'},
onViewAppear: () => {
console.log('onviewappear');
},
onViewDisAppear: () => {
console.log('onviewdisappear');
}
});
}
扩展阅读
样式
在书写 Rax 页面样式之前需要知道几件事情
- Rax 无单位与 rem 单位等价
- 页面宽度默认是 750rem,各端兼容
- 为了与 Weex 有一致的效果,我们推荐 Flexbox
Rax 样式支持行内样式与 className
行内
import styles from './foo.css';
function Foo() {
return <div style={styles.container}>
<span style={styles.container_title}>hello world</span>
</div>;
}
className
import './foo.css';
function Foo() {
return <div className="container">
<span className="container_title">hello world</span>
</div>;
}
扩展阅读
3、Rax 的 Driver
不同场景的 Driver 让 Rax 在不同的运行环境下都有一个良好的表现,在 Weex 和 Browser 上的表现是我们平时业务上最关心的,除此之外 Rax 有更多的尝试
Driver-weex
- Device 相关,抹平与 web 的差异
- setRem(基准值的设定(getDeviceWidth/getViewportWidth))
- 部分样式处理(transformPropsAttrsToStyle)
- w3cElements 能力的提供
- span p img button video textarea nav footer aside main h1 h2 h3 …
Driver-browser
- Device 相关,抹平与 Weex 的差异
- flex 布局相关
- 抹平不同浏览器之间的差异(webkit 前缀的支持)
Driver-server
- 服务端渲染的基础方法支持
Driver-webgl
- 一套标签化的 3d 场景描述
- carmeras control core geometries lights materials objects(line mesh points scene) scenes…
- three 的上层包装
4、Rax 的 framework
Write Once, Run everywhere!
Native 和 Web 天生就有很多差异,为了让开发者有更好的体验,大部分的差异是不需要让开发者去关心的。因此 Native 端我们模拟出了大量 Web 端的标准全局 API,Web 端同样为了模拟 Native 的特性做了很多支持。
weex-rax-framework
- 全局的 window 变量(全局方法的注册)
- 跨页面实例的 emitter 通信与页面内通信 (addEventListener)
- location, Response, Request, XMLHttpRequest, URL, URLSearchParams, FontFace, WebSocket, Event, CustomEvent, matchMedia, setTimeout, setInterval, requestAnimationFrame…
- weex_define, weex_require, weex_downgrade, weex_env, weex_code, weex_options, weex_data, weex_config …
web-rax-frmework
- Object.assign Object.entries Object.values…
- Array.from
- Number.isNaN
- appear 模拟,抹平 weex 之间的差异
- defaule builtin modules
5、上层体系
环境 API
Rax Framework 参照 W3C 规范,提供了以下在 Weex 和 Web 环境一致的全局 API,例如你可以在 Weex 的页面这样使用全局的 postMessage
addEventListener('message', (e) => {
console.log('this data is', e.data);
});
setTimeout(() => {
window.postMessage('{hello:1}', '*');
}, 1000);
(全局 API 的可用环境为 Weex 版本 >=0.9.5 可用,对应手淘版本 >= 6.4.0 可用,低于该版的设备建议可以考虑降级 h5)
通用组件
Rax 的通用组件主要的工作就是对表 Weex 的基础标签,在保证功能完备的基础上抹平 Web 的实现。
基础组件的 API 设计是遵照 RN 的思路。举个例子,Weex 为我们提供了通用的 click 事件,但是对标 RN 的规范,我们还是推荐开发者使用 Touchable 标签的 press 实现点击。Rax 作为开源项目与社区接轨也是为了能够更好的与社区融合,我们也可以更方便的吸纳社区的力量。
基础的组件可以组成复杂的组件或者模块,上面的 tabheader 就是个例子。
无论什么类型的页面我们都可以通过组件进行拼装,随着业务需求的发展,我们还会遇到很多新点子,很多新的挑战。我们鼓励开发者可以提炼出更多通用的功能或者组件贡献给 Rax。
想了解更多 Rax 组件能力,可以参考 Rax 的官网和github
6、开发环境
rax-cli
- cli 命令面向开源用户,不依赖阿里内部的工具以及环境
- 项目同时构建出 web 与 weex bundle
环境安装
npm install rax-cli -g
项目初始化
rax init YourProjectName
页面预览
cd YourProjectName
npm run start
查看运行的 Rax 的 bundle 源码会发现首行有一题条这样的注释
// {"framework" : "Rax"}
bundle 如果缺少该 framework 声明将会有 framework 提供的 API 缺失问题。
_小技巧:上面的 weex bundle 也可以这样访问 //h5Page地址.html?_wx_tpl://weexBundle地址.js_
cli 工具创建的目录结构较为简单,适合开发者进行上层定制
7、调试环境
###调试环境###
使用 cli 工具本地起一个调试环境,生成如下形式的页面地址:
//your_page.html?wx_tpl=//your_page_bundle.js
在浏览器环境直接访问该地址,可以得到 Web 页面;通过 Weex Playground 访问,则会返回相应的 Weex 页面。
###调试工具###
1、 weex-toolkit(本地调试)
我们使用 weex-toolkit 来进行调试
tnpm install -g weex-toolkit
weex debug -l
8、发布
发布注意事项
- 业务发布前,必须在以下环境完成测试
- Weex(Android/iOS) 未降级的客户端版本
- Web(Android/iOS) 未降级和已降级的客户端版本
- 有外投多个客户端的业务需要在每个客户端做以上回归
- 手淘版本无法做到全量覆盖,如业务依赖某个手淘版本,必须
- 在该手淘环境和线上环境都完成测试
- 明确发布先后顺序
- 明确在老版本手淘下访问的兼容方案
- 离线包无法做到全量更新,如业务使用离线包,必须
- 明确改动不会对未覆盖到版本造成影响(如果接口的不兼容变更)
- 紧急问题的修复采用【强制更新+推新包+全网络】的策略
- 使用内置组件的业务应提前做好充足的降级的预案
- 内置组件 bundle 降级完整 bundle 规则
- Weex bundle 降级 h5 规则
- 业务发布后,必须再次验证以上环境
9、完整的例子
我知道怎么写一个 hello world,但对于如何写一个项目可能还有些无从下手,我们能找到一些成品的例子作为参考。Rax 官网提供了大量的 demo 和 模版,帮助新上手的同学进行学习。
以下页面例子可以参考
(以上内容是关于 Rax 上手的一些内容,Rax 的其他教程敬请期待~)