Hybrid容器設計之第三方網站

範大腳腳發表於2017-11-17
接上文:(閱讀本文前,建議閱讀前三篇文章先)
淺談Hybrid技術的設計與實現
淺談Hybrid技術的設計與實現第二彈
淺談Hybrid技術的設計與實現第三彈——落地篇
之前設計Hybrid整塊互動的時候,受眾都是自己的團隊,沒有想往“公司化”和“平臺化”方向發展,而近期業務的發展逐漸超出預期了,慢慢會有第三方網站接入我們的APP,而且第三方網站還會用一些Native的能力,這個時候之前的使用似乎就不太合適了,所謂JS-SDK就需要存在了。
類似這種需求,做的最完善的當屬微信的native容器了,微信這種屬於海量容器,對所有的接入方基本一視同仁,就算內部團隊會有一些“特權”能力,使用方式與第三方接入都是一套體系,可能只是文件有所不同罷了。微信容器中,最常用的端能力要屬:
① 統一登入,獲取微信的登入態&使用微信登入,微信方給予第三方應用有限的使用者資訊(非JS-SDK)
② 分享介面
③ 微信支付
我們今天以微信(中間可能參考其他APP平臺)為範本,思考下我們自己的容器如何處理第三方的情況。
我們做這塊設計之前,首先需要明確一個定位:
我們的Native容器,應該給第三方網站提供哪些能力?
如果不加限制的提供能力,就屬於“內部”專案了,如微信容器一般,對外提供的能力屈指可數,這裡是我們一個比較常見的第三方容器:
這個當history過深(不為1時)後也會產生一個關閉按鈕,直接回到上一個native頁面。
我們這裡約定,對於第三方網站,這些Native UI我們都不可定製化(但是能通過釋放介面配置增添選單專案),甚至風格色彩都是不可定製的,如果哪天我們的APP風格變了,那麼第三方網站只能適配我們的APP(雖然APP便整體風格這種事情有點操蛋,但是在中小公司還是比較常見)
這裡可定製的是:
① 標題展示
② 分享出去的文字、圖片等(這塊參考微信)
③ 不同的APP這塊可能會有定製化需求,這塊也最好標準化,不讓產品有過多的瞎想
除了登入之外,另一個比較常用的服務就是喚起支付(APP中的支付),然後APP公共頁面再引導使用者選擇微信或者支付寶支付。
其實喚起支付屬於H5跳Native(或者Hybrid)的一類,與之類似的有:
① 開啟某一個native頁面,比較典型的是在網頁貼吧中開啟一個網址如果安裝了貼吧APP,會直接開啟那個頁面
② 這裡也有可能是開啟第三方APP,而開啟第三方APP這種功能,對於比較大的APP平臺會做嚴格的限制
因為圖片上傳什麼對H5來說是一個比較麻煩的功能,完全依賴H5可能體驗不好,於是native方可能會釋放一些圖片操作的介面如:
① H5呼叫native的選取相簿&圖片介面
② 圖片預覽介面(檢視大圖)
③ 上傳圖片
④ 下載圖片
有些比較特別的網站也許還會有獲取當前網路狀態的介面或者地理位置介面或者呼叫掃一掃功能(場景較少),而對於UI層面的操作又會包含關閉當前視窗(同點選關閉按鈕)的介面。
所有的這些功能,我們最初就應該設想清楚,並且清晰的知道每一個介面適用於什麼場景,在能力列表出來後,我們就要做另一個事情了:許可權限制。
許可權限制
微信使用了一個appid,去限制每一個接入方的能力,甚至可以以收費的形式釋放某些介面,可以預見,如果微信開放開啟第三方App的收費功能,會有很多公司爭相埋單。這種appid的行為,相當於一種統一收口的動作,雖然接入方千奇百怪,但是所有的接入方如果要使用容器的能力,甚至想在容器中展示,就必須有一個appid。
另一方面,appid的使用其實成本比較高,前端需要額外的介面訪問不說,平臺方還需要提供一個第三方網站讓第三方網站管理自己的應用,這個對於一些小平臺甚至偽平臺有一些得不償失,我們來簡單看看一個demo:
複製程式碼
 1 //第三方網站要用什麼介面,必須先宣告
 2 wx.config({
 3     debug: false,
 4     appId: `wxf8b4f85f3a794e77`,//伺服器端讀出
 5     timestamp: 1485169627,//伺服器端讀出
 6     nonceStr: `jhGQ4jiN4CQpaGPC`,//伺服器端讀出
 7     signature: `a19573b7f65427a33a96f2a57a4f40075135a5b4`,//伺服器端讀出
 8     jsApiList: [
 9         `onMenuShareTimeline`,
10         `onMenuShareAppMessage`,
11         `onMenuShareQQ`,
12         `onMenuShareWeibo`,
13         `onMenuShareQZone`
14     ]
15 });
複製程式碼
複製程式碼
 1 // 2. 分享介面
 2 // 2.1 監聽“分享給朋友”,按鈕點選、自定義分享內容及分享結果介面
 3 document.querySelector(`#onMenuShareAppMessage`).onclick = function () {
 4     wx.onMenuShareAppMessage({
 5         title: `分享標題`,
 6         desc: `分享描述`,
 7         link: `http://movie.douban.com/subject/25785114/`,
 8         imgUrl: `http://demo.open.weixin.qq.com/jssdk/images/p2166127561.jpg`,
 9         trigger: function (res) {
10             // 不要嘗試在trigger中使用ajax非同步請求修改本次分享的內容,因為客戶端分享操作是一個同步操作,這時候使用ajax的回包會還沒有返回
11             alert(`使用者點選傳送給朋友`);
12         },
13         success: function (res) {
14             alert(`已分享`);
15         },
16         cancel: function (res) {
17             alert(`已取消`);
18         },
19         fail: function (res) {
20             alert(JSON.stringify(res));
21         }
22     });
23     alert(`已註冊獲取“傳送給朋友”狀態事件`);
24 };
複製程式碼
如果沒有第一段程式碼的宣告,第二段程式碼就沒用;如果appid沒有相關介面許可權,這裡就註冊不了,也不能呼叫介面,比如我們就可以為某一個appid賦予開啟第三方app的許可權,或者我們定義某個appid具有app,其他應用可以根據開啟對應app,沒有的話就引導下載:
wx.openApp(appid);
這裡的實現方案可以是這樣,一般來說,我們對外釋放的介面都是比較通用的,像一些私密的介面才會有白名單維護,比如我們自己的app對應的幾個圖片操作介面:
複製程式碼
 1 //選取圖片介面
 2 wx.chooseImage({
 3     count: 1, // 預設9
 4     sizeType: [`original`, `compressed`], // 可以指定是原圖還是壓縮圖,預設二者都有
 5     sourceType: [`album`, `camera`], // 可以指定來源是相簿還是相機,預設二者都有
 6     success: function (res) {
 7         var localIds = res.localIds; // 返回選定照片的本地ID列表,localId可以作為img標籤的src屬性顯示圖片
 8     }
 9 });
