【前端 CDN】CDN緩存造成的bug,JS識別移動端設備和PC端設備,域名重定向

目錄

一、項目問題和原因

二、解決方案

2.1 前端入口文件識別

2.2 pc 端和 mobile 端域名拆分

三、遺留問題處理

3.1 pc 端處理


一、項目問題和原因


背景:

  • 項目使用原生端開一個 webview殼,訪問服務器前端地址
  • 項目分爲 pc 端和 mobile 端,分爲兩個工程
  • 用戶在 pc 端和 mobile 端都是使用一模一樣的域名訪問,由 nginx 根據用戶的 UserAgent 轉發到對應的地址
  • 服務器加了CDN

 

現象:

正式環境中,用戶在 mobile 端訪問時,卻訪問到了 pc 端的頁面,由於文件訪問是相對路徑訪問的,訪問了錯誤的頁面,拿不到對應的資源,故報錯。同理,用戶在 pc 端訪問時,卻訪問到了 mobile 端頁面。

 

原因:

1. 購買的CDN服務器不支持根據UserAgent轉發到對應文件(有的CDN支持)

2. 由於 pc 端和 mobile 端使用了同一個域名訪問,在CDN對這個文件訪問的存儲,可能是 pc 的,也可能是 mobile 的。導致 pc 端用戶用地址去訪問文件,拿到了 mobile 端的頁面,或是 mobile 端用戶用地址去訪問文件,CDN返回的是 pc 端的文件。

3. 若是一整套文件都相同,用戶最多訪問到不同的文件,但CDN存儲的使用文件中,會出現 mobile 端文件和pc文件混雜的情況,故會拿不到資源文件,導致報錯

 

二、解決方案


思考了多種解決方案,最終決定使用pc端訪問地址和 mobile 端訪問地址分開的做法(2.2)。

2.1 方案一、前端入口文件識別

在項目的入口文件上(一般是index.html),加入一段js代碼,通過獲取userAgent判斷是否訪問到了正確的設備,若不正確,則重定向。

若不是在入口文件識別,而是使用外鏈的方式加載js,由於已經拿到了錯的文件了,通過相對路徑去拿地址肯定也是拿不到的。

故直接內嵌在入口文件的頭部。

如 mobile 端的入口文件:(pc端同理)

<!-- index.html -->

