发掘 WKWebView 的神奇妙用
文章转载自这里
导览
-
更灵活的 JS 控制
-
解决 native 与 web 命名空间冲突
-
更灵活的 JS 参数传递
-
web 与 native 双向通讯
-
更好的渲染方式
-
多个新 api
更灵活的 JS 控制
WKWebView 中, 使用 javaScriptEnabled
可以 disable 所有 webview 试图去加载的 js 文件, 但是因为 native 很多时候其实都需要通过 EvaluateJavaScript
来进行数据交互. 直接禁用 javaScriptEnabled
实际上是非常粗粒度的行为. 因此这个属性将会被抛弃 取而代之的新属性 allowsContentJavaScript
, 使用这个属性可以禁用内联的 JS, url 方式加载的远端 js, 以及本地路径的 js 文件, 但是 native 直接执行的 js 仍然有效 在 decidePolicy
代理方法中使用 WKWebpagePreferences
, 更可以对每个 web 页面进行更细致的配置, 来决定当前 web 页面是否加载 js 以下图代码为例, 针对特定的 url, 将 allowContentJavaScript
设为 false, 可以使 web 页面更为灵活, 例如在某些特定的页面, 你只希望获得 html 的布局能力, 然后点击交互网络请求等细节交由 native 接管, 这样可以使页面加载更为迅速
解决 native 与 web 命名空间冲突
图片a.png如上图所示, 例如 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"
为了解决这个问题, 苹果引入了 callAsyncJavaScript
api
如上图, 在执行这段 js 时, 这段 js 的 scope 内使用到的变量, 可以直接通过
arguments
这个参数进行传递, 并且可以直接使用 swift 的字典数据类型
图片e.png
上图是一个更神奇的妙用
在这段 js 中 return 了一个 promise 对象, 因此 native 的 completion 会等待这个 promise 被 resolve 了, 才会执行, 并且可以在 native 获取到 fetch 成功后的数据
web 与 native 双向通讯
图片f.png以上图为例, 这是现有 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
图片x.png
如上图, 在 js 侧就可以通过 promise 的形式获取到这个 replyHandler 的入参并执行后续的逻辑
因此, 这套双向通讯的机制很好地利用了 js 的 promise 异步机制与 swift 的代码块相结合, 比起开发者自己去构建双向通讯机制, 会更为友好和完善
更好的渲染方式
在 css 中, 媒体查询样式可以使得同一份 css 代码在不同的平台运行有不同的展示效果
例如上图的页面, 被标记的 header 和 footer 实际上在 iPhone 上并不希望展示, 因为更多的时候 app 有着自己本身的 header 和 footer, 这个与 web 的 header 和 footer 在 ui 上造成冲突
图片z.png
因此在写 css 的时候, 我们可以通过媒体查询这一特性, 使得 css 在
no-header-and-footer-device
里自动隐藏这些 ui 元素图片q.png
如上图, 通过
mediaType
这个新的对象属性, 就可以实现 css 媒体查询的自适应
多个新 api
WebView 内容查找
find.png使用上图的
find
api, 可以使 WebView 直接选中并滚动到对应的区域
生成 PDF
图片pdf.png使用上图的
createPDF
api, 可以截图整个 WebView 的全部内容, 包括屏幕外的, 并作为 pdf 输出
Archive Web Content
图片arc.png使用上图的
createWebArchiveData
api, 可以为当前页面的所有数据创建一个 snapshot, 包括整个页面的 html, js, css 以便重新运行这个 ArchiveData 时, 可以重现当时 Web 页面的所有内容, 因此也非常适用于 Debug
APP-bound domains
图片app.png例如上图的 web content 中, 出现了一个外部的 url, 我们希望用户能在浏览器中跳转到这个 url, 但同时我们不希望用户在这个新的 web 页面中, 信息被泄露或者遇到其他安全问题, 按苹果的描述, 使用 APP-bound domains 可以使得浏览器在指定 domain 以外的 web 应用中, 禁止它们与用户有
deep interaction
, 不过具体没有说明会禁止什么行为 要使用这一能力, 我们需要在 info.plist 中增加自己的 domain 白名单图片r.png
总结
今年的 WebKit 更新, 个人感觉苹果升级了更多 js 与 native 的交互能力, 充分运用的话能使得我们的 hybrid 应用的代码有很好的简化.