hybrid

核心诉求

  • 应对快速迭代开发,无论 Android 还是 iOS 原生内容的发布都会有诸多限制和审核,不利于快速的迭代需求

  • 降低人力成本,理论来说不必需要 Android 和 iOS 两波开发者(实际开发中,只有有过开发经验的开发者,才能达到这个要求,新人直接上手人力工时消耗只多不少)

  • 一套代码,多处运行,本质来说还是降低人力需求

存在问题

  • 开发者能力要求较高,需要有一定的开发经验和调试技巧

  • 性能和交互,不能和原生比拟

混合开发模式

  • webview + h5

  • JS + 原生

  • flutter 独立开发

webview + h5

webview + h5 的开发形式,h5 整个项目内容独立,有自己的单独域名,即可在浏览器访问使用,又可在 app 中使用,在app中使用可以理解为在一个没有UI的浏览器里打开了,h5域名并访问

webview 是什么

一个基于 webkit 的引擎,可以解析DOM,CSS,展示html页面的控件,可以把它当做没有UI界面的浏览器

1
2
3
4
5
// webkit 组成

|———— WebCore
webkit +
|———— JavaScriptCore

WebCore:包含了目前被各个浏览器所使用的WebKit共享部分,是加载和渲染网页的基础部分,具体包括HTML解释器、CSS解释器等

JavaScriptCore:是WebKit中的默认JavaScript引擎。在Google中,它被替换为V8引擎

Andoid平台

SDK 中有一个控件叫 WebView

IOS/MacOS 平台

SDK 中有一个控件叫 WebView/UIWebView/WKWebView(UIView/NSView)

不管是ios还是安卓,自带浏览器底层都是基于webkit(blink也是它的一个分支)的,然后各自系统中均带有webview控件,也是基于webkit引擎,所以不管通过APP调用webview展示html页面还是通过在浏览器打开html页面,效果是一样的

开发形式

原生和h5无交互

  • 比如整个产品是 app 开发的,只将部分频繁变动的资讯类内容作为h5开发(无需登录,权限等),没有和原生实际的交互(唤起语音,视频等)

  • 该模式相当于纯前端开发内容,只需把开发,部署好的前端域名交给原生开发嵌入到 webview 即可

原生和h5有交互

  • 相较于无交互模式,有交互模式,需要额外对接 jsBridge 来保证 native 和 h5 数据通信
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+----------------------------+
| +--------+ |
| | h5 | |
| +--------+ |
| | |
| +--------+ |
| |jsBridge| |
| +--------+ |
| | |
+----------------------------+
| | |
| +--------+ |
| | SDK | |
| +--------+ |
| | |
| +--------+ |
| | Native | |
| +--------+ |
+----------------------------+

JavaScript 和 Native 通信,基于 WebView 的机制和开放的 API, 三种常见方案:

  • API 注入:原理其实就是 Native 获取 JavaScript 环境上下文,并直接在上面挂载对象或者方法(一般直接挂载window),使 js 可以直接调用,Android 与 IOS 分别拥有对应的挂载方式

  • WebView 中的 prompt/console/alert 拦截,通常使用 prompt ,因为这个方法在前端中使用频率低,比较不会出现冲突

  • WebView URL Scheme 跳转拦截,Web 端通过某种方式(例如 iframe.src)发送 URL Scheme 请求,之后 Native 拦截到请求并根据 URL SCHEME(包括所带的参数)进行相关操作

1
2
3
4
5
6
7
8
// 实现代码
var WVJBIframe = document.createElement('iframe')
WVJBIframe.style.display = 'none'
WVJBIframe.src = 'ddjsscheme://__bridge_loaded__'
document.documentElement.appendChild(WVJBIframe)
setTimeout(function () {
document.documentElement.removeChild(WVJBIframe)
}, 0)

JavaScript 调用 Native

jsBridge 则是基于 API 注入的方式实现

