一、前言
移动端动态化方案之多,令人叹为观止
1)Web容器增强,这是我们今天讨论的重点,核心就是基于Chromium和WebKit的能力做订制增强版,代表作Cordova,VasSonic等
2)虚拟运行环境,自建一套虚拟环境,要么使用原生能力渲染,要么自己独立渲染,代表作RN,Flutter等
3)布局动态化,这是偏Natvie动态化的方式,一般采用布局引擎+JSON的实现方式,简单高效,布局引擎代表作Yoga,FlexboxLayout等
4)Native动态化,Native的动态化一直是被人所诟病的,热修复就是解决这一问题最好的方式,有人会在Natvie动态化分类里包含插件化,但个人认为一来插件化的技术已经是过去式,二来插件化主要还是在Android上的应用
接下来我们展开聊一聊Web容器增强这一分类
二、背景
Web增强容器技术方案本身并不复杂,但如果想把它做好还需要做很多加持技术的实现,接下来我们来讨论下这两类技术的实现
三、Web容器增强本身技术实现
【1】JS到Native的通信实现
1)iOS实现,利用WKUserContentController的addScriptMessageHandler方法给messageHandlers注入对象,在JS层使用window.webkit.messageHandlers.注入的对象.postMessage()来触发调用,接收到指定对象后,解析出指定类和指定方法,利用OC的Runtime动态调用指定类的指定方法
2)Android有两种实现,一种利用WebView的addJavascriptInterface方法给全局window对象注入对象(Java类中使用@JavascriptInterface标注的方法均可以访问),在JS层直接调用暴露出的方法;一种方法是利用JS的prompt机制,在WebChromeClient的onJsPrompt进行通信处理,Android4.1(JellyBean)之前如果addJavascriptInterface被禁用掉,就只能使用这种方案,这种方案可以保证在所有Android平台上都可以进行JS到Native的通信
双端传递的数据是一致的,为一个数组[callbackId, service, action, actionArgs],callbackId为这次调用的回调ID,在JS层可以通过这个回调ID查询到成功回调和失败回调,service为类名,action为方法名,actionArgs为方法的参数
【2】Native到JS的通信实现
1)iOS实现,利用WKWebView的evaluateJavaScript:completionHandler:方法执行JS层暴露出来的回调Function
2)Android实现,利用WebView的evaluateJavascript方法执行JS层暴露出来的回调Function
这里需要注意,维系前端和客户端callback关系的是callbackID,成功和失败的callback Function会存储在JS层,前端调到客户端的时候会将callbackID传递过来,客户端调回前端再将callbackID传递回去,前端拿到callbackID,再找到成功和失败的callback Function,并且执行
【3】容器增强插件机制实现
对于Web容器的增强,端能力是至关重要的一环,我们在业务开发中会写很多不同种类的端能力,所以将端能力做成插件机制是比较方便的选择,一般的实现思路就是定义一个Plugin的基类,在里边定义初始化,执行,重置,销毁等钩子函数,以及一些系统的生命周期函数,比如iOS的onAppTerminate和onMemoryWarning等,比如Android的onStart,onStop,onNewIntent,onDestroy等
四、Web容器增强加持技术实现
【1】预下载实现
Web容器加载的巨大瓶颈在线上资源,所以将资源本地化是解决这一问题的利器
为了让下载成功率更高,除了上图的方案和策略外,还可以对资源包进行增量更新,让需要下载的资源包更小,从而达到提升下载成功率的目的
【2】预创建实现
WebView的提前创建会节省一部分时间,更极致的做法是提前创建WebView复用池,要针对不同的策略来选择,比如线上的加载走一套,线下的加载走一套
【3】预渲染实现
1)客户端预渲染实现,一般的H5页面都会分为骨架和内容,预渲染主要旨在预渲染骨架部分,骨架部分一般是渲染一次,后边会一直使用,也可以预渲染内容,这种方案叫CSR(Client Side Rendering)
2)服务端预渲染,服务端直接把骨架和内容进行渲染,直接返回给客户端渲染好的HTML,这种方案叫SSR(Server Side Rendering),但这也会增加服务端的计算资源,从而造成成本的增加
上图是从SSR到CSR的5种不同技术形态,其中站在客户端角度使用频次最高的是CSR with Prerendering
【4】网络接管机制实现
对于Web容器的加载,Android上由于系统WebView兼容性较差,都会基于chromium自建浏览内核,或者使用第三方浏览内核,比如腾讯的X5内核;在iOS上使用WKWebView
Web容器的加载核心在网络请求和渲染数据上,如果是自建浏览内核的话,这两点都能做到白盒化,但绝大部分团队是没有这个能力的,所以可以退而求其次进行网络请求的接管,但做网络请求的接管是要建立在对网络的精细优化上的,不然这种技术方案的成效会大打折扣,接下来让我们看下具体接管实现方案
1)Android上的网络接管机制实现
核心关键是WebViewClient的shouldInterceptRequest方法,通过拦截这个方法可以将一个Request对象转成一个Response对象
这里需要注意两点,一是重定向的请求拦截不到,这个需要看业务需求;二是shouldInterceptRequest跑在子线程,所以可以使用同步的网络请求方式获取数据,HttpURLConnection是最佳选择,一般会使用URLStreamHandlerFactory进行底层网络的接管,达到极致优化的目的
2)iOS上的网络接管机制实现
核心关键是利用URL Loading System机制,使用URLProtocol的registerClass注册全局的自定义Protocol对象(建议注册一个Protocol,注册多个Protocol会造成不必要的冲突问题)
在iOS11以上,使用WKURLSchemeHandler来接管http和https的请求,但会抛出"https is a URL scheme that WKWebView handles natively",经查阅webkit源码,需要使用runtime hook webkit的handlesURLScheme方法,不处理http和https的请求,通过[configuration setURLSchemeHandler:schemeHandler forURLScheme:@"https"]来进行接管注册
五、结语
移动端Web容器增强技术是一个古老的技术,很多年依然盛行,个人觉着有2点
1)前端阵营的繁荣
2)在四种动态化技术方案里最复杂,但由于Chromium和Webkit已经非常成熟,所以上手成本较低,可以快速落地
六、参考资料
【1】https://github.com/apache/cordova-android
【2】https://github.com/apache/cordova-ios
【3】https://www.infoq.cn/article/rlDGpUSTHQxeUQ4FQJ7T
【4】https://github.com/Tencent/VasSonic
【5】https://github.com/Tencent/VasSonic/blob/master/assets/sonic%E5%8F%91%E5%B1%95%E5%8E%86%E7%A8%8B.md
【6】https://developers.google.cn/web/updates/2019/02/rendering-on-the-web