1.爲何要使用熱發佈;html
2.行業現狀;前端
3.熱發佈總體設計方案;vue
4.上線功能和數據狀況;java
5.使用過程當中遇到的問題;android
6.以後須要作的事情;webpack
考拉做爲一個跨境電商類的App,從最開始就註定會受到政策類的條款限制,從而致使常常會出現一些實時變動的需求。而目前這些實時性的需求又必須經過App的直接發版原本解決,不只發佈週期長,應用市場的審覈很浪費時間,並且用戶升級率也不高。ios
當產品策劃提到一些新功能場景時,都必須經過一個完整的迭代流程來進行開發,最終經過發佈新版原本讓用戶使用到新的功能,開發和測試周期長。同時,用戶若是想使用新功能,必須依賴升級。沒法讓舊版本的用戶使用到新功能。nginx
移動開發過程當中常常會出現由於考慮不周致使的一些線上邏輯問題或者崩潰問題,通常狀況下都會經過從新打包發佈應用市場來解決,目前也有更方便的經過補丁來解決問題。經過動態化方案也能夠更好的經過熱發的方式來修復bug。簡單高效。web
目前整個移動市場是分爲android和ios,當需求提出的時候,會同時通知到android和ios兩位開發,進行需求評審和功能迭代,在需求溝通和實現過程當中總會出現各類溝通問題致使需求實現效果不統一,同時兩位開發也會很浪費人力。經過動態化方案,兩端能夠共用同一套代碼,來解決各自的邏輯,可以節省不少人力。數據庫
目前比較流行的兩種熱發佈解決方案是,facebook的React Native和阿里的weex。
相同點:
1.二者的思想是同樣的,都是經過將js轉換成Virtual-dom再映射到Native的對應view組件;
2.遵循 W3C 標準實現了統一的 JSEngine 和 DOM API,可以以web的方式(HTML+CSS+JS)來開發native頁面和應用;
3.都可以完成三端渲染,以及功能模塊的動態下發;
不一樣點:
1.上層DSL語言不一樣,前端流行的兩大框架就是React和Vue,React Native使用的自家的React,而weex使用的是vue;
2.RN支持ios和Android兩個平臺,web端須要本身支持和兼容;Weex號稱支持到三端,可是仍是有不少的兼容任務;
3.RN在打包的時候會將基礎解析部分和業務邏輯一塊兒打包,因此js文件會比較大,而weex只是將業務部分打包,具體的解析過程是在sdk中;因此bundle會比較小;
使用感覺:兩種方案咱們都進行過調研,發現RN在編寫過程當中要求仍是比較高,沒有vue上手快,並且RN更像是一門新語言,他爲不一樣的端提供了不一樣的組件,而weex更但願這些工做由native作,在weex頁面中三端使用都是相同的;並且當時RN提供的list組件性能並非很好,weex提供的是RecyclerView性能相對好不少; 所以咱們移動端動態化採用了weex的方案進行。其實這兩種方案都是採用virtual-dom渲染的方式來實現的,下面簡單介紹一下weex方案的原理.
上面這張圖就是weex的原理圖,首先最開始是經過上層的DSL語言來實現業務功能,實現完成後會經過一些構建方式來進行打包,經過打包能夠將整個業務邏輯打包成一個jsBundle,jsBundle裏面就包含了所實現的功能,以及能夠被下層jsFramework識別的代碼。
有了jsBundle以後就能夠將它放在後臺系統中,這個就是咱們須要熱發佈的功能。
下面一層就是sdk作的事情,sdk在拿到jsBundle以後會經過jscore進行解析,轉換成一個虛擬dom樹,不一樣的端在各自的sdk中解析虛擬的dom樹再渲染成各自端的組件進行頁面的展現。瞭解了weex的功能原理以後,下面介紹下咱們總體的熱發佈應用方案。
總體的動態化方案功能涉及到前端,後端和移動端,三端的功能需求實現,具體的結構圖以下。
主要包括三大部分:
weex層,weex工程負責業務頁面的編寫,打包和發佈;
後臺層,熱發佈的後臺負責配置相對應的功能頁面,提供App檢測更新的能接口和推送下發更新包的能力;
app層,App端負責檢測更新,緩存Bundle,以及渲染展現的策略;
下面能夠看下總體的熱發佈流程:
最開始是編寫weex的代碼實現功能需求,以後經過webpack打包構建jsBundle,將打包構建好的jsBundle發佈到本身的服務器上,目前使用的是ndp平臺和nginx服務器,在ndp平臺配置好cdn規則,這樣每個js文件就會有一個單獨的靜態資源地址。以後會把這個靜態地址和頁面路由信息配置在咱們的熱發佈後臺。用戶在使用App的時候就能夠拿到熱發的頁面渲染。固然再App端也是有兩種方式,一種是用戶主動打開頁面觸發更新接口拉取新數據,另外一種是後臺發佈後推送給App進行後臺更新。 由於咱們整個策略是分爲三部分,因此下面詳細介紹一下各個部分所作的工做。
由於我自己是客戶端開發,對前端的開發流程不甚瞭解,因此在開發過程當中走了不少彎路。
其實前端已經有不少年的歷史,不少開發工具都很完善,目前我在項目中使用的也是webpack來進行打包構建。
由於weex上層的DSL選擇的是vue,因此具體需求頁面的代碼都建議採用vue來編寫(遇到的問題能夠參考vue的官方文檔vuejs.org/index.html,不過weex因爲是在客戶端渲染,因此有些vue中俄功能沒法使用到。
代碼編寫不是重點,重點說下webpack打包時候的配置,我目前是在package.json中配置了兩種打包方案。區分了開發和生產環境。在生產環境下能夠對js文件進行壓縮和混淆。
經過webpack在build文件的入口處能夠設置一些公共的組件,減小vue代碼中的引入:
const App = require("${relativePath}")
//全局註冊組件
Vue.component('wrapperLayout', require("components/wrapper-layout.vue"))
App.el = '#root'
new Vue(App)
複製代碼
咱們這裏配置了一個通用的頁面框架,包括導航條和網絡請求loading效果。
在vue文件build的過程當中要設置"weex-loader":
let weexConfig = getBaseConfig()
weexConfig.output.filename = 'weex/[name].js'
weexConfig.module.loaders[1].loaders.push('weex-loader')
module.exports = [weexConfig]
複製代碼
build完成以後會生成對應頁面的js文件。與java不一樣,這些js文件會將引入的組件和方法所有包含進來,因此一個頁面只有一個js文件,這樣也就致使了這個js文件可能會比較大。目前來看,咱們開發的複雜頁面最大在2-300k上下。
打包完成後會將全部的js文件和資源文件經過構建平臺部署在negix服務器上,而且關聯到cdn,這樣在頁面設置的時候能夠直接使用cdn的連接。
資源圖片目前瞭解到的能夠採用五種方式:local,base64,CDN,iconfont,SVG。目前咱們採用的主要是base64和CDN的方式,而前端活動頁採用的方式是iconfont。
後臺數據庫中存放bundle信息的表結構參數爲:
bundleId,minSupportVersion,bundleVersion,bundleModule,fileDownloadUrl,loadType,desc,person,time
bundleId:當前頁面的惟一標識;
minSupportVersion:當前頁面最低能夠支持到的App版本號;
bundleVersion:當前bundle的發佈版本;
bundleModule:當前bundle所屬模塊;
fileDownLoadUrl:bundle的下載地址;
loadType:當前bundle頁面可使用的加載方式,1:網絡檢查,先使用本地,二次加載最新;2:網絡檢查更新,當此即加載,使用最新;
desc:當前bundle更新狀況的的描述信息;
person:更新者;
time:更新和上傳時間;
數據庫中的表結構爲:
bundleId | minSupportVersion | bundleVersion | bundleModule | fileDownloadUrl | loadType | desc | person | time |
---|---|---|---|---|---|---|---|---|
recommendDetail | 3.9 | 1 | seeding | http://gala/3.9/recommend/recommenddetail.js | 1 | 更新了xxx,changeLog... | 張三 | 2017-10-16 |
** 其中bundleId和minSupportVersion一塊兒做爲惟一標識 **;一旦兩個有一個有變化,就作爲一條新的數據
** 數據庫的操做有兩種狀況: **
** 1.native無改動 **
某次更新的內容,並不依賴於WeexSdk和Native的更改,因此以前的全部版本都支持,則後臺接口直接數據庫中根據bundleId和appVersion查詢到當前這條數據,更新bundleVersion(加一),替換fileDownloadUrl爲新的地址便可。
例如:原來數據庫中存的是:
bundleId | minSupportVersion | bundleVersion | bundleModule | fileDownloadUrl | loadType | desc | person | time |
---|---|---|---|---|---|---|---|---|
recommendDetail | 3.9 | 1 | seeding | http://gala/3.9/recommend/recommenddetail.js | 1 | 更新了xxx,changeLog... | 張三 | 2017-10-16 |
則更新成:
bundleId | minSupportVersion | bundleVersion | bundleModule | fileDownloadUrl | loadType | desc | person | time |
---|---|---|---|---|---|---|---|---|
recommendDetail | 3.9 | 2 | seeding | http://gala/3.9/recommend/recommenddetail.js | 1 | 更新了xxx,changeLog... | 張三 | 2017-10-16 |
只是文件和版本變了,其餘都不變;這樣3.9和4.0版本的App均可以使用這次開發的新功能。
** 2.native有改動 **
以前的某個版本在數據庫中有一條當前頁面的數據。新版本此次更新的內容,最新版本才能夠用,以前全部的舊版本都沒法使用,則在數據庫中插入一條新數據。
例如:原來數據庫中存的是:
bundleId | minSupportVersion | bundleVersion | bundleModule | fileDownloadUrl | loadType | desc | person | time |
---|---|---|---|---|---|---|---|---|
recommendDetail | 3.9 | 1 | seeding | http://gala/3.9/recommend/recommenddetail.js | 1 | 更新了xxx,changeLog... | 張三 | 2017-10-15 |
則更新成:
bundleId | minSupportVersion | bundleVersion | bundleModule | fileDownloadUrl | loadType | desc | person | time |
---|---|---|---|---|---|---|---|---|
recommendDetail | 3.9 | 1 | seeding | http://gala/3.9/recommend/recommenddetail.js | 1 | 更新了xxx,changeLog... | 張三 | 2017-10-15 |
recommendDetail | 4.0 | 1 | seeding | http://gala/4.0/recommend/recommenddetail.js | 1 | 更新了xxx,changeLog... | 張三 | 2017-10-16 |
這次更新以後,數據庫中針對recommendDetail的bundleId會存在兩條信息,第一條給3.9到4.0版本之間的App使用,第二條給4.0版本以後的App使用。
app中檢測更新的接口:
requestParams:bundleId,appVersion;
後臺接口需根據bundleId,appVersion在數據庫中查詢獲得不大於appVersion的minSupportVersion最大的一條數據返回;
response:
{
"bundleId":"recommendDetail",
"minSupportVersion":3.9,
"bundleVersion":1,
"fileDownloadUrl":"http://gala/3.9/recommend/recommendDetail.js",
"loadType":1
}
複製代碼
例如: 1.若是當前App的版本是3.9,
針對上面的第一種狀況,後臺要返回給我最新的bundleVersoin爲2的recommendDetail.js文件
針對第二種狀況,後臺要返回給我3.9版本可用的recommendDetail.js文件,
2.若是當前App的版本是4.0,
針對上面的第一種狀況,後臺要返回給我minSupportVersion爲3.9,
針對第二種狀況,後臺要返回給我4.0版本可用的recommendDetail.js文件。
經過熱發後臺咱們能夠根據對應的bundleId拿到須要顯示的Bundle信息,那麼咱們的native頁面又是如何知道要用那個bundleId去請求,以及顯示的呢?下面看下咱們App端的策略,這個圖是咱們整個App端的架構,其中又分爲兩大部分,一部分是路由策略,一部分是檢測更新策略。
整個weex頁面都是基於路由功能來進行展現和跳轉的。也就是說每一個weex頁面都會有一個惟一的uri。舉一個例子,好比在App中的A頁面,這時候點了一個按鈕跳轉到B頁面,而B頁面是一個weex頁面,那麼A就會將B的路由信息傳遞給Router,Router會解析這個路由信息將他對應的bundleId傳給weexVersionManager,即weex版本管理器,在管理器裏拿到bundle進行顯示,若是B頁面再出現了跳轉邏輯,就會經過weex和Native的橋接器再將路由信息傳遞給Router進行下一次跳轉。
上面這部分就是Bundle加載和檢測更新的部分,具體流程能夠看下下面的流程圖,這裏主要分爲兩部分,一部分是顯示策略,一部分是檢測更新策略。 先看一下顯示的流程,根據以前的流程,路由中將頁面對應的bundleId傳給VersionManager,versionManager在拿到bundleId以後會先在本地檢查是否有可使用的緩存,若是有緩存會先取緩存來加載,若是本地沒有緩存,則會去熱發佈後臺請求最新的bundle,請求回來以後再判斷是否需 要刷新當前頁面來進行顯示。若是網絡失敗了也會給用戶一個網絡錯誤的提示。 右邊這部分就是檢測更新的流程,檢測更新的過程是在後臺執行的,不會影響用戶前臺的交互。在開始檢測的時候會去後臺查詢是否有新的bundle可用,拿到返回的數據後會判斷當前這個bundle是否已經下載過了,若是已經下載過了說明本地就是最新的,則不會再作其餘事情,也不會影響用戶的使用。若是 本地沒有下載過,說明須要更新和覆蓋以前的緩存,那麼就會去下載這個jsBundle,下載成功後將jsBUndle緩存起來。再根據以前協議規則中設置的loadType進行區分。以前提到了loadType是分爲當次加載和二次加載,若是是當次加載會直接刷新頁面,若是是二次加載則等到用戶第二次進入 的時候纔會使用這個最新的。
在考拉AndroidApp中,目前weex頁面在考拉App中的佔比達到了10%。目前上線的功能是種草社區和小考拉問答絕大部分頁面。以下圖:
性能狀況:整個頁面的渲染時長能夠維持在1秒內。其中下載js時長是:100-200ms。正常頁面渲染時長在150-300ms左右
1.關於.9圖片的使用: 在img標籤裏可使用.9圖片,可是要將resize屬性設置爲stretch,以下:
<img class="comment-reply-bg" src="local: //res/bg_comment_reply" resize="stretch"/>
複製代碼
2.關於class樣式切換:
<div class="focus-common" :class="[focusStatus?'focused-container':'focus-container']" >
複製代碼
3.style樣式切換:
:style="{color: focusStatus?'#999999':'#8CAA5E'}"`
複製代碼
4.父組件調用子組件的方法和data:
在子組件中設置ref即id,在父組件中便可經過ref來獲取到對應的子view調用子view的方法:
父vue:
<recommendDetailPage ref="detailPage"></recommendDetailPage>
this.$refs.detailPage.setData(this.$refs.detailPage[0].recommendDetail)
複製代碼
5.子組件給父組件回調:
//子組件經過emit通知父組件,event中傳參數
showGoBack(event) {
this.$emit('showgoback',event);
},
//父組件使用子組件時,html中聲明這個方法,js中調用方法
<bridgeWebview :loadUrl="url" class="webview_style" @showgoback="showGoBack"></bridgeWebview>
showGoBack(event) {
console.log("event.canGoBack2:"+event.canGoBack);
},
複製代碼
6.text顯示問題
text標籤中的內容不能換行,不然在手機上顯示會出現換行;下面代碼中第二個會顯示兩行。例如:
1. <text >hello world</text>
2. <text >hello world
</text>
複製代碼
1.三端共建,提升效率 咱們考拉移動端目前也在三端通用性上進行了進一步的調研和實現。由於ios和android兩端以前的底層支持狀況不同,致使在共建底層接口和共建組件,以及溝通上會花費一些時間。以後的目的主要是能夠完善底層的共建框架,這樣上層的業務才能夠在三端通用,提升開發效率。
2.組件weex的熱發佈 目前個人開發的weex都是頁面級別的,後續會在組件級別上進行一些嘗試。而且抽離這些公共組件,打包成npm包,能夠在多個項目中共用。