跳到内容

工作原理

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" eventmessage,使用 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" eventmessage,使用 submitTitle 显示本机按钮,并在点击本机按钮时回复 Web 组件。

注意:首次在应用程序中设置 Strada Android 还需要额外的工作。有关完整说明,请参阅 快速入门 指南。

添加 CSS 以隐藏桥接元素

我们现在已经在 Web 和原生应用中设置了 "form" 组件。每当原生应用支持 "form" 组件时,它都会从 Web 组件接收一条消息并显示其原生按钮。

还有一项最后的任务需要完成。当显示原生按钮时,我们希望隐藏 <form> 中的 Web 提交按钮。如果满足以下条件,则可以轻松编写仅应用于特定范围的 CSS:

[data-bridge-components~="form"]
[data-controller~="bridge--form"]
[type="submit"]
{
display: none;
}

现在,您已在应用中获得了改进的表单屏幕!

下一步:Strada Web