前幾年曾寫過「用 Line、FB 手機 APP 開啟網頁對前端工程師的困擾﹍JS 辨識內建瀏覽器(webView)的方法」,除了解釋 "什麼是內建瀏覽器"、"為何 APP 要用 webView"、"內建瀏覽器會產生什麼問題" 等等,還有提到幾個要點:
?openExternalBrowser=1 ,例如:
- 這件事以目前的網路技術,沒有治本的解決方法
- 手機 APP 只會越來越多,不可能針對所有 APP 個別處理
- 暫時只能針對少數熱門 APP 處理,例如 Line、FB..
一、webView 的進展
1. JS 基本問題 上一篇的留言 #2 整理了「你知道你的網站可能在 InAppBrowser/webview 無法使用嗎?」的 Javascript 基本問題:- alert() :無法出現對話提醒視窗
- confirm() :對話確認視窗無法出現
- window.open 或 window.opener:與電腦上完全不同的顯示行為。
- window.close 或 self.close :失效。
- alert() 與 confirm() 都能正常執行
- window open、close 問題不變
try {
eval('async () => {}');
}
這行語法包含了 async 以及箭頭函數,我們來看看結果如何。
3. Android、iOS 相容性
以下是我的手機內建瀏覽器(非網頁瀏覽器)實測結果:
- Android 5 webView:出現了箭頭函數的錯誤訊息,代表 ES6 的支援就有問題了,更不可能支援 ES7
- iOS 12 webView:出現了 SyntaxError 錯誤訊息,代表支援 ES6 箭頭函數、不支援 async,所以 ES7 一定不行
二、JS 處理 webView 的構思
前一篇文章我認為沒有一勞永逸的解法,所以只提供了解決問題的概念,瞭解如何判斷各種內建瀏覽器的語法。而這段時間以來,網路上陸續出現了各種有助於解決 webView 的拼圖,大致整理一下其他的關鍵技術,最後會提出我的整合構想: 1. Chrome 網址協定 如果手機有裝 Chrome 瀏覽器的話,可以使用 Chrome 提供的網址協定,參考這篇「手機網頁跳出WebView強制使用Chrome開啟」。 Android 使用範例:googlechrome://navigate?url=www.wfublog.com
2021.4.24 補充:留言 #1 提到以上 Android 範例會失效,查到這個討論串「URL Scheme to open Chrome/Firefox (Android OS) from Facebook post」原來有人發現 FB app 在 Android 不能使用 "googlechrome://" 這個協議。但我自己測試沒問題,也許是 Android 版本太低,而比較新的 Android 版本(例如留言者使用的 Android 8)就不支援。那麼可參考該討論串提供的解法,改用以下協議試試:
intent:https://www.wfublog.com#Intent;end
iOS 使用範例:
googlechrome://www.wfublog.com
2. Line 開啟外部瀏覽器
手機使用 Line 開啟連結網址時,官方留了一個後門可以用外部瀏覽器開啟,只要在網址後面加上參數 https://www.wfublog.com?openExternalBrowser=1
3. 偵測是否為行動裝置
根據「讓 Line 按鈕只在手機+行動裝置顯示」→「三、判斷行動裝置」,以下為判斷語法:
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) )
4. 測試 async 支援度
因為 webView 對 JS 支援度不夠,那麼可以偵測瀏覽器是否支援較新的 JS 規範,例如測試瀏覽器是否支援 async 語法,前面有範例語法。
當然,這並非嚴謹的測試方法:
- 因為不支援 async 的瀏覽器不一定只有 webView,也包含了舊瀏覽器與瀏覽器舊版本
- 但這仍不失為可行的檢驗方法,並竟我的網頁就是需要支援 async 才能正常顯示
- 所以任何不支援 async 的瀏覽器,我都需要提醒訪客改用最新的主流瀏覽器版本,例如 Chrome
- 但如果開發者的網頁並沒有使用 async/await 語法,則不一定要測試 async,只要測試夠用的 JS 規範即可,例如前面提過可測試箭頭函數
- 如果將來 webView 支援了 async,而網頁又有想排除 webView 的必要需求時,必須改為偵測更新的 JS 規範
- 偵測瀏覽器 JS 是否支援 async,若支援則不處理
- 瀏覽器不支援 async 時,偵測是否為行動裝置
- 如果不是行動裝置,則 alert 提醒使用最新 chrome 瀏覽器
- 如果是行動裝置時,偵測是否為 Line 內建瀏覽器
- 如果是 Line 內建瀏覽器,直接用外部瀏覽器開啟網頁
- 如果不是 Line 內建瀏覽器時
- 偵測是否為 Android
- 如果是 Chrome 則 alert 提醒更新為最新版本
- 不是 Chrome 則 confirm 請訪客同意用 Chrome 開啟網頁,或是請自行改用外部瀏覽器開啟
- 偵測是否為 iOS
- 如果是 Chrome 則 alert 提醒更新為最新版本
- 不是 Chrome 則 confirm 請訪客同意用 Chrome 開啟網頁,或是請自行改用外部瀏覽器開啟
三、範例程式碼
根據前面提出的構想,以下為範例程式碼,請自行修改相關字串:(function() {
var chrome_warning_text = "建議使用最新版 chrome 瀏覽器才能正常執行本網頁",
chrome_protocol_text = "如您已安裝 Chrome,請同意用 Chrome 開啟網站,才能確保執行正常。或是請取消後自行改用外部瀏覽器開啟本頁面網址",
userAgent = navigator.userAgent,
thisHref = location.href,
thisUrl = location.hostname + location.pathname,
isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent),
isLine = userAgent.indexOf("Line") > -1,
isAndroid = userAgent.indexOf("Android") > -1,
isIOS = /iPhone|iPad|iPod/i.test(userAgent),
isChrome = /Chrome/.test(userAgent) && /Google Inc/.test(navigator.vendor);
try {
// 測試是否支援 async
eval("async () => {}");
} catch {
// 測試是否為行動裝置
if (isMobile) {
// 偵測是否為 Line 內建瀏覽器
if (isLine) {
// Line 內建瀏覽器 直接用外部瀏覽器開啟網頁
location.href = thisHref.indexOf("?") > 0 ? thisHref + "&openExternalBrowser=1" : thisHref + "?openExternalBrowser=1";
} else {
// 偵測是否為 Android
if (isAndroid) {
// 偵測是否為 Chrome
if (isChrome) {
// 建議使用最新版 chrome
alert(chrome_warning_text);
} else {
// 不是 Chrome 則請訪客同意用 Chrome 開啟網頁
if (confirm(chrome_protocol_text)){
location.href = "googlechrome://navigate?url=" + thisUrl;
}
}
return;
}
// 偵測是否為 iOS
if (isIOS) {
// 偵測是否為 Chrome
if (isChrome) {
// 建議使用最新版 chrome
alert(chrome_warning_text);
} else {
// 不是 Chrome 則請訪客同意用 Chrome 開啟網頁
if (confirm(chrome_protocol_text)){
location.href = "googlechrome://" + thisUrl;
}
}
return;
}
// 其他作業系統建議使用最新版 chrome
alert(chrome_warning_text);
}
} else {
// 非行動裝置建議使用最新版 chrome
alert(chrome_warning_text);
}
}
})();
四、Babel
除了以上本篇提供的思路,跨瀏覽器、跨裝置讓 JS 相容還有一套知名的工具「Babel」 ,可以將 ES6 以上的 JS 新規格語法轉換回 ES5 語法,讓較低階的瀏覽器也能執行。 1. 線上轉換工具 這是 Babel 提供的線上轉換工具: 不過我把 async/await 語法丟進去後,並沒發現這個工具能夠轉換為相容的 ES5 語法,也許這個線上工具並非最新 Babel 版本。 2. 安裝 Babel 從這篇「以 async/await 為例,說明 babel 插件怎麼搭」看來,Babel 有辦法可以處理 async,也許要找到對的版本。 不過 Windows 下使用看來要花一些功夫,此參考資料提供給開發者來研究了。 3. 感想 那麼 Babel 是否能做為 webView 相容性的解答?也許有可能,但需要持續關注 Babel 的開發狀況、版本差異性、以及 webView 的進展,感覺上是滿累的,就看前端開發者如何抉擇了。五、如何解決 window.opener
前面提過 window.opener 會是 webView 的無解難題,本篇提供的任何方案都無法完全消除隱憂。不過畢竟 window.opener 使用機率低,所以這裡單獨另開一個章節說明解法,解決思路大致是這樣:- 前面提過 webView 無法另開視窗,只能開在同一視窗
- 那麼可以在新開的頁面檢測 window.opener 相關資訊
- 如果是正常網頁瀏覽器,window.opener 的網址會是原頁面的網址
- 但在 webView 之下 window.opener 的網址就不會是原頁面的網址
- 那麼新開的頁面當檢測到異常後,就可以 alert 提醒訊息
- 也可按本篇範例程式碼的流程引導訪客使用 Chrome 瀏覽器
六、總結
- 本篇的內容嚴格來說是「async 語法跨瀏覽器、跨裝置的解決方案」,而 webView 的問題只能說是順道一併解決。
- 不過內容全是圍繞內建瀏覽器產生的問題,所以也不算偏離主題。
- 本篇的範例程式碼算是有時效性,將來 JS 的發展若出現另一個突破性的階段,那麼 webView 的問題就要再改成「XXX 語法跨瀏覽器、跨裝置的解決方案」了
- 即便如此,到時只要修改範例程式碼
try{...} 這裡的內容就好,那麼本篇程式碼的泛用性還是不錯的
更多 Javascript 技巧相關文章:
用「Chrome 網址協定」這個方法在安卓貌似失效了,我遇到的問題跟這個人一樣
回覆刪除https://stackoverflow.com/questions/57912296/not-allowed-to-load-local-resource-trying-opening-googlechrome-navigateurl-xx
這篇文發在2019年9月,比大大的文還早,換句話說這個方法可能還是有效的,只是在某些情況下導致了Not allowed to load local resource的錯誤,但是我怎麼樣都想不透是哪邊有問題,請教一下大大怎麼看?
p.s.我有問題的手機也是ASUS(Android 8.0.0)
文章裡的方法可以讓IOS裝置在FB裡去跳轉Chrome瀏覽器
回覆刪除但有沒有辦法讓IOS去跳轉到原有的safari瀏覽器呢?