10 //上傳圖片介面
11 wx.uploadImage({
12     localId: “, // 需要上傳的圖片的本地ID,由chooseImage介面獲得
13     isShowProgressTips: 1, // 預設為1,顯示進度提示
14     success: function (res) {
15         var serverId = res.serverId; // 返回圖片的伺服器端ID
16     }
17 });
18 //預覽圖片介面
19 wx.previewImage({
20     current: “, // 當前顯示圖片的http連結
21     urls: [] // 需要預覽的圖片http連結列表
22 });
複製程式碼
其中預覽我把他作為私密介面不予釋放,需要特定的appid才能使用,就可以這樣:
複製程式碼
 1 //1代表公共介面,0代表私密介面
 2 var apilist = {
 3     `chooseImage`: 1,
 4     `uploadImage`: 1,
 5     `previewImage`: 0
 6 };
 7 //所有的應用id
 8 var appids = [1, 2, 3, 4];
 9 //白名單
10 var whitList = [{1: [`previewImage`, `其他私密能力`]}];
複製程式碼
實話實說維護一個appid,這樣做的成本比較高,單單做appid和祕鑰對於呼叫者來說也挺麻煩,對於有些比較小的平臺來說,可以採取域名白名單的方法,後端維護一個列表:
1 //域名白名單
2 var whitList = [
3     {`domain.com`: [`previewImage`, `其他私密能力`]},
4     {`domain2.com`: [`previewImage`, `uploadImage`, `其他私密能力`]}
5 ];
這種方法比較做起來成本較低,一些小一點的平臺可以這樣做,而這樣做的話,需要考慮每個域名對應的App開啟協議,可能會有開啟需求,我們這裡採用的比較簡單的方案,域名白名單,表設計大概這樣:
1 var whitList = [
2     {id: `domain.com`, apis: [`previewImage`, `其他私密能力`], schema: `xxxx://`},
3     {id: `domain2.com`, apis: [`previewImage`, `uploadImage`, `其他私密能力`], schema: `xxxx://`}
4 ];
我們明確知道某個域名具有哪些能力,如果不具有這些能力就不予理睬,我們也知道某個域名具有開啟某個app的許可權。
能力列表
明確了能力,以及能力限制,接下來我們便來整理一下幾個核心的對外介面。
header的定義
我們這裡做的第一件事情,依舊是header的定義,並且對於第三方,我們要求header只能是這個樣子:
針對header我們有以下約定:
① 進入一個頁面預設包含,返回+title+功能選單三個按鈕
② 返回按鈕預設執行history.back()的操作,如果history.length為1,則退到native上一步操作
③ title預設讀取html中的title標籤,可使用介面更改
④ 關閉按鈕預設不存在,在history比較深並且點選過一次返回按鈕後展示出來,防止頁面死迴圈假死
⑤ 功能選單預設彈出以下選單項,其中分享文案預設讀取當前tdk(title+description)標籤,和第一張圖片,也可讀取頁面標籤定製(也可以使用介面定製,事實上容器不會做這種業務工作,是業務框架層bridge做的工作),比如:
1 <meta name=”med-title” content=”分享標題”>
2 <meta name=”med-description” content=”分享內容”>
3 <meta name=”med-link” content=”http://….”>
4 <meta name=”med-img” content=”http://….”>
我們業務層程式碼,或者bridge程式碼會將之翻譯為:
複製程式碼
 1 wx.onMenuShareTimeline({
 2     title: medTitle,
 3     link: medLink,
 4     imgUrl: medImg,
 5     trigger: function (res) {
 6     },
 7     success: function (res) {
 8     },
 9     cancel: function (res) {
10     },
11     fail: function (res) {
12     }
13 });
複製程式碼
分享到朋友圈&QQ空間
分享到朋友圈前端程式碼為:
複製程式碼
 1 MED.origin = MED.origin || {};
 2 //shareTimeline分享到朋友圈;shareAppMessage分享給朋友;shareQQ分享給qq好友;shareQZone分享到空間,設定方面稍作更改即可
 3 MED.origin.medShareXXX = MED.medShareXXX = function (o) {
 4     _.requestHybrid({
 5         tagname: `shareTimeline`,
 6         param: {
 7             title: o.title,
 8             desc: o.desc,
 9             image: o.img,
10             url: o.link
11         },
12         callback: function(data) {
13             if(data.code === 0) {
14                 o.success &&  o.success(data.data);
15             } else {
16                 o.cancel &&  o.cancel(data.data);
17             }
18         }
19     });
20 };
複製程式碼
H5上傳圖片方面的體驗很差,這塊我們在H5情況下依舊使用file上傳,但是在容器裡面釋放幾個圖片操作介面
圖片操作
複製程式碼
 1 //選取圖片,最初想把選取和上傳合併的,後面想想還是分開合適
 2 _.requestHybrid({
 3     tagname: `chooseImage`,
 4     param: {
 5         //1-9張限制
 6         count: 1,
 7         sizeType: [`original`, `compressed`], // 可以指定是原圖還是壓縮圖,預設二者都有
 8         sourceType: [`album`, `camera`], // 可以指定來源是相簿還是相機,預設二者都有
 9     },
10     callback: function(data) {
11         if(data.code === 0) {
12             //這塊有一些疑問,選擇和上傳還是連著一起算了
13             o.success &&  o.success(data.data.localIds); // 返回選定照片的本地ID列表,localId可以作為img標籤的src屬性顯示圖片;
14         } else {
15             o.error &&  o.error(data.data);
16         }
17     }
18 });
19 
20 //上傳圖片
21 _.requestHybrid({
22     tagname: `uploadImage`,
23     param: {
24         //由chooseImage獲取
25         localId: 1,
26         isShowProgressTips: 1 // 預設為1,顯示進度提示
27     },
28     callback: function(data) {
29         if(data.code === 0) {
30             o.success &&  o.success(data.data.url); // 返回src;
31         } else {
32             o.error &&  o.error(data.data);
33         }
34     }
35 });
36 
37 //圖片預覽,預覽的地方做個圖片下載的功能
38 _.requestHybrid({
39     tagname: `previewImage`,
40     param: {
41         current: “, // 當前顯示圖片的http連結
42         urls: [] // 需要預覽的圖片http連結列表
43     }
44 });
複製程式碼
獲取網路狀態
複製程式碼
1 //獲取網路狀態
2 _.requestHybrid({
3     tagname: `getNetworkType`,
4     callback: function(data) {
5         //data.networkType 2g 3g 4g wifi
6     }
7 });
複製程式碼
地圖操作
Native的地理操作一塊相對H5體驗要好一些,特別是地圖展示一塊的體驗要好得多,所以這兩塊也需要釋放API:
複製程式碼
 1 //獲取經緯度資訊
 2 _.requestHybrid({
 3     tagname: `getLocation`,
 4     callback: function(data) {
 5         if(data.code !== 0) return;
 6         var res = data.res;
 7         var latitude = res.latitude; // 緯度,浮點數,範圍為90 ~ -90
 8         var longitude = res.longitude; // 經度,浮點數,範圍為180 ~ -180。
 9         var speed = res.speed; // 速度,以米/每秒計
10         var accuracy = res.accuracy; // 位置精度
11     }
12 });
13 
14 //根據經緯度等資訊開啟native地圖
15 _.requestHybrid({
16     tagname: `openLocation`,
17     params: {
18         latitude: 0, // 緯度,浮點數,範圍為90 ~ -90
19         longitude: 0, // 經度,浮點數,範圍為180 ~ -180。
20         name: “, // 位置名
21         address: “, // 地址詳情說明
22         scale: 1, // 地圖縮放級別,整形值,範圍從1~28。預設為最大
23         infoUrl: “ // 在檢視位置介面底部顯示的超連結,可點選跳轉
24     }
25 });
複製程式碼
所謂的name和地址是指下面資訊框這一坨:
介面操作
關閉當前webview,回到native上一次操作:
1 _.requestHybrid({
2     tagname: `closeWindow`
3 });
native鍵盤
H5在文字輸入一塊可以說是弱爆了,比Native體驗差遠了,所以我們在native鍵盤這塊也做了一個nativeUI,如果需求允許可以使用:
複製程式碼
 1 //喚起輸入文字的軟鍵盤
 2 //這塊程式碼有一些業務耦合,需要如何處理下????
 3 _.requestHybrid({
 4     tagname: `showKeyboard`,
 5     param: {
 6         hasImg: 1, //是否需要上傳圖片區域,如果需要則為1,不需要為0
 7         count: 1, //如果需要圖片上傳,這裡限制圖片選擇的數量,1-9
 8         sizeType: [`original`, `compressed`], // 可以指定是原圖還是壓縮圖,預設二者都有
 9         sourceType: [`album`, `camera`], // 可以指定來源是相簿還是相機,預設二者都有
10         textMin: 20, //文字要求最少輸入字元數
11         textMax: 500 //文字要求最多輸入字元數
12     },
13     //輸入結束的回撥或者說點選傳送時候的回撥
14     callback: function (data) {
15         var content = data.content;//文字內容
16         var urls = data.urls;//圖片地址
17     }
18 });
複製程式碼
結語
今天分析了一下第三方webview需要釋放的介面,接下來我這邊開始落地,我們真實工作中可能還要考慮新老容器過渡等問題,本文含金量相對較小,各位謹慎閱讀吧。
本文轉自葉小釵部落格園部落格,原文連結http://www.cnblogs.com/yexiaochai/p/6341025.html,如需轉載請自行聯絡原作者


相關文章