原生嵌入ionic混合的开发模式(使用+踩坑篇)

原创 controlling 随笔 ionic 25阅读 14 天前 举报

最近项目中使用到了原生+混合(ionic)的开发模式,从此踏上了踩坑填坑之路。希望此文可以帮助和我一样遇到相同问题的你们。
$ ionic info

一、Android原生项目嵌入ionic项目:

(1)将编写好的ionic项目打包成Android工程

$ ionic cordova platform add android
$ ionic cordova build android --prod --release

(2)拷贝ionic工程文件到原生Android项目中

主要涉及文件有platforms/android目录下的:

  • CordovaLib文件夹
  • src下的com、io、org文件夹
  • res/xml/config.xml文件
  • assets/www文件夹
a. 在Android Studio中,打开ionic项目的platforms/android,点击右侧边栏的Gradle,展开结构:CordovaLib->Tasks->build->点击assembleRelease(或者assembleDebug)


将生成的CordovaLib/build/outputs/aar/CordovaLib-release.aar拷贝到Android项目aars目录下
ps: "AAR(Android Archive)包是一个Android库项目的二进制归档文件。"——百度百科

b. 将ionic项目platforms/android/src/com、io、org三个文件夹拷贝到Android项目src/main/java目录下
c. 将ionic项目platforms/android/assets/www,拷贝到Android项目src/main/assets/www中
d. 将ionic项目platforms/android/res/xml/config.xml拷贝到Android项目src/main/res/xml目录下

如果不变更插件(新增或移除),只需要替换www文件夹中的文件。

二、iOS原生项目嵌入ionic项目:

主要涉及文件有platforms/ios目录下的:

  • CordovaLib文件夹
  • 应用名xxx/Classes文件夹
  • 应用名xxx/Plugins文件夹
  • 应用名xxx/config.xml文件
  • www文件夹

如果不变更插件(新增或移除),只需要替换www文件夹中的文件。

三、原生与ionic之间的交互

项目中,我们可以通过自定义的cordova插件,在原生项目和ionic项目中传递或接收参数。

(1)安装plugman插件

$ npm install -g plugman

(2)创建cordova插件

命令格式:

$ plugman create -name <plugmanName> --plugin_id <pluginID> --plugin_version <pluginVersion> [--path <directory>] [--variable NAME=VALUE]

例子:
插件名:myPlugin
插件ID:cordova-plugin-my-plugin
版本:1.0.0
插件生成路径,默认为当前路径:D:\
描述:这是一个自定义的插件
作者:me

$ plugman create -name myPlugin --plugin_id cordova-plugin-my-plugin --plugin_version 1.0.0 --path D:\ --variable description="这是一个自定义的插件" --variable author="me"

打开生成路径D:,可以看到新生成的myPlugin文件夹,这些自定义信息被写入了myPlugin/plugin.xml中。
cmd中进入myPlugin目录,执行npm init生成package.json文件

D:\myPlugin>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See npm help json for definitive documentation on these fields
and exactly what they do.

Use npm install <pkg> afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (myplugin)
version: (1.0.0)
description: 这是一个自定义的插件
entry point: (index.js)
test command:
git repository:
keywords:
author: me
license: (ISC)
About to write to D:\myPlugin\package.json:

{
"name": "myplugin",
"version": "1.0.0",
"description": "这是一个自定义的插件",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "me",
"license": "ISC"
}

Is this ok? (yes) y

此时myPlugin文件夹中新增了一个package.json文件,里面定义了插件的相关信息。

(3)为插件添加平台

命令格式:

$ plugman platform add -platform_name <ios|amazon-fireos|android|blackberry10|wp8>

例子:

$ plugman platform add -platform_name android

(4)ionic项目中添加/移除自定义插件

进入ionic项目目录,添加插件:

$ cordova plugin add D:\myplugin --save

此时ionic项目中plugins文件夹下新增cordova-plugin-my-plugin插件。
移除插件:

$ cordova plugin rm cordova-plugin-my-plugin

更多内容请参考:https://cordova.apache.org/docs/zh-tw/latest/plugin_ref/plugman.html

(5)插件的使用

假设我们给插件添加android和ios平台(以上第三步),此时你打开插件目录,可以看到如下目录结构:

打开myPlugin.js可以看到一个默认的coolMethod方法,方法名可以修改,但注意其他平台的文件都要一起修改。
在coolMethod中默认传递了一个参数、成功和失败的回调方法。

如果不需要传递参数,可以写成:

在ionic项目.ts文件中,先声明cordova变量,再调用插件方法:

下面来说说项目中踩的坑

坑一:ionic版本问题

因为是新项目,最开始准备使用ionic4 beta13(写此文时已出beta15)来写项目,因为如果使用ionic3,将来升级到ionic4工作量还是比较大的。
然而项目写到一半时,发现低版本安卓中会白屏(我测试了android 8.0正常,7.1.0开始白屏,网上有人做了测试说是7.1.1以下异常),连接Google浏览器手机调试模式,我看到项目本身没有任何报错,但是shadow dom都没有渲染出来,而官方似乎仍在努力解决低版本的问题。
填坑:
在官方解决方案出来前,暂时放弃ionic4 beta版本,改用ionic3。