<!DOCTYPE html>
<html lang="zh" dir="ltr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" id="setscale" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
  ...
  <script type="text/javascript">
    let type;
    // 判斷用戶是哪種設備
    if ((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
      type =  "mobile"
    } else {
      type =  "pc"
    }
    // 若不是mobile設備,重定向到pc
    if(type != 'mobile')  window.location.replace(window.location.origin + "/ index");
  </script>

  <link href="build/main.css?v=5.5" rel="stylesheet">

</head>
<body>
...
</body>
</html>

 

2.2 方案二、pc 端和 mobile 端域名拆分

將 pc 端和 mobile 端的域名進行拆分,將頂級域名 testUrl.com 和主域名 www.testUrl.com 指向 pc 項目,將子域名 m.testUrl.com 指向 mobile 項目。用戶通過不同的域名訪問項目。

由於CDN緩存是根據訪問路徑緩存的,當區分開 pc 端項目路徑和 mobile 端項目路徑後,用戶訪問的文件就不會在混亂了。

 

三、遺留問題處理


使用 2.2 中的辦法解決了用戶訪問的問題,但是如果用戶不重新下載APP,或是直接在 web 上訪問舊線路,就依舊會報錯。如何引導用戶正常使用線路?

3.1 pc 端處理

3.1.1 判斷當前是 pc 端還是 mobile 端

var myBrowser = {
    versions: function () {
    var u = navigator.userAgent;
    return {//移動終端瀏覽器版本信息
        trident: u.indexOf('Trident') > -1, //IE內核
        presto: u.indexOf('Presto') > -1, //opera內核
        webKit: u.indexOf('AppleWebKit') > -1, //蘋果、谷歌內核
        gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐內核
        mobile: !!u.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i), //是否爲移動終端
        ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios終端
        android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android終端或者uc瀏覽器
        iPhone: u.indexOf('iPhone') > -1 || u.indexOf('Mac') > -1, //是否爲iPhone或者QQHD瀏覽器
        iPad: u.indexOf('iPad') > -1, //是否iPad
        webApp: u.indexOf('Safari') == -1, //是否web應該程序,沒有頭部與底部
        wx: u.indexOf('MicroMessenger') > -1,//是否是微信
        hasVersion: u.indexOf('Version') > -1,
        isie: navigator.userAgent.toLowerCase().indexOf(".net") > 0 || navigator.userAgent.toLowerCase().indexOf("msie") > 0,
        isie6: navigator.userAgent.toLowerCase().indexOf("msie 6.0") > 0,
        isie7: navigator.userAgent.toLowerCase().indexOf("msie 7.0") > 0,
        isie8: navigator.userAgent.toLowerCase().indexOf("msie 8.0") > 0,
        isie9: navigator.userAgent.toLowerCase().indexOf("msie 9.0") > 0,
        ischrome: navigator.userAgent.toLowerCase().indexOf("chrome") > 0 && navigator.userAgent.toLowerCase().indexOf("safari") > 0,
        issafari: navigator.userAgent.toLowerCase().indexOf("chrome") < 0 && navigator.userAgent.toLowerCase().indexOf("safari") > 0,
        isfirefox: navigator.userAgent.toLowerCase().indexOf("firefox") > 0
    };
} ()

 

3.2.2 跳轉錯誤則彈出提示,修改域名,並進行重定向

if(myBrowser.versions.mobile) {
    var jumpUrl = window.location.origin;
    var alertContent = '<div class="pcAlertWrap"><div class="pcAlert"><p class="pcAlertText">請下載最新版本</p><div class="btnWrap"><div class="closeBtn">確定</div></div></div>'

    var isApp = false;
    // 若window.android_Web不是undefined,則是在安卓的web瀏覽器中
    if((myBrowser.versions.android && window.android_Web != undefined)) {
        isApp = true;
    } else if(myBrowser.versions.ios) {
        try {
            isApp = true;
            window.webkit.messageHandlers.testAAApp.postMessage('')
        } catch (error) {
            isApp = false;
        }
    }
    if(isApp) {
        // 若在app中,則提示下載最新版本
        alertContent = `<div class="pcAlertWrap"><div class="pcAlert"><p class="pcAlertText">請下載最新版本<div class="btnWrap"><div class="sureBtn">確定</div></div></div>`
    } else {
        // 若在web中,則重定向到m.的網址去
        // 若是主域名
        if(jumpUrl.indexOf('://www') > 0) {
            jumpUrl = jumpUrl.replace('://www', '://m')
        }
        // 若已經是移動端的二級域名,則無需操作
        else if(jumpUrl.indexOf('://m.') > 0){
        } else {
            // 若是頂級域名,則插入m.
            jumpUrl = jumpUrl.replace('://', '://m.')
        }
        alertContent = `<div class="pcAlertWrap"><div class="pcAlert"><p class="pcAlertText">請使用 <a href="${jumpUrl}">${jumpUrl}</a> 訪問獲得更佳體驗</p><div class="btnWrap"><div class="sureBtn">確定</div></div></div>`
    }
    window.onload = function() {
        $('body').append(alertContent);
        $('.pcAlertWrap').addClass('active');
        $('.closeBtn').on('click',function(){
            $('.pcAlertWrap').removeClass('active');
        })
        $('.sureBtn').on('click',function(){
            $('.pcAlertWrap').removeClass('active');
            window.location.replace(jumpUrl)
        })
    }
}

3.3.3 css 樣式

<style>
    .pcAlertWrap{
    position: fixed;
    left: 0;
    top: 0;
    bottom: 0;
    right: 0;
    background: rgba(0,0,0,0.5);
    -webkit-box-align: center;
    -webkit-align-items: center;
    -ms-flex-align: center;
    align-items: center;
    justify-content: center;
    z-index: 99999;
    display: none;
    }
    .pcAlertWrap.active{
    display: -webkit-box;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex;
    }
    .pcAlert{
    position: relative;
    width: 300px;
    min-height: 150px;
    background: #fff;
    padding: 10px;
    font-size: 14px;
    border-radius: 5px;
    z-index: 1;
    }
    .pcAlertText{
    margin-top: 10px;
    text-align: center;
    }
    .btnWrap{
    position: absolute;
    width: 100%;
    right: 0;
    bottom: 10px;
    text-align: center;
    color: #fff;
    font-size: 14px;
    z-index: 999;
    }
    .closeBtn,.sureBtn{
    display: inline-block;
    margin: 0 10px;
    width: 70px;
    height:30px;
    line-height: 30px;
    background: #0000FF;
    border-radius: 4px;
    
    }
</style>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章