前幾年曾寫過「用 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
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 技巧相關文章: