编辑语:
随着RISC-V生态的蓬勃发展,相关开源开发套件也开始逐渐丰富。为了帮助开发者快速了解、玩转新推出的RISC-V开发套件,OCC推出RISC-V生态开发套件解析系列内容,详细讲解生态开发套件的功能特点与上手教程。
通过上期内容的介绍,许多开发者已经初步了解了Waft应用的开发,并完成了Waft运行环境的搭建。本期内容旨在加深大家对Waft应用开发的了解,我们将通过拆解Waft应用的开发步骤,手把手教大家创建一个Waft应用。
01 开发环境支持
MacOS,Linux(Windows支持wsl、linux虚拟机)
02 waft开发流程
03 安装waft-cli 工具
PS: 权限问题使用sudo执行即可
npm i waft-cli -g
使用 Windows 的开发者,可以安装官方推荐的wsl,推荐安装Linux虚拟机-Ubuntu(18.04、20.04), Linux 的发行版本推荐安装 Ubuntu(18.04、 20.04), 可在微软商店中下载。之后可在对应的 Linux 环境中执行开发所需命令。
wal网址:
https://docs.microsoft.com/en-us/windows/wsl/install?spm=a2cl5.25411629.0.0.2c5a180fdX4SIu
04 创建项目
通过waft脚手架初始化项目,会引导您进行项目命名等,您将得到一个新project
需要Node 14版本以上
waft init
这里我们选择第9个模板,平头哥天气Demo
05 启动调试服务
cd my-waft-project npm run start
REPL命令行模式
启动后自动开启REPL模式,您可以通过.help查询支持哪些命令列表。
打包
waft>.build --aot=true --aotTarget=riscv64
编译后的产物在工程目录的build目录下,产物名称为app.aot,示例视频如下。
文件夹及目录说明:
06 推送
方式1(adb push):
电脑与开发板用type-c数据线连接,注意使用adb push通道,参考下图标注
adb push build/app.aot /mnt/UDISK/wasm/
方式2(scp):
pc及开发板需要在同一个局域网内。
scp build/app.aot root@172.16.1.36:/mnt/UDISK/wasm
172.16.1.36替换为开发板的ip地址,ssh的登陆密码为:tina。
07 运行waft
通过adb shell或者串口接入开发板,在adb shell终端或者串口工具终端输入如下命令:
waft_app /mnt/UDISK/wasm/app.aot
示例运行界面:
模拟器调试
当前只支持Mac模拟器 部分API不支持
点击面板中的启动模拟器,初次会自动下载模拟器(时间较长,需要等待一会)。
完整可以参考此文档:
https://www.yuque.com/waft/docs/dywumg_wk97d5?spm=a2cl5.25411629.0.0.77da180f7fYw5F
Web调试(Beta)
Web调试是通过将页面跨平台到Web端进行运行和渲染。由于Web端样式一致性仍在建设中,所以尽量以真机和模拟器的效果为准。
可以通过start时提示的链接打开进入。
08 布局和样式
1. 布局写法和数据绑定
Waft基于核心的响应式的数据绑定模块,来实现逻辑层修改数据,就能驱动视图层的更新的效果。
视图代码:
<div> <div>{{title}}</div> <div onTap="tapMe">示例按钮</div> </div>
逻辑脚本:
let page: Login; export class Login extends Page { constructor(props: ComponentProps) { super(props); page = this; // assemblyScript不支持闭包,在闭包中需要先赋给全局变量 this.addEventListener('tapMe', function () { page.setState(`{"title":"Hello Waft"}`); }); } onLoad(query: JSONObject): void{ console.log('onload') } }
如上所示,只要点击示例按钮,就会触发执行tapMe的函数回调,从而进行title字段的状态更新,视图因此会根据状态更新内容。
2.样式写法
Waft支持了主流的css样式,以及web规范的css写法,布局的单位为rpx,示例如下:
.title-div{ width: 30rpx; height: 10rpx; background-color: red; } .title-text{ font-size: 20rpx; color: #FFFFFF; }
目前对rules规则如@media,css变量等能力暂未支持,具体可以参考ACSS规范:https://www.yuque.com/waft/docs/taglw5
3. 布局基准
默认框架的布局基准为1024px(指的是布局设计的全屏宽度像素)。
全屏应用:可以在app.json配置中自定义应用的基准。比如天猫精灵全屏的应用可以配置为1024。
{ "pages": [ "pages/index/index" ], "widgets":[ "widgets/weather/weather" ] "window": { "defaultTitle": "Hello Waft" }, "viewport": 1024 }
widget:需要在具体的某个widget的.json文件下配置,如果样式基准为300px来布局,可以配置viewport字段为300,如下:
{ "usingComponents": { "card-layout": "waft-ui/src/components/card-layout/card-layout" }, "viewport": 300 }
09 页面逻辑写法
页面逻辑的写法示例如下:
1. Function写法
import { JSON, JSONArray, JSONObject } from "waft-json"; import { getDataSource, log, FuncPage } from "waft"; let thisPage: FuncPage; export function Index(page: FuncPage): void{ thisPage = page; // 设置默认state page.setState(getDataSource().toString()); page.onload = function (query: string) { log('--> onload:' + query); }; page.onshow = function (event: string) { log('--> onshow:' + event); }; page.onhide = function (event: string) { log('--> onhide:' + event); }; page.onunload = function (event: string) { log('--> onunload:' + event); }; page.addEventListener('tapTitle', function (event: string) { log('--> tapTitle event:' + event); thisPage.setState(`{"title":"Hello Waft"}`); }); }
2. Class写法
import { JSON, JSONObject } from "waft-json"; import { console, getDataSource, Page, ComponentProps, BaseEvent } from "waft"; let thisPage: Page; export class Index extends Page { constructor(props: ComponentProps){ super(props); thisPage = this; // 设置默认的state this.setState(JSON.stringify(getDataSource())); this.addEventListener('tapTitle', function (event: BaseEvent) { console.log('tapTitle event'); thisPage.setState(`{"title":"Hello Waft"}`); }); } onShow(): void{ // 页面显示 console.log('page onShow'); } onLoad(query: JSONObject): void{ // 页面加载后 console.log('page onLoad:' + JSON.stringify(query)); } onHide(): void{ // 页面隐藏 console.log('page onHide'); } onUnLoad(): void{ // 页面销毁 console.log('page onUnLoad'); } onMessage(data: JSONObject): void{ // 信息推送更新 console.log('page onMessage:' + JSON.stringify(data)); } onError(error: Error): void{ // 错误 console.log('page onError'); } }
10 Page开发示例
根据模板创建页面
在src/pages下新建页面demo,包含了demo.axml,demo.acss,demo.ts,demo.json4个文件
在app.json中配置页面
{ "pages": [ "pages/demo/demo" ], "window": { "defaultTitle": "Hello Waft" } }
【可选】在app.json配置页面渲染基准:
waft支持配置viewport。viewport代表了视觉布局的卡片总宽度基准(手机上都按750px布局),如果你的页面设计宽度总像素是按1024px布局,那只要设置viewport为1024,css即可按照1024rpx来写。(可以不配置,默认waft会按1024px来布局)
{ "pages": [ "pages/demo/demo" ], "window": { "defaultTitle": "Hello Waft" } "viewport": 1024 }
【可选】在mock.json中配置页面mock启动参数:
启动参数包含了以下值:
- path: 启动页面路径;(默认为首页)
- query: 启动的query参数
- dataSource:启动的额外下发数据 (如服务端预先请求完数据,会放到dataSource下)
可以在src目录下的mock.json文件中配置数据,该数据可以在运行环境中通过 getLaunchData() 函数调用。
会在实际下发时按照实际运行,该参数只影响调试环境,不影响生产运行环境。
{ "path": "pages/demo/demo", "query": { "id": "123456" } "dataSource":{ "title": "hello" } }
在axml文件中开发界面:
开发界面和数据绑定
<div> <div>{{title}}</div> </div>
在ts文件中开发逻辑:
- 可以在页面初始化时,通过全局的getLaunchData()方法,获取到应用启动的信息
- 取出其中的dataSource字段,设置到默认的state
- 在生命周期中,可以进行您的定制操作
import { JSON, JSONObject } from "waft-json"; import { console, getDataSource, WaftPage, ComponentProps } from "waft"; export class Index extends WaftPage { constructor(props: ComponentProps){ super(props); this.setState(JSON.stringify(getDataSource())); } onShow(): void{ console.log('page onShow'); } onLoad(query: JSONObject): void{ console.log('page onLoad:' + JSON.stringify(query)); } onHide(): void{ console.log('page onHide'); } onUnLoad(): void{ console.log('page onUnLoad'); } onMessage(data: JSONObject): void{ console.log('page onMessage:' + JSON.stringify(data)); } onError(error: Error): void{ console.log('page onError'); } }
11 waft-router
waft框架提供了多页路由的api。
在目录里新建多个页面,并在app.json中配置
在app.json中配置页面,和默认启动页
在页面中使用history API跳转
push
跳转路由界面
const query = new JSONObject(); query.set("id", 123); // query可以省略 或用 null 或 new JSONObject() history.pushState({url : 'pages/detail/detail', query: query });
replace
替换当前页面,换为路由界面
// query可以省略 或用 null 或 new JSONObject() history.replaceState({url : 'pages/detail/detail', query: JSON.parseObject(`{"id":"123456"}`)});
goBack
返回上一页
history.goBack();
12 waft-store
waft提供了全局状态store。
store数据获取
improt { store } from 'waft'; const globalData = store.getGlobalData();
store设置数据
import { store } from 'waft' store.dispatch('state', new JSONObject())
store更新订阅
在component或者page中,可以通过this.observer方法监听全局store的更改。
immediate参数标识是否默认立即返回初始的状态。
export class Login extends Page { constructor(props: ComponentProps) { super(props); // 监听全局store变化 this.observer(['menuData'], (key:string, data: JSONObject)=>{ console.log('menuData change:' + data.toString()); }, { immediate: true }); } }
store默认映射到当前页面的state
在component或者page中,可以通过this.connect方法把store中的内容订阅并同步到page的state上。
第二个参数可以传入一个reducer,修改参数再返回。return null时不处理。
export class Login extends Page { constructor(props: ComponentProps) { super(props); // 自动把store的key内容同步到page的state this.connect(['menuData']); // 监听全局store变化,并修改; this.connect(['menuData'], (key:string, data: JSONObject, oldData: JSONObject | null)=>{ return data; }); } }
附:
waft开发手册:https://www.yuque.com/waft/docs/ubi8k4
13 下期预告
本期内容就介绍到这里,下期我们将带大家上手Button的使用,欢迎大家持续关注RISC-V生态开发套件系列内容。