坑二:嵌入iOS原生项目后ionic的跨域问题

其实对于ionic的接口跨域问题,解决方法有多种,然而最简单的解决方法嵌入了iOS项目后就失效了。
假设我们的后端api接口为https://www.qdfuns.com/api/getSomething,而我们的本地服务为http://localhost:8100,在一些安全策略比较高的浏览器中就会发生跨域问题,原生应用在渲染ionic时用的webview也会有此问题。
假设你的api接口可以拦截跨域攻击等潜在危害,那么跨域问题可以在后端解决,如果不行,那接下来的内容也许可以帮助到你。

方法一:关闭谷歌安全策略(简单粗暴,仅适用于本地调试)
打开chrome属性-快捷方式-目标后面添加--disable-web-security --user-data-dir,例如:
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir
重启chrome浏览器即可。不同版本的chrome参数可能会有变化。

方法二:使用官方提供的代理
在ionic.config.json文件中添加如下配置:

在.ts文件中使用

配置了proxy代理,可以看到在ionic serve中会有这么一行输出:
Proxy added:/api => https://www.qdfuns.com/api
即通过代理将http://localhost/api/getSomething指向了https://www.qdfuns.com/api/getSomething。

详见官方文档:https://ionicframework.com/docs/cli/configuring.html

然而,proxy的方案或许对纯ionic项目是好用的,但是嵌入原生iOS项目后就失效了,原生Android暂未出现跨域问题。于是我们开始尝试第三种方案。

方法三:使用cordova-plugin-advanced-http调用原生方式请求(不适用于browser平台)
添加cordova-plugin-advanced-http插件

$ cordova plugin add cordova-plugin-advanced-http

这个插件会默认绑定一个叫cordova-plugin-file的插件,这里引出了个新坑,稍后介绍。
get请求:

关于判断是否是JSON字符串,可以使用如下方法:

因为我们的Android+ionic项目没有跨域问题,并且本地使用浏览器调试时,没有cordova变量,所以我们做了平台区分,如果是ios项目才使用cordova-plugin-advanced-http,否则使用@angular/common/http的HttpClient请求。
插件官方文档:
https://github.com/silkimen/cordova-plugin-advanced-http
ionic官方文档:
https://ionicframework.com/docs/native/http

坑三:cordova-plugin-file插件导致Android手机物理返回键返回路径有误

前面为了解决ios的跨域问题,我们在项目中添加了cordova-plugin-advanced-http插件,而此插件又间接安装上了cordova-plugin-file插件,在cordova-plugin-file里有一部分关于fileSystemPaths的代码,会导致ionic监听android物理返回键返回的路径错误。
例如ionic项目中有A,B,C三个页面,正常的物理返回是C->B->A->原生页面
但是因为cordova-plugin-file的fileSystemPaths影响,变成了C->原生页面,B->原生页面,A->原生页面,所有ionic的页面物理返回都直接退出了ionic项目。
填坑:
注释掉platforms/android/assets/www/cordova_plugins.js文件的以下代码,然后一切恢复正常:

这里提到了Android手机的物理返回键,那么ionic项目中如何监听这个返回键呢?
在首页.ts文件中

坑四:原生项目启动ionic项目慢

对ionic项目进行优化,使用ionic3提供的懒加载和预加载功能。

填坑1:ionic懒加载
在app.module.ts文件中

在页面的xxx.module.ts文件中

在页面的xxx.ts文件中

在使用懒加载前,所有的页面的ts代码会被转成js代码,写在main.js中,在使用了懒加载之后,页面的ts代码转成的js会被分散到1.js,2.js,3.js……文件中。应用启动时调用的main.js文件大大缩小,而页面加载时也只调用其对应的js文件。

填坑2:ionic预加载
在app.module.ts文件中,设置preloadModules: true

对应的xxx.ts文件中,设置优先级priority: 'high'

预加载的效果目前还没看出来……
官方参考文档:https://ionicframework.com/docs/api/navigation/IonicPage

填坑3:打prod包,压缩css和js代码

$ ionic cordova build android --prod --release

经测试prod包的启动时间比dev包快了将近一倍。

坑五:正确的本地图片路径,嵌入原生后html/ts中引用不能显示

把ionic项目嵌入原生Android或者iOS项目后,原本在html/ts中引用并显示正常的图片,无法加载了。嵌入的ionic项目所有资源文件都仍在www目录下,且相对路径没有变动。
填坑:
将需要使用本地图片的地方都改成背景图方式写在scss中。

关于打包或者运行中的一些错误,会在后续文章中做整理,如果您有任何问题欢迎随时留言。

评论 ( 0 )
最新评论
暂无评论

赶紧努力消灭 0 回复