工作原理
Strada 使用基于组件的方法在 WebView
中的 Web 组件 和原生应用程序中的 原生组件 之间创建双向通信通道。Strada 充当 Web 代码和原生应用程序代码之间的“桥梁”,并抽象掉固有的复杂性。
Web 组件“发送”消息到其对应的原生组件。原生组件“接收”消息以构建和显示原生控件,并使用消息中提供的 data
填充它们。当原生组件想要通知其对应的 Web 组件用户操作或状态更改时,原生组件会“回复”最初收到的消息。Web 组件“接收”回复并调用回调以根据回复消息 data
更新 Web 组件。
Strada 利用 Stimulus 为 Web 组件带来相同的功能。事实上,核心 BridgeComponent
类是 Stimulus Controller
的扩展,因此在构建 Strada 组件之前,你应该熟悉 Stimulus。
﹟ 演示示例
如果你想通过示例了解 Strada 的实际应用,你可以运行 Turbo iOS 演示应用程序 或 Turbo Android 演示应用程序。它们与 Turbo Native 演示 Web 应用程序 配合使用,你可以在其中查看演示 Web 组件的源代码。
下面 "form"
组件示例的完整源代码可以在演示应用程序中找到。
﹟ 构建 Web 组件
假设你在 Web 应用程序中有一个带有提交按钮的简单 <form>
<form method="post">
<!-- form elements -->
<button
class="button"
type="submit">
Submit Form
</button>
</form>
在原生应用程序栏的右上角显示 提交 按钮是移动应用程序中的典型惯例。它的优点是永远不会隐藏在虚拟键盘下方,并且无论你在页面上滚动到哪里,它始终可见。我们可以通过一组 "form"
Web 和原生组件在原生按钮中显示提交按钮,而不是在 WebView
中显示提交按钮。
让我们使用桥接属性更新表单,并创建一个新的 Web 组件,利用 Strada 和 Stimulus 约定
<form
method="post"
data-controller="bridge--form">
<!-- form elements -->
<button
class="button"
type="submit"
data-bridge--form-target="submit"
data-bridge-title="Submit">
Submit Form
</button>
</form>
现在,我们将创建一个新的 "form"
组件。这与创建 Stimulus 控制器的方式类似,但扩展了 BridgeComponent
类
// bridge/form_controller.js
import { BridgeComponent } from "@hotwired/strada"
export default class extends BridgeComponent {
static component = "form"
static targets = [ "submit" ]
// ...
}
创建了基本的 "form"
组件后,我们现在可以向相应的本机 "form"
组件发送 message
。我们将在消息中将提交按钮的标题作为 JSON data
发送,以便本机组件可以使用 submitTitle
设置本机按钮的标题。
// bridge/form_controller.js
import { BridgeComponent, BridgeElement } from "@hotwired/strada"
export default class extends BridgeComponent {
static component = "form"
static targets = [ "submit" ]
submitTargetConnected(target) {
const submitButton = new BridgeElement(target)
const submitTitle = submitButton.title
this.send("connect", { submitTitle }, () => {
target.click()
})
}
}
注意调用 send()
时的第三个参数。这是一个回调函数,当本机组件回复 "connect"
消息时将调用该函数。<form>
中的提交按钮被单击,将表单提交到服务器,就像用户直接点击按钮一样。
Web 组件现已准备就绪,我们可以在 iOS 和 Android 应用程序中构建相应的 "form"
组件。
﹟ 构建本机 iOS 组件
让我们在 Turbo iOS 应用程序中创建一个本机 "form"
组件。首先,我们将对 BridgeComponent
类进行子类化
final class FormComponent: BridgeComponent {
override class var name: String { "form" }
// ...
}
现在,我们可以实现用于处理接收和回复消息的代码
final class FormComponent: BridgeComponent {
override class var name: String { "form" }
// Handle incoming messages based on the message `event`.
override func onReceive(message: Message) {
switch message.event {
case "connect":
handleConnectEvent(message: message)
}
}
private func handleConnectEvent(message: Message) {
guard let data: MessageData = message.data() else { return }
configureBarButton(with: data.submitTitle)
}
private func configureBarButton(with title: String) {
let item = UIBarButtonItem(title: title,
style: .plain,
target: self,
action: #selector(performAction))
// Display the button in the app bar
}
// Reply to the originally received "connect" event message (without any new data).
@objc func performAction() {
reply(to: "connect")
}
}
private extension FormComponent {
struct MessageData: Decodable {
let submitTitle: String
}
}
组件接收 "connect"
event
的 message
,使用 submitTitle
显示本机按钮,并在点击本机按钮时回复 Web 组件。
注意:首次在应用程序中设置 Strada iOS 还需要额外的工作。有关完整说明,请参阅 快速入门 指南。
﹟ 构建本机 Android 组件
让我们在 Turbo Android 应用程序中创建一个本机 "form"
组件。首先,我们将对 BridgeComponent
类进行子类化
class FormComponent(
name: String,
private val delegate: BridgeDelegate<NavDestination>
) : BridgeComponent<NavDestination>(name, delegate) {
// ...
}
现在,我们可以实现用于处理接收和回复消息的代码
class FormComponent(
name: String,
private val delegate: BridgeDelegate<NavDestination>
) : BridgeComponent<NavDestination>(name, delegate) {
// Handle incoming messages based on the message `event`.
override fun onReceive(message: Message) {
when (message.event) {
"connect" -> handleConnectEvent(message)
}
}
private fun handleConnectEvent(message: Message) {
val data = message.data<MessageData>() ?: return
showToolbarButton(data)
}
private fun showToolbarButton(data: MessageData) {
// Display the button in the toolbar
binding.formSubmit.apply {
text = data.title
setOnClickListener {
performSubmit()
}
}
}
// Reply to the originally received "connect" event message (without any new data).
private fun performSubmit(): Boolean {
return replyTo("connect")
}
@Serializable
data class MessageData(
@SerialName("submitTitle") val title: String
)
}
组件接收 "connect"
event
的 message
,使用 submitTitle
显示本机按钮,并在点击本机按钮时回复 Web 组件。
注意:首次在应用程序中设置 Strada Android 还需要额外的工作。有关完整说明,请参阅 快速入门 指南。
﹟ 添加 CSS 以隐藏桥接元素
我们现在已经在 Web 和原生应用中设置了 "form"
组件。每当原生应用支持 "form"
组件时,它都会从 Web 组件接收一条消息并显示其原生按钮。
还有一项最后的任务需要完成。当显示原生按钮时,我们希望隐藏 <form>
中的 Web 提交按钮。如果满足以下条件,则可以轻松编写仅应用于特定范围的 CSS:
- 原生应用的特定版本支持
"form"
组件 - 应用中的特定
<form>
已连接到"form"
组件
[data-bridge-components~="form"]
[data-controller~="bridge--form"]
[type="submit"] {
display: none;
}
现在,您已在应用中获得了改进的表单屏幕!