ZKX's LAB

WKWebView 新变化:发掘 WKWebView 的神奇妙用

2020-12-15新闻15

作者:RyRYanZhong,iOS 开发,字节跳动研发工程师

来源公众号丨老司机技术周报(ID:LSJCoding)导览更灵活的 JS 控制解决 native 与 web 命名空间冲突更灵活的 JS 参数传递web 与 native 双向通讯更好的渲染方式多个新 api更灵活的 JS 控制

WKWebView 中, 使用 javaEnabled 可以 disable 所有 webview 试图去加载的 js 文件, 但是因为 native 很多时候其实都需要通过 EvaluateJava 来进行数据交互. 直接禁用 javaEnabled 实际上是非常粗粒度的行为. 因此这个属性将会被抛弃取而代之的新属性 allowsContentJava , 使用这个属性可以禁用内联的 JS, url 方式加载的远端 js, 以及本地路径的 js 文件, 但是 native 直接执行的 js 仍然有效在 decidePolicy 代理方法中使用 WKWebpagePreferences, 更可以对每个 web 页面进行更细致的配置, 来决定当前 web 页面是否加载 js以下图代码为例, 针对特定的 url, 将 allowContentJava 设为 false, 可以使 web 页面更为灵活, 例如在某些特定的页面, 你只希望获得 html 的布局能力, 然后点击交互网络请求等细节交由 native 接管, 这样可以使页面加载更为迅速

解决 native 与 web 命名空间冲突

如上图所示, 例如 native 执行了 js 后将 window.commentDetails 设为 null, 这样会导致 js 内的同名函数直接被置空, 这种 native 与 web 的命名空间冲突问题非常难以察觉, 甚至有恶意的 web 应用会故意覆盖同名函数来达到改变应用行为或者获取用户信息的能力.要彻底解决, 就需要将 native 的 js 运行环境跟 web 的 js 运行环境进行隔离, 因此 native 需要一个自己的 global object , WKContentWorld 就是为此而生的

如上图所示, 使用方式是在 native 执行 js 的 api 里, 加上 .defaultClient 的参数

更灵活的 JS 参数传递

例如我们希望在 native 侧构建一个完整的 dom 节点并插入到 html 时, 我们需要 hardcode 很多 js 中数据结构的代码

例如下图中的 maring: "0" , 这里的 0 在 native 的其他地方其实是 Int 类型, 但因为让这段 js 代码能转 string 从而让 webview 调用, 因此需要 hardcore 成 "0"

为了解决这个问题, 苹果引入了 callAsyncJava api

如上图, 在执行这段 js 时, 这段 js 的 scope 内使用到的变量, 可以直接通过 arguments 这个参数进行传递, 并且可以直接使用 swift 的字典数据类型

上图是一个更神奇的妙用

在这段 js 中 return 了一个 promise 对象, 因此 native 的 completion 会等待这个 promise 被 resolve 了, 才会执行, 并且可以在 native 获取到 fetch 成功后的数据

web 与 native 双向通讯

以上图为例, 这是现有 api 下很常见的 js 和 native 的通讯方式, web 直接调用 webkit 中的 postMessage 来调用 native, 在现有的 webkit 框架下只能实现 web to native 的单向调用, 如果要双向通讯, 则需要开发者在 postMessage 的基础上构建自己的双向通讯机制.

但在新的 WKWebView 中, 苹果加强了 WebView 与 native 的通讯能力, postMessage 在 js 的返回值是从 undefine 变成了 promise 对象

如上图, 在 userContentController 收到 message 后, 可以添加一个 replyHandler 参数, 当你执行这个 replyHandler 时, js 对应的 promise 就会被 resolve

如上图, 在 js 侧就可以通过 promise 的形式获取到这个 replyHandler 的入参并执行后续的逻辑

因此, 这套双向通讯的机制很好地利用了 js 的 promise 异步机制与 swift 的代码块相结合, 比起开发者自己去构建双向通讯机制, 会更为友好和完善

更好的渲染方式

在 css 中, 媒体查询样式可以使得同一份 css 代码在不同的平台运行有不同的展示效果

例如上图的页面, 被标记的 header 和 footer 实际上在 iPhone 上并不希望展示, 因为更多的时候 app 有着自己本身的 header 和 footer, 这个与 web 的 header 和 footer 在 ui 上造成冲突

因此在写 css 的时候, 我们可以通过媒体查询这一特性, 使得 css 在 no-header-and-footer-device 里自动隐藏这些 ui 元素

如上图, 通过 mediaType 这个新的对象属性, 就可以实现 css 媒体查询的自适应

多个新 apiWebView 内容查找

使用上图的 find api, 可以使 WebView 直接选中并滚动到对应的区域

生成 PDF

使用上图的 createPDF api, 可以截图整个 WebView 的全部内容, 包括屏幕外的, 并作为 pdf 输出

Archive Web Content

使用上图的 createWebArchiveData api, 可以为当前页面的所有数据创建一个 snapshot, 包括整个页面的 html, js, css 以便重新运行这个 ArchiveData 时, 可以重现当时 Web 页面的所有内容, 因此也非常适用于 Debug

APP-bound domains

例如上图的 web content 中, 出现了一个外部的 url, 我们希望用户能在浏览器中跳转到这个 url, 但同时我们不希望用户在这个新的 web 页面中, 信息被泄露或者遇到其他安全问题, 按苹果的描述, 使用 APP-bound domains 可以使得浏览器在指定 domain 以外的 web 应用中, 禁止它们与用户有 deep interaction , 不过具体没有说明会禁止什么行为要使用这一能力, 我们需要在 info.plist 中增加自己的 domain 白名单

总结

今年的 WebKit 更新, 个人感觉苹果升级了更多 js 与 native 的交互能力, 充分运用的话能使得我们的 hybrid 应用的代码有很好的简化.

#技术编程

随机阅读

qrcode
访问手机版