在移动互联网时代,任何一个公司的产品都少不了APP,这是人们生活水平提高的结果。随着智能手机越来越普及,移动端成为兵家必争之地。正所谓“得移动端者得天下”,移动端已成为互联网领域最大的流量分发入口,一大批互联网公司正是在这大趋势下崛起。但在移动端刚出来那会儿,Android、iOS都是各自为营,分开开发的,团队之间是独立的,从需求调研,研发,测试,上线一整套流程需要的周期很长,少则几个月,多达1年甚至更长。这样的项目周期满足不了高速迭代发展的需求,伴随着移动互联网的高速发展,公司间竞争越来越激烈,如何将好想法快速落地、快速试错,成为备受关注的问题。因为只有尽快占领市场,才能在移动互联网中分得一杯羹。在这种场景下,跨平台框架应运而生
众所周知,Android 应用采用 Java 或 Kotlin 编写,iOS 应用采用 Objective-C 或 Swift 编写,Web 端采用 HTML /CSS/JavaScript 编写。当需要开发支持多端的应用,每一端都需要独立研发、测试,一直到上线,以及后续的维护工作,工作量成倍增涨,势必延长研发周期。因此跨平台框架的出现是为了解决多端独立开发,开发维护成本高,项目周期长的问题。
几种APP开发模式
主流应用程序大体分为四大类型:Native App、Hybrid App 、Web App,随着大前端的流行又衍生出很多跨平台解决方案和框架。什么是大前端:大前端是指移动端(主要是安卓和IOS),PC端(主要是网页web),小程序,也包括车载终端等客户端。随着终端越来越多于是出现了跨平台应用,跨平台应用就是实现一套前端代码,可以在PC端,IOS端,安卓端,还有其他终端(如果车载终端等)完美运行的应用,人们称之为跨平台应用。
Native App
只用平台原生的开发语言,开发框架来做开发APP。是一种基于智能手机本地操作系统如iOS、Android、WP并使用原生程式编写运行的应用程序。由于是原生开发,性能比较好可以直接调用摄像头,蓝牙等硬件。 这种原生程序一般依托于操作系统,有很强的交互,是一个完整的App,可拓展性强。缺点是对于不同的操作系统需要使用特定的语言开发,不具备移植性,IOS系统开发语言:Objective-C / Swift
。Android系统 开发语言:Java / Kotlin
优点:
- 直接依托于操作系统,交互性最强,性能最好,体验是最优。
- 功能最为强大,特别是在与系统交互中,几乎所有功能都能实现
缺点:
- 开发成本高,无法跨平台,不同平台Android和iOS上都要各自独立开发
- 门槛较高,原生人员有一定的入门门槛。相比广大的前端人员而言,较少
- 更新缓慢,特别是发布应用商店后,需要等到审核周期。原生应用更新是一个很大的问题,Android中还能直接下载整包APK进行更新,但在IOS中,如果是发布AppStore,必须通过AppStore地址更新,而每次更新都需要审核,所以无法达到及时更新
- 维护成本高
Hybrid App
用 JS+H5 开发应用的技术,可以通过某种手段调用系统能力。从原生开发者的角度,混合应用其实就是一个原生开发的 App 外壳,这个外壳将原生功能封装成很多 API 并注入到 WebView 里,然后将前端页面打包进 App,App 启动时用 WebView 加载前端页面,剩下的就全交给前端了。混合应用框架的本质就是这个原生 App 外壳,这个外壳重点实现三件事,只要做到这三件事,基本上就可以被称为混合开发通用框架。
- 实现原生与前端(Javascript)的交互
- 封装基本的原生功能,供前端调用
- 实现原生插件机制,供原生开发者扩展功能
简而言之,在Hybrid模式下,由原生提供统一的API给JS调用,实际的主要逻辑有Html和JS来完成,而由于最终是放在webview中显示的,所以只需要写一套代码即可。
优点:
- 开发成本较低,可以跨平台,调试方便,前端人员稍微学习下JS api的调用即可,无需两个独立的原生人员。一般Hybrid中的跨平台最少可以跨三个平台:Android App,iOS App,普通webkit浏览器
- 维护成本低,功能可复用,如果代码合理,只需要一名前端就可以维护多个app,而且很多功能还可以互相复用
- 更新较为自由,虽然没有Web App更新那么快速,但是Hybrid中也可以通过原生提供api,进行资源主动下载,达到只更新资源文件,不更新apk(ipa)的效果
- 针对新手友好,学习成本较低。这种开发模式下,只需要前端人员关注一些原生提供的API,具体的实现无需关心,没有新的学习内容,只需要前端人员即可开发
- 功能更加完善,性能和体验要比起web app好太多。因为可以调用原生api,所以很多功能只要原生提供出就可以实现,另外性能也比较接近原生了
- 部分性能要求的页面可用原生实现。这应该是Hybrid模式的最多一个好处了,因为这种模式是原生混合web,所以我们完全可以将交互强,性能要求高的页面用原生写,然后一些其它页面用JS写,嵌入webview中以达到最佳体验
缺点:
- 相比原生,性能仍然有较大损耗。这种模式受限于webview的性能桎梏,相比原生而言有不少损耗,体验无法和原生相比。不过相比于原生的诸多优点,这个缺点显然可以接受
- 不适用于交互性较强的App,这种模式的主要使用一些新闻阅读类,信息展示类的App。但是不适用于一些交互较强或者性能要求较高的App
Web App
指采用H5语言写出的App,不需要下载安装。类似于现在所说的轻应用,生存在浏览器中的应用,可以说是触屏版的网页应用。类比于电脑网页中的应用,只是移动端有触屏和手势。Web App生存于浏览器里,宿主是浏览器。对比与原生应用缺点也很明显,因为web App实际上就是手机上的网页,依赖于浏览器内核,无法调用系统权限如地理信息,通讯录,语音,摄像头等等。而且由于不同的浏览器身的属性不尽相同,如浏览器自带的手势,页面切换方式,链接跳转方式,存在版本兼容问题等等
优点:
- 开发成本低,可以跨平台,调试方便。Web App一般只需要一个前端人员开发出一套代码,然后即可应用于各大主流浏览器(特殊情况可以代码进行下兼容)。没有新的学习成本,而且可以直接在浏览器中调试。
- 维护成本低
- 更新最为快速,由于web app资源是直接部署在服务器端的,所以只需要替换服务器端的文件,用户访问是就已经更新了(当然需要解决一些缓存问题)
- 无需安装App,不会占用手机内存,通过浏览器即可访问,无需安装,用户就会比较愿意去用
缺点:
- 性能低,用户体验差,由于是直接通过的浏览器访问,所以无法使用原生的API,操作体验不好。而且由于依赖于浏览器,部分手势还可能和浏览器的手势冲突
- 依赖于网络,页面访问速度慢,耗费流量。Web App每次访问都需要去服务端加载资源访问,所以必须依赖于网络。而且网速慢时访问速度很不理想,特别是在移动端,如果网站优化不好会无故消耗大量流量
- 功能受限,大量功能无法实现。只能使用Html5的一些特殊api,无法调用原生API,所以很多功能存在无法实现情况
- 临时性入口,用户留存率低。这既是它的优点,也是缺点,优点是无需安装。确定是用完后有时候很难再找到,或者说很难专门为某个web app留存一个入口,导致用户很难再次使用
React Native App
Facebook发起的开源的一套新的APP开发方案,Facebook在当初深入研究Hybrid开发后,觉得这种模式有先天的缺陷,所以果断放弃,转而自行研究,后来推出了自己的“React Native”方案,不同于H5,也不同于原生,更像是用JS写出原生应用。用 javascript语言就能同时编写 ios,android,以及后台的一项技术。用React Native 就是真正意义上的全栈,一个项目从头到尾可以一个人搞定。一开始我以为React Native App属于Hybrid App。因为它们都使用JS写应用,只不过Hybrid App用WebView 实现,而React Native App自己写了一个引擎实现。但其实不是,在开发的角度来说,两者使用起来差不太多,都可以用JS写App,但是React Native App编译之后生成的是原生的控件,所以是原生应用。但开发模式上与原生App差别又很大,应该说是一种跨平台解决方案的新思路,其实很多大公司都已经在使用React Native开发了
优点:
- 开发人员单一技术栈,一次学习,跨平台开发。这种模式是统一由JS编写,有着独特的语法,所以只需要学习一次,即可同时开发Android和iOS
- 相对Hybird app或者Webapp。不用Webview,彻底摆脱了Webview让人不爽的交互和性能问题,有较强的扩展性,这是因为Native端提供的是基本控件,JS可以自由组合使用可以直接使用Native原生的动画(在FB Group这个app里面,面板滑出带一点果冻弹动,面板基于某个点展开这种动画随处可见,这种动画用Native code来做小菜一碟,但是用Web来做就难上加难)。
- 相对于Native App。可以通过更新远端JS,直接更新app,不过这快成为各家大型Native app的标配了
- 社区繁荣,遇到问题容易解决。这应该是React Native的很大一个优势,不像Hybrid模式和原生模式一样各自为营,这种模式是Facebook统一发起的,所以有一个统一的社区,里面有大量资源和活跃的人员,对开发者很友好
- 性能体验高于Hybrid,不逊色与原生。这种模式和Hybrid不一样,Hybrid中的view层实际上还是dom,但是这种模式的view层是虚拟dom,所以性能要高于Hybrid,距离原生差距不大。这种模式可以认为是用JS写原生,即页面用JS写,然后原生通过Bridge技术分析JS,将JS内容单独渲染成原生Android和iOS,所以也就是为什么性能不逊色原生
缺点:
- 从Native到Web,要做很多概念转换,势必造成双方都要妥协。最终web要用一套CSS的阉割版,Native要费劲地把这个阉割版转换成native原生的表达方式(比如iOS的Constraint\origin\Center等属性),两边都会不爽
- 虽然可以部分跨平台,但并不是Hybrid中的一次编写,两次运行那种,而是不同平台代码有所区别。这种模式实际上还是JS来写原生,所以Android和iOS中的原生代码会有所区别,如果需要跨平台,对开发人员有一定要求。
WebView简介
为了理解Hybrid App与React Native App的区别,你不得不知道的组件就是WebView。Android WebView在Android平台上是一个特殊的View, 基于webkit引擎、展现web页面的控件,这个类可以被用来在你的app中仅仅显示一张在线的网页,还可以用来开发浏览器。WebView内部实现是采用渲染引擎来展示view的内容,提供网页前进后退,网页放大,缩小,搜索。现在很多App
里都内置了Web网页(Hybrid App
),比如说很多电商平台,淘宝、京东、聚划算等等,如下图
Android的Webview在低版本和高版本采用了不同的webkit版本内核,4.4后直接使用了Chrome。WebView控件功能强大,除了具有一般View的属性和设置外,还可以对url请求、页面加载、渲染、页面交互进行强大的处理。
Webview类常用方法
加载url,加载方式根据资源分为三种,如下
//方式1. 加载一个网页:
webView.loadUrl("http://www.google.com/");
//方式2:加载apk包中的html页面
webView.loadUrl("file:///android_asset/test.html");
//方式3:加载手机本地的html页面
webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");
// 方式4: 加载 HTML 页面的一小段内容
WebView.loadData(String data, String mimeType, String encoding)
// 参数说明:
// 参数1:需要截取展示的内容
// 内容里不能出现 ’#’, ‘%’, ‘\’ , ‘?’ 这四个字符,若出现了需用 %23, %25, %27, %3f 对应来替代,否则会出现异常
// 参数2:展示内容的类型
// 参数3:字节码
WebView的状态
//激活WebView为活跃状态,能正常执行网页的响应
webView.onResume() ;
//当页面被失去焦点被切换到后台不可见状态,需要执行onPause
//通过onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。
webView.onPause();
//当应用程序(存在webview)被切换到后台时,这个方法不仅仅针对当前的webview而是全局的全应用程序的webview
//它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。
webView.pauseTimers()
//恢复pauseTimers状态
webView.resumeTimers();
//销毁Webview
//在关闭了Activity时,如果Webview的音乐或视频,还在播放。就必须销毁Webview
//但是注意:webview调用destory时,webview仍绑定在Activity上
//这是由于自定义webview构建时传入了该Activity的context对象
//因此需要先从父容器中移除webview,然后再销毁webview:
rootLayout.removeView(webView);
webView.destroy();
Webview与 Js的交互
Webview既然能够嵌套HTML页面,而HTML中最重要的就是JS事件了,那么Webview与JS事件的交互就是开发中经常需要面对的问题,这里介绍一种比较通用的方式,通过 WebChromeClient
的onJsAlert()
、onJsConfirm()
、onJsPrompt()
方法回调拦截JS对话框alert()
、confirm()
、prompt()
消息。
javascript.html:以.html格式放到src/main/assets文件夹里
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson_Ho</title>
<script>
function clickprompt(){
// 调用prompt()
var result=prompt("js://demo?arg1=111&arg2=222");
alert("demo " + result);
}
</script>
</head>
<!-- 点击按钮则调用clickprompt() -->
<body>
<button type="button" id="button1" onclick="clickprompt()">点击调用Android代码</button>
</body>
</html>
当使用mWebView.loadUrl("file:///android_asset/javascript.html")
加载了上述JS代码后,就会触发回调onJsPrompt()
,具体如下:
- 如果是拦截警告框(即
alert()
),则触发回调onJsAlert()
; - 如果是拦截确认框(即
confirm()
),则触发回调onJsConfirm()
;
Webview:在Android通过WebChromeClient复写onJsPrompt()
public class MainActivity extends AppCompatActivity {
WebView mWebView;
// Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = mWebView.getSettings();
// 设置与Js交互的权限
webSettings.setJavaScriptEnabled(true);
// 设置允许JS弹窗
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
// 先加载JS代码
// 格式规定为:file:///android_asset/文件名.html
mWebView.loadUrl("file:///android_asset/javascript.html");
mWebView.setWebChromeClient(new WebChromeClient() {
// 拦截输入框(原理同方式2)
// 参数message:代表promt()的内容(不是url)
// 参数result:代表输入框的返回值
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
// 根据协议的参数,判断是否是所需要的url(原理同方式2)
// 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
//假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的)
Uri uri = Uri.parse(message);
// 如果url的协议 = 预先约定的 js 协议
// 就解析往下解析参数
if ( uri.getScheme().equals("js")) {
// 如果 authority = 预先约定协议里的 webview,即代表都符合约定的协议
// 所以拦截url,下面JS开始调用Android需要的方法
if (uri.getAuthority().equals("webview")) {
//
// 执行JS所需要调用的逻辑
System.out.println("js调用了Android的方法");
// 可以在协议上带有参数并传递到Android上
HashMap<String, String> params = new HashMap<>();
Set<String> collection = uri.getQueryParameterNames();
//参数result:代表消息框的返回值(输入值)
result.confirm("js调用了Android的方法成功啦");
}
return true;
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
// 通过alert()和confirm()拦截的原理相同,此处不作过多讲述
// 拦截JS的警告框
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
// 拦截JS的确认框
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
}
);
}
}
最终结果展示
通过刚刚的代码,对Webview的作用应该理解了,借此对刚刚说的几种App类型用大白话做一个总结吧。
App | 解释 |
---|---|
Native App | 原生应用。类比于windows系统上的桌面应用,可以轻松的调用系统提供的API 但移植性差,例如windows上的桌面应用不能直接在Linux上使用 |
Web App | Web 应用。严格来说,我觉得它不能称之为App,它的实现类比于windows上的网页 其实就可以理解为它是移动端的网页应用。而我理解的App是安装在操作系统上的App |
Hybrid App | 混合应用。它实际上可以说是原生组件提供的功能,在系统原生组件中提供了WebView组件 这个组件特殊的地方在于,可以在这个组件中展示HTML页面。因此很多人说 现在的App开发就是用H5开发,然后套了个外壳,这个壳指的就是WebView |
React Native App | 原生应用。虽然开发的时候是用JS开发,但是编译后是原生组件。可以这么理解 React Native框架提供了React组件到原生组件的映射,每个JS事件可以对应到原生组件的事件 最终编译后把React组件和对应的JS事件代码翻译成原生组件和事件代码 |
以上是个人理解。可能会有类比不恰当的地方,但大概就是这么个意思吧
APP对比
Native App | Hybrid App | Web App | React Native App | |
---|---|---|---|---|
App特性比较 | ||||
图像渲染 | 本地API渲染 | 混合 | H5 | 本地API渲染 |
性能 | 最快 | 慢 | 快 | 快 |
界面 | 原生 | 模仿 | 模仿 | |
发布 | App Store | App Store | Web | App Store |
本机设备访问 | ||||
照相机 | 支持 | 支持 | 不支持 | 支持 |
系统通知 | 支持 | 支持 | 不支持 | 支持 |
定位 | 支持 | 支持 | 支持 | 支持 |
蓝牙 | 支持 | 支持 | 不支持 | 支持 |
其他比较 | ||||
开发成本 | 高 | 中 | 低 | 中 |
维护更新 | 复杂 | 简单 | 简单 | 简单 |
体验 | 优 | 优 | 差 | 优 |
Store或Market | 认可 | 认可 | 不认可 | 认可 |
安装 | 需要 | 需要 | 不需要 | 需要 |
跨平台 | 差 | 优 | 优 | 优 |
网络要求 | 支持离线 | 依赖网络 | 依赖网络 | 支持离线 |
资源存储 | 本地 | 本地和服务器 | 服务器 | 本地 |
适用场景 | 1.偏操作互动多的工具类应用 2.需要访问特定的原生API 3.对速度要求较高 |
1.同Native App中提到的适用场景 2.需要频繁小幅度更新 |
1.作为对非核心业务在移动端的入口补足 2.作为用户轻量、低频使用的体验增强 3.作为吸引用户安装Native App的引导页 |
1.适用于快速迭代不同平台的App应用 2.绝大部分功能在不同系统上可以使用一套代码解决,成本低 |
其他类似React Native App框架
React Native App的实现原理最主要的就是在编译时,把JS组件转换为原生组件。市面上还有一些实现了这种转换功能的跨平台框架。
Weex
Weex 是阿里开源的一款跨平台移动开发工具,是阿里巴巴开发团队在RN的成功案例上,重新设计出的一套开发模式,站在了巨人肩膀上并有淘宝团队项目做养料,广受关注,2016年4月正式开源,并在v2.0版本官方支持Vue.js,与RN分庭抗礼。Weex 这个名字是取得 weeks 的谐音。Weex能够完美兼顾性能与动态性,让移动开发者通过简捷的前端语法写出Native级别的性能体验,并支持iOS、安卓、YunOS及Web等多端部署。Weex 是一个使用 Web 开发体验来开发高性能原生应用的框架。使用同一套代码就可以构建 Android、iOS 和 Web 应用。Weex 的结构是解耦的,渲染引擎与语法层是分开的,目前主要支持 Vue.js 和 Rax 这两个前端框架。Weex 在 iOS 和 Android 上都实现了一个渲染引擎,并提供了一套基础的内置组件。基于这些组件,你可以用JS封装更多的上层组件。
Weex于2016年6月开始发布版本,第一个版本号为v0.5.0。
Flutter
Flutter是谷歌推出的跨平台项目,它的前身是Sky项目,起源于2015年。Sky项目一开始就定位Dart作为开发语言,使用Dart语言开发移动端项目,Sky它不依赖于平台,它的代码可以运行在Android、iOS设备上,真正做到了“一次代码,处处运行”,让你在Android、iOS设备上拥有接近原生的体验。主要特点是跨平台、高保真、有些性能。Flutter提供了丰富的组件、接口,开发者可以很快地为 Flutter添加 Native扩展。同时Flutter还可以使用 Native引擎渲染视图,这无疑能为用户提供良好的体验。
Flutter在2017年5月发布了第一个版本v0.0.6。
其他跨平台框架
市面有很多实现了跨平台的框架,其实现原理基本属于上面四大类App中的一种,下面分别介绍一下它们
Taro
小程序跨平台开发,一款可以用TSX、JSX和React语法开发小程序的框架,内置了一些UI组件,还有物料市场,目前看势头很好。还可以集成React-native,真正做到一套代码多处运行,不仅能编译成各种平台小程序,还可以是RN的应用,还支持快应用。官网地址点这里。现如今市面上端的形态多种多样,Web、React-Native、微信小程序等各种端大行其道,当业务要求同时在不同的端都要求有所表现的时候,针对不同的端去编写多套代码的成本显然非常高,这时候只编写一套代码就能够适配到多端的能力就显得极为需要。
使用 Taro,我们可以只书写一套代码,再通过 Taro 的编译工具,将源代码分别编译出可以在不同端(微信/百度/支付宝/字节跳动/QQ/京东小程序、快应用、H5、React-Native 等)运行的代码。
Taro的源码我没看过,但是我看里面用了很多他们自己写的babel包,应该是JIT模式,加入了中间层,把你写的东西,编译成了小程序可以执行的代码,个人认为小程序不要做得太复杂,不然你还不如做个APP,轻量跨平台,自然是最快速的,而且可以使用TSX语法,React,太好了。
快应用
华为、小米等九大手机厂商为了跟小程序竞争搞出来的,基于硬件平台共同推出的新型应用生态。像RN这些框架,会内置一些渲染/排版引擎,那么打包出来提交比较大,快应用是集成到安卓手机的ROM中,所以只有源码那部分,安装体积比较小,这样就叫快应用。快应用使用原生js开发,框架跟原生微信小程序很像(写着不舒服,Taro支持快应用),官网地址点这里
Electron
Electron就是把Node.js的运行环境和谷歌浏览器内核一起打包了,于是就拥有了Node.js和H5技术的融合能力,又因为是基于C++编写,于是可以跨平台。Mac、windows、Linux。所以Electron是一个用 HTML
,CSS
和 JavaScript
来构建跨平台桌面应用程序的一个开源库,没错,你可以用写网页的知识来写一个桌面应用程序!Electron开发出来的东西是软件,是一个安装在电脑上的软件!是不是发现了新大陆,前端开发不仅仅只能写web页面了,可以做的事情很多了。官网地址点这里