注入 API 方式的主要原理是,通过 WebView 提供的接口,向 JavaScript 的 Context(window)中注入对象或者方法,让 JavaScript 调用时,直接执行相应的 Native 代码逻辑,达到 JavaScript 调用 Native 的目的

jsBridge 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
var bridgeModuleDict = {
// 是否初始化成功
isInit: false,
// 想要调用的事件句柄,参数,异步回调
execute: function (handlerName, param, cb) {
callHandlerEx(handlerName, param, cb)
}
}

// 原生那边提供的所有api均采用异步的方式,因此所有的事件采用异步回调的方式,在回调成功后执行业务逻辑
function callHandlerEx(handlerName, param, cb) {
var handleParam = param || {}
var onSuccess = handleParam.onSuccess || function () {}
var onFail = handleParam.onFail || function () {}
var onCancel = handleParam.onCancel || function () {}

var handleFullParam = {
handlerName: handlerName,
params: handleParam
}

var responseCallback = function (responseData) {
var response = JSON.parse(responseData || '')
var errorCode = response.errorCode
var result = response.result
// errorCode 错误码主要看和原生的约定了,一般 '0' 是成功,'-1' 是取消,都不是就是失败
errorCode === '0' ? onSuccess && onSuccess(result) : errorCode === '-1' ? onCancel && onCancel(result) : onFail && onFail(result, errorCode)
cb && cb(response)
}

// 该方法是native注入到window上到方法
window.WebViewJavascriptBridge.callHandler('dd.native.call', handleFullParam, responseCallback)
}

window.spBridge = bridgeModuleDict

// 初始化 deviceReady 事件
function setupWebViewJavascriptBridge() {
if (!window.spBridge.isInit) {
var readyEvent = document.createEvent('Event')
readyEvent.initEvent('deviceReady', true, true)
document.dispatchEvent(readyEvent)
window.spBridge.isInit = true
}
}

// 初始化整个jsBridge
if (!!spBridge && !spBridge.isInit) {
setupWebViewJavascriptBridge()
}

上面代码,主要帮助理解 jsBridge 的通信机制(项目中的缩减,脱敏处理),实际开发中,还有原生提供的一份约定的 api 文档,提供给前端快速查询和使用

接口约定形式

1
window.spBridge.execute(url, param, resCallback)
参数 说明
url 命名空间.功能.方法;例如:sys.communication.call
params {title: ‘hello world’}
resCallback json字符串{“code”: “0”, “message”: “”, “result”: {“key”: “value”}}

拨打电话

1
window.spBridge.execute("sys.communication.call", {"phone": 12345678911}, resCallback)

Native 调用 JavaScript

  • 无论 iOS 的 UIWebView 还是 WKWebView,或 Android 的 WebView 组件,直接调用相应的 API 即可

  • Native 调用 JavaScript,本质是拼接 JavaScript 字符串,从外部调用,JavaScript 的方法必须在 window 上

JSBridge 如何引用

1、由 Native 端进行注入

  • 注入方式和 Native 调用 JavaScript 类似,直接执行桥的全部代码

  • 优点:JSBridge 的版本容易与 Native 保持一致,不需对 JSBridge 版本兼容

  • 缺点:注入时机不确定,需要实现注入失败后重试的机制

2、由 JavaScript 端引用

  • 直接与 JavaScript 一起执行

  • 优点:JavaScript 端可以确定 JSBridge 的存在,直接调用即可

  • 缺点:如果桥的实现方式有更改,需要兼容处理版本

JS + 原生

主要:React Native 和 Weex

在编译和运行时候会直接把对应的UI映射成原生UI,所以最终运行在设备上的还是原生的页面

weex 工作原理

weex 踩坑

flutter

实现了一个自绘引擎,使用Flutter自己的布局和绘制系统

学习成本

  • 一门新的语言 dart,尽管语言都有相通性,但是想好写出好的代码,必要的知识肯定要深入学习的

  • 新的框架写法,不要不断的使用才能熟练掌握

flutter 原理

flutter 环境搭建

返回
顶部