元素

WebView

web view 在应用内显示一个网页,或你传入的一段 HTML:iOS 和 Mac Catalyst 上是 WKWebView,Android 上是 android.webkit.WebView。由于 nsh 用原生视图渲染,web view 和其他视图一样处在布局中:没有覆盖层,与周围视图之间也不存在层级问题。

import { signal, Text, View } from "nsh";
import { WebView, type WebViewNavigation } from "nsh/webview";

function Browser() {
  const nav = signal<WebViewNavigation | null>(null);
  return (
    <View style={{ flex: 1 }}>
      <Text numberOfLines={1}>{nav()?.title ?? "Loading…"}</Text>
      <WebView
        source="https://example.com"
        style={{ flex: 1 }}
        onNavigationStateChange={(e) => nav.set(e)}
      />
    </View>
  );
}

WebView 从 nsh/webview 导入,source 是必填的。Web view 与视频指南介绍了通信模式和能力声明。

#属性

WebView 接受 view 的属性(不含 children),另外还有:

属性类型默认值说明
sourcestring | { uri: string } | { html: string; baseUrl?: string }一个 URL,或一段 HTML 字符串及其相对链接所基于的 base URL。
injectedJavaScriptstring每个页面加载完成后执行。
injectedJavaScriptBeforeContentLoadedstring在每个页面自身的脚本之前执行。
userAgentstring浏览器的 user agent 字符串。
javaScriptEnabledbooleantrue页面可以运行脚本。
scrollEnabledbooleantrue用户可以滚动页面。
ios 键类型说明
allowsBackForwardNavigationGesturesboolean从边缘轻扫以后退和前进。

#事件

事件载荷时机
onLoadStartWebViewNavigation页面开始加载。
onLoadWebViewNavigation页面加载完成。
onError{ message }加载失败。
onNavigationStateChangeWebViewNavigationURL、标题、加载状态或历史记录发生变化。
onMessage{ data: string }页面调用了 window.ReactNativeWebView.postMessage(data)。

WebViewNavigation 为 { url, title, loading, canGoBack, canGoForward }。

#Ref 方法

方法说明
reload()重新加载页面。
goBack() / goForward()在页面历史中后退 / 前进。
stopLoading()停止加载。
injectJavaScript(script)在页面中执行 JavaScript。
postMessage(data)向页面发送一个字符串,页面会在 window 上收到 message 事件。

#通信

应用和页面之间交换的是字符串。页面通过 window.ReactNativeWebView.postMessage(data) 发送;应用通过 ref 的 postMessage(data) 发送。结构化数据请用 JSON。

import { signal, Text, View, type YNode } from "nsh";
import { WebView } from "nsh/webview";

const page = `<!doctype html>
<meta name="viewport" content="width=device-width">
<button onclick="window.ReactNativeWebView.postMessage('hello from the page')">Post to the app</button>
<p id="got"></p>
<script>
  window.addEventListener("message", (e) => { document.getElementById("got").textContent = e.data; });
</script>`;

function Bridge() {
  const message = signal("");
  let web!: YNode;
  return (
    <View style={{ gap: 12 }}>
      <WebView ref={web} source={{ html: page }} style={{ height: 200 }} onMessage={(e) => message.set(e.data)} />
      <Text onPress={() => web.postMessage("hi from the app")}>Post to the page · got: {message()}</Text>
    </View>
  );
}

#能力声明

如果应用在 nsh.json 中声明了能力,web view 只能显示和跳转到 webview 下列出的主机。被拒绝的 source 不会显示任何内容;被拒绝的顶层跳转(用户点击的链接)会在原生侧取消。

{
  "capabilities": {
    "webview": { "hosts": ["example.com", "*.example.com"] }
  }
}

#平台说明

  • iOS 在第一次创建 web view 时才加载 WebKit,从不显示 web view 的应用不必为此付出开销。
  • 在 Android 上,页面的 postMessage 调用会到达后台线程,再按顺序转发给应用。
  • 要给 web view 加圆角,设置 borderRadius 和 overflow: "hidden"。