概覽
-
Cordova
Cordova 能夠將你的 HTML/JS 程式碼打包在一個原生的容器中執行,並且可以呼叫系統的各類軟硬體介面(JavaScript API)。我們將這種架構稱之為
hybrid app
, 得益於這種架構,我們能將前端程式碼跨平臺執行,並且得到接近原生應用的系統特性。最終釋出到各大應用市場,包括蘋果的 App Store。 -
Cordova 外掛
Cordova 通過外掛的生態系統為開發者提供了廣泛的軟硬體介面支援,諸如檔案讀寫、推送通知、解壓縮、通訊錄等等。這種方式保證了在開發過程中按需引入相應的功能外掛,同時也加快了外掛的更新維護,減少程式碼冗餘。Cordova 外掛使用 CLI 的方式經過 NPM 管理依賴並安裝,十分方便。
-
Cross-walk Project
Cross-walk 是英特爾開發的一款為HTML應用提供執行時環境的開源專案,它基於最新的 Chromium 核心開發,可以提供最新的 Web 特性和一致的相容性。隨著 Cordova Android 4.0.0+ 引入了對嵌入式 webview 的支援,現在你可以方便地在你的 Cordova 應用上使用 Crosswalk 的 webview。通過使用 Crosswalk 的 webview 外掛,開發者可以享用遠端除錯的功能,前沿的 HTML5 特性,例如WebGL, WebAudio 和 WebRTC,以及在包括 Android 4.0 Ice Cream Sandwich(ICS) 在內的 Android 裝置上效能的顯著提升。
-
Vue.js
Vue.js 是一套構建使用者介面的 漸進式框架。與其他重量級框架不同的是,Vue 採用自底向上增量開發的設計。Vue 的核心庫只關注檢視層,並且非常容易學習,非常容易與其它庫或已有專案整合。另一方面,Vue 完全有能力驅動採用單檔案元件和Vue 生態系統支援的庫開發的複雜單頁應用。基於 Vue 框架,開發單頁面的 hybrid app 將會變的非常高效。
-
CodePush
CodePush 是微軟提供的一項雲服務,它為開發者提供了直接向使用者推送熱更新 (Hot-Code-Update) 的軟體支援和 CDN 分發網路。CodePush 為 Cordova 開發者提供了外掛,我們可以快速地植入應用中。它提供的特性包括:版本管理、灰度更新、緊急回滾、增量更新 (Diff) 等等。它的 CDN 是世界上最大的 CDN 服務商 Akamai 提供的,穩定性和速度可想而知。
未來可能收費
初始化開發環境
- Nodejs 版本更新 使用
node -v && npm -v
檢查版本,如果低於 v6.0.0 和 v3.0.0,請使用n
工具更新:sudo npm install n -g && n stable
- NPM 包的源管理 實際使用中 npm 的源伺服器位於美國,速度較慢,所以我們用阿里巴巴的 npm 源映象替代預設源:
sudo npm install nrm -g && nrm use taobao
,此操作將會將源地址設定為:registry.npm.taobao.org/
,伺服器位於阿里雲,速度飛快。 - 安裝 Xcode (OS X 選裝) 前往 Mac App Store 下載最新版 Xcode,並安裝 Command Line Tools,便於在 iOS 模擬器中除錯。
- 安裝 Android Studio(必裝) 前往 Android Studio 官網 下載 Android Studio 並安裝 SDK 24+,根據引導提示即可安裝完成。缺少這一步將不能成功 build 和 run 專案。
- 新建 Cordova 專案 簡單地按照官方文件新建專案:文件連結
-
一點小麻煩 當你執行到
cordova platform add android
這一步時,可能會遇到卡在> Configuring > 0/2 projects
長達幾個小時之久。在此需要對cordova platform add android
這個命令的原理做一個解釋,它做了以下幾件事:- 建立 Android 專案框架和檔案結構
- 首次執行下載 gradle (專門用來管理 Android 專案 maven 包的工具)
- 配置 build.gradle 構建指令碼
- 下載專案需要的 maven 包
- 清理冗餘的 Cordova Lib
- 構建專案
可惜的是,gradle 似乎在下載 https 協議的時候有一些 bug,導致 maven 包不能成功下載。這時候需要一些奇技淫巧:
- 找到你的 gradle 目錄,一般在
~/.gradle/wrapper/dists/gradle-2.14.1-all/53l0mv9mggp9q5m2ip574m21oh/gradle-2.14.1/
下。 - 修改
src/core/org/gradle/api/artifacts/ArtifactRepositoryContainer.java
檔案,把MAVEN_CENTRAL_URL
變數修改為repo1.maven.org/maven2
- 修改
src/core/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandler.java
檔案,把BINTRAY_JCENTER_URL
變數修改為jcenter.bintray.com/
- 重新構建 Android 專案,
cordova platform remove android && cordova platform add android
,大功告成。
-
執行示例專案 下面我們嘗試在模擬器和安卓真機中除錯:
- iOS:
cordova run ios
等待編譯完成後會自動開啟 iOS 模擬器 - Android:
cordova run android
或cordova build android
後在yourApp/platforms/android/build/outputs/apk
目錄中找到打包好的 apk 進行真機安裝並除錯。 - iOS 的真機除錯需要開發者帳號並註冊開發裝置,在此不詳述。
- iOS:
使用 Cordova 外掛引入 Cross-walk webview
在上一步中我們成功地讓示例 html 檔案跑在了 Cordova 環境中。但在實際開發中,會遇到 Android 系統版本落後,原生 webview 效能差相容性差的問題,所以我們要用 Cross-walk 替換原生的 Android webview 來執行我們的專案。 Cross-walk 開發社群已經為我們準備好了 Cordova 外掛,只需要執行: cordova plugin add cordova-plugin-crosswalk-webview@latest —save
在這裡需要說明的是,必須加上 @latest
字尾,安裝 2.2.0+ 版本,否則會遇到 #XWALK-7422 的問題。 成功安裝後再次 cordova build android
,你會發現輸出了兩個 apk 檔案,一個對應 arm 平臺,一個對應 x86 平臺,體積相較原來的檔案增加了 20Mb 左右,多出來的體積便是 crosswalk-webview 的 runtime。 你也可以在專案的 config.xml
中設定:
<plugin name="cordova-plugin-crosswalk-webview" spec="~2.2.0">
<variable name="XWALK_VERSION" value="22+" />
<variable name="XWALK_LITEVERSION" value="xwalk_core_library_canary:17+" />
<variable name="XWALK_COMMANDLINE" value="--disable-pull-to-refresh-effect" />
<variable name="XWALK_MODE" value="embedded" />
<variable name="XWALK_MULTIPLEAPK" value="true" />
</plugin>
複製程式碼
把 XWALK_MULTIPLEAPK
的 value 設定為 false,即可打包出一個整合兩個平臺的 apk 包,當然,體積會暴增至 40Mb+。
使用 Chrome 除錯 Cross-walk webview 中的專案
由於 Cross-walk 基於 Chromium,天生便具有了 Chrome 遠端除錯的功能。 電腦連線 Android 手機並開啟 USB 除錯模式,開啟你的 Cordova 專案後,在 Chrome 瀏覽器位址列中輸入 chrome://inspect
,選擇你的裝置,即可開啟遠端控制檯,真機除錯變得十分方便。Vue Devtool 同樣可以在此使用。
iOS 的小優化 將 UIWebView 替換為 WKWebView
WKWebView 是蘋果在 iOS 8 中引入的新元件,目的是給出一個新的高效能的 Web View 解決方案,擺脫過去 UIWebView 的老舊笨重特別是記憶體佔用量巨大的問題(開啟一個示例專案,WKWebView 佔用23M,而 UIWebView 佔用85M)。 在這裡我們將使用 WKWebView 替換 Cordova 預設的 UIWebView: cordova plugin add cordova-plugin-wkwebview-engine@latest --save
並在 config.xml 的 <platform name="ios">
中新增:
<feature name="CDVWKWebViewEngine">
<param name="ios-package" value="CDVWKWebViewEngine" />
</feature>
複製程式碼
使用 Cordova 外掛的 JavaScript API 呼叫系統原生介面
可在外掛搜尋頁搜尋需要的外掛,以Camera外掛連結外掛為例,在命令列進入專案所在資料夾,輸入cordova plugin add cordova-plugin-camera --save
然後在頁面的js部分先要新增:
document.addEventListener("deviceready", func, false);
複製程式碼
所有的外掛都需要在func函式之後執行。 Camera的方法都定義在navigator物件上,Navigator物件包含有關瀏覽器的資訊,所有瀏覽器都支援,在檔案任何js部分呼叫navigator.xxx方法即可。 但並不是所有外掛的方法都定義到navigator物件上,根據具體的外掛文件而定。例如極光推送的外掛外掛連結的方法都定義的window物件上。 之後的外掛各種方法的使用可以參照對應外掛的文件使用。
Cordova-plugin-whitelist 和 CSP 安全策略
出於安全考慮,Cordova 4.0 以上環境中,需要安裝cordova-plugin-whitelist
外掛並 對 config.xml
中的 <access origin="your-policy" />
標籤和 index.html
中的 META
標籤做一定設定,防止出現共享 Webview 模式下的跨站攻擊等安全問題。 執行:
cordova plugin add cordova-plugin-whitelist --save
複製程式碼
並編輯 config.xml
:
<!--以下是開發環境配置,不對來源做任何限制,生產打包時需要設定-->
<access origin="*" />
<allow-intent href="*" />
<allow-navigation href="*" />
複製程式碼
在 index.html
中新增:
<!--同樣,生產打包時需要嚴格設定-->
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: cdvfile: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; font-src * data:; img-src *; script-src *;">
複製程式碼
CSP(Content-Security-Policy)的詳細設定:
- 指令:
- 指令值:
Vue.js 的分片打包模式
在大型應用中,我們可能需要將應用拆分為多個小模組,按需從伺服器下載。Vue.js 允許將元件定義為一個工廠函式,動態地解析元件的定義。工廠函式接收一個 resolve 回撥,在收到從伺服器下載的元件定義時呼叫。 在定義router的時候,可以給component屬性一個工廠函式,require.ensure
是commonjs非同步載入方法,中括號中可以為空,這裡寫上相當於先載入,但沒執行,下面的require才是執行。 require.ensure
語法告訴 webpack,自動將編譯後的程式碼分割成不同的塊。 在命令列打包npm run build
之後,在dist/static/js資料夾下會多出一個js檔案和map檔案 。
routes:[{
path:'/example',component:resolve => require.ensure(['../components/example.vue'], () => {
resolve(require('../views/example.vue'))
})
}】
複製程式碼
如果你在require.ensure
的函式中引用了兩個以上的模組,比如某個路由下的所有元件都打包在同個非同步中,需要提供require.ensure
第三個引數作為的名稱,這時webpack會把它們打包在一起,比如:
require.ensure([], function(require){
var list = require('./list');
list.show();
var edit = require('./edit');
edit.display();
}, 'all');
複製程式碼
list.js和edit.js將會被打包成一個檔案,並命名為all.js。如果不希望打包在一起,只能寫兩個require.ensure分別引用這兩個檔案。 注意 如果希望生成的檔案是我們給的Chunk,需要改一下webpack的配置,將build/webpack.prod.conf.js的第19行修改如下:
chunkFilename: utils.assetsPath('js/[name].js')
複製程式碼
新增 CodePush 熱更新功能
為了實現本次 App 架構中的單個元件釋出和熱更新功能,引進了微軟開發的 CodePush 工具。
- 執行:
cordova plugin add cordova-plugin-code-push@latest --save
複製程式碼
- 安裝 code-push 命令列並註冊帳號(僅開發試用,正式部署會有專門的帳號):
sudo npm install -g code-push-cli
code-push register //你可以通過 Github 或者 微軟帳號登入
code-push login //登入後會在本地生成 session 檔案用來驗證你的釋出許可權
複製程式碼
- 建立 app:
code-push app add <appName>
複製程式碼
這一步會生成 key,將 staging 中的 key 複製到下一步使用。
- 在 app 的
config.xml
中配置你的部署 key:
<platform name="android">
<preference name="CodePushDeploymentKey" value="YOUR-ANDROID-DEPLOYMENT-KEY" />
</platform>
<platform name="ios">
<preference name="CodePushDeploymentKey" value="YOUR-IOS-DEPLOYMENT-KEY" />
</platform>
複製程式碼
- 在你的
index.html
中允許安全策略,新增codepush.azurewebsites.net
的域名:
<meta http-equiv="Content-Security-Policy" content="default-src https://codepush.azurewebsites.net ... />
複製程式碼
- 新增程式碼更新邏輯:
var app = {
// Application Constructor
initialize: function() {
document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
},
onDeviceReady: function() {
const downloadProgress = (progress) => { console.log(`Downloaded ${progress.receivedBytes} of ${progress.totalBytes}`); }
codePush.sync( null, {updateDialog: true, installMode:InstallMode.IMMEDIATE, mandatoryInstallMode: InstallMode.IMMEDIATE}, downloadProgress );
}
};
app.initialize();
複製程式碼
這時重新打包 app,並執行。
- 驗證釋出程式碼功能
修改你的任意程式碼後,執行:
code-push release-cordova <appName> <platform>
code-push release-cordova MyApp-ios ios
code-push release-cordova MyApp-Android android
複製程式碼
- 關閉 app 重新開啟並測試更新是否成功
自定義外掛
在實際專案中可能需要自己開發一些特殊的外掛。這需要涉及到一些原生開發的知識。這裡以開發iOS的外掛為例。
- 首先,在任意目錄下建立一個外掛目錄,如在desktop新建一個資料夾rayeye-TestPlugin,然後在com.anasit.callnative目錄下新建src、www兩個目錄,src中存放的是對應平臺的外掛程式碼,www存放的是外掛的js程式碼,再新建一個plugin.xml檔案,寫入的是外掛的相關配置資訊,本例只開發了iOS平臺,所以在src下再新建ios目錄。
-
在www資料夾下建立testPlugin.js:
var argscheck = require('cordova/argscheck'), exec = require('cordova/exec'); var testPlugin = function(successCallback, errorCallback, options) { argscheck.checkArgs('fFO', 'navigator.testPlugin', arguments); options = options || {}; var getValue = argscheck.getValue; var a = getValue(options.a, 'aaa'); var b = getValue(options.b, 'bbb'); var args = [a, b]; exec(successCallback, errorCallback,"testPlugin","test",args); }; module.exports = testPlugin; 複製程式碼
-
iOS native部分,在src/ios資料夾下新建 IosPlugin.h 和 IosPlugin.m檔案:
// IosPlugin.h #import <Cordova/CDV.h> @interface IosPlugin : CDVPlugin - (void)test:(CDVInvokedUrlCommand*)command; @end 複製程式碼
-
// IosPlugin.m #import "IosPlugin.h" #import <Cordova/CDV.h> @implementation IosPlugin - (void)test:(CDVInvokedUrlCommand *)command{ NSString *s1 = [NSString stringWithFormat:@"%@", [command.arguments objectAtIndex:0]]; NSString *s2 = [NSString stringWithFormat:@"%@", [command.arguments objectAtIndex:1]]; UIAlertView *alertview = [[UIAlertView alloc] initWithTitle:s1 message:s2 delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"確定", nil]; [alertview show]; } @end 複製程式碼
-
編輯plugin.xml:
<?xml version="1.0" encoding="UTF-8"?> <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" id="rayeye-TestPlugin" version="0.0.1"> <name>rayeye-TestPlugin</name> <description>Call Native Plugin</description> <license>Apache 2.0</license> <keywords>call native</keywords> <js-module name="testPlugin" src="www/testPlugin.js"> <clobbers target="window.testPlugin"/> </js-module> <platform name="ios"> <config-file target="config.xml" parent="/*"> <feature name="testPlugin"> <param name="ios-package" value="IosPlugin" /> </feature> </config-file> <header-file src="src/ios/IosPlugin.h" /> <source-file src="src/ios/IosPlugin.m" /> </platform> </plugin> 複製程式碼
-
在專案根目錄下開啟命令列,輸入cordova plugin add rayeye-TestPlugin(注:根據自己設定的路徑寫),按下回車後,外掛就會自動安裝到專案根目錄下的plugins目錄中。
-
最後在頁面檔案中呼叫
window.testPlugin(successcallback, errorcallback, {a: "111",b: "222"});
即可彈出一個原生的彈框。
Android 生產打包
- 從Android Studio的"File"->"Open"開啟你的cordova專案下的/platforms/android。
- 選擇"Build"->"Generate Signed APK",點選"next"。
- 此處選擇一個已有的簽名,或者新建一個。點選"next"。
- 此處選擇release生產版本,點選finish完成。
iOS 生產打包
- 前提是在蘋果開發者網
developer.apple.com/account/
站生成了需要的appid,證照和profile檔案,不累述。 - 在Display Name 修改應用名稱。
- 用Xcode開啟工程,注意是.xcworkspace(白顏色)的工程,不是.xcodeproj(藍顏色)的工程。
- 在Resources/Images.xcassets裡修改啟動圖片和應用圖示,AppIcon是對應平臺的圖示,LaunchImage是啟動圖片。
- 選擇“Product”->"Edit Scheme...",在“Build Configuration”中選擇“Release”,單擊"Close"。
- 選擇選單欄中的"Product"->"Archive",選擇剛剛的工程,選擇“Export...”,選擇第一個(Save for iOS App Store Deployment),點選Export。然後會生成一個資料夾,裡面是ipa包。
- 開啟 Application Loader,選擇“交付您的應用程式”,選取之前生成的ipa包,然後下一步。
- 去
itunesconnect.apple.com/WebObjects/…
完成app相關資訊的填寫
極光推送的使用
- 外掛地址
github.com/jpush/jpush…
- 安裝:
cordova plugin add jpush-phonegap-plugin --variable API_KEY=your_jpush_appkey
document.addEventListener("deviceready", onDeviceReady, false); document.addEventListener("jpush.openNotification", onOpenNotification, false);//點選通知啟動或喚醒應用程式時會出發該事件 document.addEventListener("jpush.receiveNotification", onReceiveNotification, false);//應用程式處於前臺時收到推送會觸發該事件 document.addEventListener("jpush.backgroundNotification", onBackgroundNotification, false);//後臺收到推送,不知道何用,處在後臺還能幹嘛嗎 document.addEventListener("jpush.receiveMessage", onReceiveMessage, false);//收到自定義訊息