多页面场景案例与原理解析
大约 4 分钟
多页面场景案例与原理解析
本章节,我们将介绍应用中的多个Kuikly页面如何进行交互和数据传递。
场景案例
这是一个简单的页面,点击数字会使数字+1,点击“open”会打开同一页面的另一个实例。
@Page("foo")
internal class FooPage : BasePager() {
private var digit by observable(0)
private val routerModule by lazy(LazyThreadSafetyMode.NONE) {
acquireModule<RouterModule>(RouterModule.MODULE_NAME)
}
override fun body(): ViewBuilder {
val ctx = this
return {
attr {
allCenter()
}
Text {
attr {
text(ctx.digit.toString())
fontSize(100f)
}
event {
click {
++ctx.digit
}
}
}
Text {
attr {
text("open")
}
event {
click {
ctx.routerModule.openPage("foo")
}
}
}
}
}
}
数据传递
下面我们修改一下,让打开的第二个页面的数字和第一个页面的数字保持一致。
@Page("foo")
internal class FooPage : BasePager() {
companion object {
// 使用全局变量,记录数字
private var globalDigit = 0
}
// 使用全局变量,初始化当前页面的数字
private var digit by observable(globalDigit)
private val routerModule by lazy(LazyThreadSafetyMode.NONE) { ... }
override fun body(): ViewBuilder {
val ctx = this
return {
attr { ... }
Text {
attr { ... }
event {
click {
// 同时更新全局变量和当前页面的数字
ctx.digit = ++globalDigit
}
}
}
Text { ... }
}
}
}
可以看出内置模式下第二个页面正确地和第一个页面保持一致,但是在动态化模式下,第二个页面的数字重新从0开始了。
原理解析
这是因为每个动态化页面的数据是互相隔离的,页面直接无法使用全局变量来共享数据。
正确做法
正确的跨页面传递数据的方式是使用页面数据PagerData传递初始数据;使用Kuikly页面事件通知来进行页面间的交互。
@Page("foo")
internal class FooPage : BasePager() {
private var digit by observable(0)
private val routerModule by lazy(LazyThreadSafetyMode.NONE) { ... }
private val notifyModule by lazy(LazyThreadSafetyMode.NONE) {
acquireModule<NotifyModule>(NotifyModule.MODULE_NAME)
}
private lateinit var notifyRef: CallbackRef
override fun created() {
super.created()
// 从PagerData(页面数据)中获取初始值
digit = pageData.params.optInt("digit")
// 监听页面事件通知
notifyRef = notifyModule.addNotify("digit_change") {
// 更新当前页面的数字
digit = it?.optInt("digit") ?: return@addNotify
}
}
override fun pageWillDestroy() {
// 移除页面事件通知
notifyModule.removeNotify("digit_change", notifyRef)
super.pageWillDestroy()
}
override fun body(): ViewBuilder {
val ctx = this
return {
attr { ... }
Text {
attr { ... }
event {
click {
val newDigit = ++ctx.digit
// 发送页面事件通知
ctx.notifyModule.postNotify(
eventName = "digit_change",
eventData = JSONObject().put("digit", newDigit)
)
}
}
}
Text {
attr {
text("open")
}
event {
click {
// 打开第二个页面,并传递PagerData(页面数据)
val param = JSONObject().put("digit", ctx.digit)
ctx.routerModule.openPage("foo", param)
}
}
}
}
}
}
页面作用域
提问
对于非动态化的页面,是否可以使用全局的observable变量来同时控制多个页面的状态?
答案是否定的。
首先,当我们尝试在companion object
中创建一个observable
变量时,IDE会提示observable
方法已经被弃用,

原因是observable
需要PageScope
(页面作用域)的上下文,我们需要在一个有效的PgageScope
(例如Pager
)中创建observable
对象。
让我们再修改一下:
@Page("foo")
internal class FooPage : BasePager() {
companion object {
private var globalDigit: ReadWriteProperty<Any, Int>? = null
}
init {
// 在Pager中创建observable变量
if (globalDigit == null) {
globalDigit = observable(0)
}
}
private var digit by globalDigit!!
private val routerModule by lazy(LazyThreadSafetyMode.NONE) { ... }
override fun body(): ViewBuilder { ... }
}
在这个例子中,虽然在第二个页面打开时全局observable传递了正确的值,但是在第二个页面上点击却未能驱动第二个页面的更新,回到第一个页面时发现,第一个页面的数字更新了。
原理解析
observable与创建它的PageScope
(页面作用域)是绑定关系,虽然我们把observable
变量放在了companion object
中,但是它依然只能驱动创建它的Pager的UI更新。
总结
- 动态化页面与其它页面的数据是隔离的,不能直接使用全局变量来共享数据;
- 使用页面数据PagerData和Kuikly页面事件通知来进行动态化页面与其它页面的数据交互;
observable
只能驱动创建它的PageScope
的UI更新,不能跨页面使用。