如何感知 WebKit 页面切换

作者: 波儿菜 | 来源:发表于2021-10-24 18:42 被阅读0次

背景

通常在 WKWebView 打开一个页面,收到页面数据时,代理方法可感知这个时机:

-webView:didCommitNavigation:

但若是改变页面 hash (也就是位置标识符"#") 打开另一页面时,这个代理方法不会调用,也没有合适的回调接口。

从表现上说,改变 hash 会产生网页历史栈,safari 也会产生历史记录,这种场景应该是有和 -webView:didCommitNavigation: 相对应的回调,官方没做好一致性,需通过 WebKit 源码进一步探索感知方式。

源码分析

断点查看触发-webView:didCommitNavigation:调用栈如下:

-> UIProcess 进程
-[AnyInstance webView:didCommitNavigation:]
WebKit::NavigationState::NavigationClient::didCommitNavigation
(IPC) WebKit::WebPageProxy::didCommitLoadForFrame

-> WebContent 进程
(IPC) Messages::WebPageProxy::DidCommitLoadForFrame
WebKit::WebFrameLoaderClient::dispatchDidCommitLoad
WebCore::FrameLoader::dispatchDidCommitLoad
WebCore::FrameLoader::receivedFirstData

在第一次收到页面数据后,会进行网页历史栈等状态的处理,最后抛给公开代理。对于改变页面 hash 打开另一页面场景,是在同一个 Document,FrameLoader 作为专门处理页面加载的地方,应该是有处理目标页面是否是同一 Document 的代码分支,扫描一下就找到了一个可疑的函数:

void FrameLoader::loadItem(...) {
    ...
    if (sameDocumentNavigation)
        loadSameDocumentItem(item);
    else
        loadDifferentDocumentItem(item, fromItem, loadType, MayAttemptCacheOnlyLoadForFormSubmissionItem, shouldTreatAsContinuingLoad);
}

追溯函数调用链,发现-webView:didCommitNavigation:调用栈正是来自else分支,查看if分支的处理,最终会通过一个 IPC 消息发送到 APP 进程,在 APP 进程代码实现如下:

void WebPageProxy::didSameDocumentNavigationForFrame(...) {
    ...
    m_navigationClient->didSameDocumentNavigation(...);
    ...
}
void NavigationState::NavigationClient::didSameDocumentNavigation(...) {
    ...
    [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState->m_webView navigation:wrapper(navigation) didSameDocumentNavigation:toWKSameDocumentNavigationType(navigationType)];
}

这里的navigationDelegate就关联了 WKWebView 的公开代理 navigationDelegate,而这个代理方法在私有代理方法列表躺着:

typedef NS_ENUM(NSInteger, _WKSameDocumentNavigationType) {
    _WKSameDocumentNavigationTypeAnchorNavigation,
    _WKSameDocumentNavigationTypeSessionStatePush,
    _WKSameDocumentNavigationTypeSessionStateReplace,
    _WKSameDocumentNavigationTypeSessionStatePop,
} WK_API_AVAILABLE(macos(10.10), ios(8.0));

@protocol WKNavigationDelegatePrivate <WKNavigationDelegate>
...
- (void)_webView:(WKWebView *)webView navigation:(WKNavigation *)navigation didSameDocumentNavigation:(_WKSameDocumentNavigationType)navigationType;
...
@end

粗略分析下源码,当 navigationType 这个枚举是 _WKSameDocumentNavigationTypeAnchorNavigation 时就表示完成了这次改变 hash 的页面切换。实现这个私有代理从源码来看是无副作用的,MR 记录在这里:https://bugs.webkit.org/show_bug.cgi?id=134855

结论

所以只需要在 WKWebView 的 navigationDelegate 所属类下面实现 _webView:navigation:didSameDocumentNavigation: 方法就能捕获到改变 hash 的页面切换的操作了,和 -webView:didCommitNavigation: 配对可完整感知 WKWebView 的页面切换完成时机。

相关文章

  • 如何感知 WebKit 页面切换

    背景 通常在 WKWebView 打开一个页面,收到页面数据时,代理方法可感知这个时机: 但若是改变页面 hash...

  • 缓冲接口返回较慢加loading动效

    前言 整个页面添加loading导致菜单无法切换,接口返回较慢时,页面感知太卡,不流畅。 [Element-ui局...

  • 小程序根页面切换

    app.js中page页面的第一项就是根页面 如何切换根页面 假如根页面是授权页面,那么当授权成功后切换的根页面是...

  • layui引入tab切换

    这里主要介绍如何在页面中引入tab切换

  • 关于单行、多行文字超出显示省略号

    WebKit浏览器或移动端的页面 在WebKit浏览器或移动端(绝大部分是WebKit内核的浏览器)的页面实现比较...

  • ViewPager页面切换效果

    如何实现那些ViewPager的炫酷的页面切换效果呢?方法很简单,下面我就让我们走进ViewPager的页面切换效...

  • 多行文字省略解决方案

    WebKit浏览器或移动端的页面在WebKit浏览器或移动端(绝大部分是WebKit内核的浏览器)的页面实现比较简...

  • vue 路由切换,页面或组件定位到顶部

    在做vue路由切换的时候,发现一个页面切换到另一个页面,切换组件的时候, 路由可以切换,但是页面和之前页面或组件位...

  • WebKit 打电话失败的问题

    关于WebKit 最近接手的一个app页面全部用H5写的,使用的是WebKit框架。我之前写的app也有H5页面,...

  • 页面切换

    (一)纯代码多页面切换 (二)多个storyboard彼此之间切换 (三)storyboard内部页面的切换 (四...

网友评论

    本文标题:如何感知 WebKit 页面切换

    本文链接:https://www.haomeiwen.com/subject/mfqdaltx.html