HTML5實戰之本地存儲(2) - 操作同步

webphp 13年前發布 | 16K 次閱讀 HTML5

本文介紹Tab之間的操作同步的實現,所謂操作同步是指將用戶在某個Tab窗口中的操作同步到所有其他同一站點得Tab中。例如IM窗口的操作(打開、最大化、最小化、關閉、設置等),由于很多用戶會在多個Tab之間切換,而IM在每個頁面中都存在,因此對于數據同步的要求是比較高的,以前的做法是使用服務器方式來同步,即所有操作都向服務器發請求,然后廣播,Tab收到消息后再響應。缺點是比較浪費資源,另外延時也比較嚴重。


實現

在具體的實現上,只是給封裝的Storage添加了一個sync接口,格式如下:

/*
    跨頁面同步指定的函數
    @param {function}   func        需要同步執行的函數(不允許重名,函數參數必須為string類型,否則參數無法同步)
    @param {Object}     context     返回的函數執行時的上下文
    @param {Boolean}    onlySync    是否只執行同步,為true時不執行原函數而只執行同步操作
    @return {function}  當執行新的函數時將會通過本地存儲進行同步操作
*/
function sync( func, context, onlySync ){ }

sync接口會接收一個需要同步的函數A,并對A進行處理,返回函數B。當執行函數B的時候會選擇性的執行函數A(有得情況下是已經執行了A,只需要同步,這時是不需要再次執行A函數的),然后把執行B時的參數通過本地存儲方式同步到其他Tab。

Key的生成

這里比較關鍵的一步是監聽本地存儲,而且要求每個函數在每個不同的頁面中都需要有一個相同的key,這樣才能實現多Tab下數據的精準共享。當前生成key的方式類似于對函數的字符串進行摘要操作,并且通過調整摘要的長度來調整key的唯一性。

由于基于函數字符串,因此如果是實現完全一致的匿名函數則會造成沖突,這是使用中需要注意的。以下是具體的實現:

/*
    生成函數同步的key
    算法:
        1. func.toString
        2. 獲取函數名(因為函數名一般不重名,因此保留整個函數名作為key前綴,但如果是匿名函數則無效)
        3. 將其中的空白符刪除(IE下引號等也會導致問題)
        4. 在剩余的內容中平均取N個字符拼接成key
    算法的問題在于不同的函數內容應該不能一樣,否則會出現同步異常
    通過調整字符數可以調整精度

    之所以要如此處理是為了確保每個頁面對于同一個函數生成的Key是相同的,故不能使用隨機數。
    這個算法的問題在于不允許函數重名。
 */
_genSyncKey: function( func ){
    //key長度
    var keyLen = 30,
        func = func.toString();
    //獲取函數名
    var funcName = '',
        reg = /^function\s+([^\(]+)\s*\(/ig,
        matches = reg.exec( func );
    if( matches ){
        funcName = matches[1];
        funcName = funcName.replace(/[^\w]+/ig,'');
    }

    func = func.replace(/(function|[^\w]+)/ig,'');

    keyLen -= funcName.length;

    if( keyLen <= 0 ){
        return funcName.substring( 0, 30 );
    }

    if( func.length <= keyLen ){
        return funcName + func;
    }
    var leftCharLen = keyLen,
        funcLeftLen = func.length,
        step = Math.ceil( func.length / keyLen ),
        key = [];
    for( var i = 0; i < func.length; i += step ){
        key.push( func.substring(i, i+1) );
        leftCharLen--;
        funcLeftLen -= step;
        //檢查剩余長度
        if( funcLeftLen <= leftCharLen ){
            key.push( func.substring(i) );
            break;
        }
    }
    return funcName + key.join('');
}

具體的實現中會特別保留整個函數名,但是經過壓縮的代碼意義不會很大,此外需要過濾特殊字符,不然IE下使用userData時會出錯。

過濾當前頁面觸發的事件

在sync方法的實現中,除了key得生成之外,還需要解決的問題是區分是否當前頁面觸發,如果是當前頁面觸發的onstorage則應該忽略掉,否則會造成死循環。實現方案是在特定的瀏覽器中加入鎖進行判斷,當頁面執行封裝后的B函數時表明是由當前頁面觸發的,此時會忽略onstorage事件。

參數同步

此外,函數執行時的參數是動態的,因此是需要進行同步的,由于本地同步只限于可以進行序列化的數據,所以對同步函數的參數類型有限制,目前只支持string類型。

以下是具體的實現:

/**
    跨頁面同步指定的函數
    @param {function}   func        需要同步執行的函數(不允許重名,函數參數必須為string類型,否則參數無法同步)
    @param {Object}     context     返回的函數執行時的上下文
    @param {Boolean}    onlySync    是否只執行同步,為true時不執行原函數而只執行同步操作
    @return {function}  當執行新的函數時將會通過本地存儲進行同步操作
 */
sync: function( func, context, onlySync ){
    if( !K.isFunction( func ) ) return;
    //生成存儲key(對于同一個函數而言,生成的key應該相同,這樣才能保證多個頁面對于同一個函數監聽的是同一個key)
    var key = this._genSyncKey( func );

    //數據項中前綴和數據之間的分隔符
    var split = '~{##}~';

    //參數之間的分隔符
    var argSplit = '~{###}~';

    //是否當前頁面觸發
    //(IE、Firefox3.6下的onstorage事件不論是否當前頁面都會收到,所以需要加鎖識別,確保當前頁面不響應,其他瀏覽器則不必)
    var isLocalTrigger = false;

    //監聽key變化
    this.onstorage( key, function(val){
        if( !isLocalTrigger || (!K.Browser.ie && parseInt(K.Browser.firefox) >= 4 ) ){
            var args = val.split( split )[1],
                arrArg = args.split( argSplit );
            func.apply( context, arrArg);
        }
        isLocalTrigger = false;
    });

    var ins = this;
    //返回的函數執行時,傳入參數必須為string類型,否則參數無法同步
    return function( ){
        //執行原方法
        if( !onlySync ){
            func.apply( context, arguments );
        }
        isLocalTrigger = true;
        //同步(加時間戳和隨機數是為了讓數據項發生變化,否則其他頁面無法監聽到
        ins.setItem( key,
            (new Date())*1
            + ''
            + Math.random()
            + split
            + ([].slice.call(arguments).join(argSplit))
        );
    };
}

接口的使用

參考示例頁面:http://www.varnow.org/pages/html5/storage/sync/action_sync.html

function changeColor( id, color ){
    $( id ).style.backgroundColor = color;
}
changeColor = Storage.sync( changeColor );

$('test').onclick =  function(){
    var colors = ['red','black','green','blue','yellow'];
    var color = colors[ Math.floor( Math.random() * 5 ) ];
    changeColor( 'test',color );
};

function close1(){
    this.style.display = 'none';
}

//綁定this
close1 = Storage.sync( close1, $('test') );

$('close1').onclick = function(){
    close1();
};

下一篇將介紹Tab間的通信同步。HTML5實戰之本地存儲(3) - 請求同步


原文鏈接: http://varnow.org/?p=340

 本文由用戶 webphp 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!