成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

React.js 下的 $.data() "踩坑"實(shí)錄

yy13818512006 / 3632人閱讀

摘要:愚安我這時(shí)候在睡午覺,迷糊中被他叫醒。很顯然,二者是不同類型的。后記這篇寫在愚安我離職的第二天,在星巴克坐了一下午,無聊寫的,延續(xù)了我以往寫東西狂貼代碼湊字?jǐn)?shù)的原則。

引子

最近在做得一個(gè)項(xiàng)目,我是基于reactjs來寫的。項(xiàng)目不大不小,就帶了個(gè)童鞋一起寫,為了不讓react寫起來那么吃力,我還是引入了jquery (1.11.1)。就這樣整個(gè)項(xiàng)目開展的還算順利,期間踩到了一些坑,但都是react的,直到...

一切都源于這樣的一個(gè)寫法

_edit:(e)->
    $ele = $(e.currentTarget).parents("td")
    _name = $ele.data("name")
    _filterArr = @props.items.filter (item)->
        item.activityName is _name
    if _filterArr.length
        @props.onEditCallBack(_filterArr[0]) if @props.onEditCallBack
    e.preventDefault()
    return

很簡(jiǎn)單地一段coffee,獲取綁在td上的data-name,然后在items里找到name為_name的item,執(zhí)行callback

可發(fā)布到線上之后,出了問題,有得item就是無法編輯,線上代碼又uglify過,不好調(diào)試,這位童鞋看了半天代碼也沒有發(fā)現(xiàn)什么問題。愚安我這時(shí)候在睡午覺,迷糊中被他叫醒。

點(diǎn)了下頁面發(fā)現(xiàn),頁面上一個(gè)data-name="111"的item無法刪除,看了下代碼之后,拽拽的對(duì)他說:“不要亂用jquery的data,這里有緩存,大小寫,類型轉(zhuǎn)換三大坑,看源碼去!”。然后將原來的data改為getAttribute之后,果然跑通了。為什么跑通,且看下文。事后,我也不知道當(dāng)時(shí)為什么突然來了這句三大坑。既然說了,那總要跟別人講下三個(gè)坑吧,不能打臉,不能不講道理是吧。

先說data屬性

貼一段MDN上關(guān)于data屬性的介紹,鏈接

  

HTML5是具有擴(kuò)展性的設(shè)計(jì),它初衷是數(shù)據(jù)應(yīng)與特定的元素相關(guān)聯(lián),但不需要任何定義。data-* 屬性允許我們?cè)跇?biāo)準(zhǔn)內(nèi)于HTML元素中存儲(chǔ)額外的信息,而不許需要使用類似于 classList,標(biāo)準(zhǔn)外屬性,DOM額外屬性或是 setUserData之類的伎倆。

一股濃濃的谷歌翻譯味兒,英語好的童鞋還是去看原文,或者幫忙去翻譯下,就在愚安我寫這篇博客的時(shí)候,順便提交了下翻譯,連我這種大學(xué)英語考試總共有幾級(jí)都不知道的人都敢翻譯,何況你呢。

  

在外部使用JavaScript去訪問這些屬性的值同樣非常簡(jiǎn)單。你可以使用getAttribute()配合它們完整的HTML名稱去讀取它們,但標(biāo)準(zhǔn)定義了一個(gè)更簡(jiǎn)單的方法:DOMStringMap你可以使用dataset讀取到數(shù)據(jù)。

文檔里寫到無論是通過getAttribute()還是dataset都可以輕松訪問節(jié)點(diǎn)上得data-*屬性的值,但二者是有區(qū)別的。

getAttribute()與dataset的區(qū)別

這里補(bǔ)充一點(diǎn)兒關(guān)于DOM的小知識(shí),直接訪問節(jié)點(diǎn)屬性和通過getAttribute訪問節(jié)點(diǎn)屬性返回的結(jié)果不一定是一樣的,但getAttribute和attributes["索引"]訪問節(jié)點(diǎn)屬性的結(jié)果一定是不同的(即使都訪問都不存在的屬性,前者返回null,后者返回undefined),舉個(gè)例子

var div = document.getElementById("test");
div.name 
//undefined
div.id 
//"test"
div.getAttribute("name") 
//"div"
div.attributes["name"] 
//name="div"
Object.prototype.toString.call(div.attributes["name"])
//"[object Attr]"

事實(shí)上,對(duì)于DOM節(jié)點(diǎn)而言,id與attributes是同樣等級(jí)的屬性。DOM不熟的同學(xué),可以去看看這方面的資料,這里我就不跑題了。

繼續(xù)看區(qū)別。

Object.prototype.toString.call(div.dataset)
//"[object DOMStringMap]"
Object.prototype.toString.call(div.attributes)
//"[object NamedNodeMap]"

很顯然,二者是不同類型的map。

div["data-a"] = 1
//1
div.getAttribute("data-a")
//null
div.attributes["data-a"]
//undefined
div.dataset["a"]
//undefined
//--------------------
div.setAttribute("data-foo", "bar")
//undefined
div.getAttribute("data-foo")
"bar"
div.attributes["data-foo"]
//data-foo="bar"
div.dataset["foo"]
//"bar"
//--------------------
div.dataset["foo2"] = "123"
//"123"
div.getAttribute("data-foo2")
//"123"
div.attributes["data-foo2"]
//data-foo2="123"
div["data-foo2"]
//undefined

通過以上三種方式,大家應(yīng)該大致知道節(jié)點(diǎn)字段,節(jié)點(diǎn)屬性,節(jié)點(diǎn)dataset之間的小關(guān)系與區(qū)別
再來貼一段文檔

  

為了使用dataset對(duì)象去獲取到數(shù)據(jù)屬性,需要獲取屬性名中data-之后的部分(要注意的是破折號(hào)連接的名稱需要轉(zhuǎn)換為駝峰樣式的名稱)。

測(cè)試

div.setAttribute("data-foo-bar",123)
//undefined
div.dataset["fooBar"]
//"123",仍為字符型
div.dataset["bar-foo"] = 123
//Uncaught DOMException: Failed to set the "bar-foo" property on "DOMStringMap": "bar-foo" is not a valid property name.
div.dataset["barFoo"] = 123
//123
div.getAttribute("data-bar-foo")
//"123"
div.dataset["barFoo"]
//"123",仍為字符型

可見這里確實(shí)存在喜聞樂見的camelCase轉(zhuǎn)換。

再說jquery.data的"坑"

開始翻jquery-1.11.1的源碼中得data函數(shù)。
注:jquery2放棄了對(duì)一些對(duì)低版本瀏覽器的支持,“坑”不全,我們還是看1.X的。

jQuery.extend({
    cache: {},
    //當(dāng)設(shè)置下面這三種元素的expando屬性時(shí)會(huì)拋出異常
    //具體方法參見jquery的src/data/accepts下的jQuery.acceptData方法
    noData: {
        "applet ": true,
        "embed ": true,
        // ...但是 Flash對(duì)象 (擁有classid)可以處理expando
        "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
    },

    hasData: function( elem ) {
        elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
        return !!elem && !isEmptyDataObject( elem );
    },

    data: function( elem, name, data ) {
        return internalData( elem, name, data );
    },

    removeData: function( elem, name ) {
        return internalRemoveData( elem, name );
    },

    // For internal use only.
    _data: function( elem, name, data ) {
        return internalData( elem, name, data, true );
    },

    _removeData: function( elem, name ) {
        return internalRemoveData( elem, name, true );
    }
});

這個(gè)就是jquery.data的大致結(jié)構(gòu),比較清晰。接下來,我們來聊聊前面說的三大坑。

類型轉(zhuǎn)換坑

首先回到最開始的事故代碼里,熟悉coffee的童鞋都知道,is關(guān)鍵字,在編譯到javascript時(shí),會(huì)變成===號(hào)(強(qiáng)等于),而存儲(chǔ)在item里的name時(shí)字符型,通過$("selector").data()函數(shù)獲取文檔節(jié)點(diǎn)的data-*屬性上的值時(shí),調(diào)用得是jquery.fn.data方法,這里就不貼完整代碼了,貼下造成這個(gè)類型轉(zhuǎn)換的部分dataAttr()。

if ( data === undefined && elem.nodeType === 1 ) {

    var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();

    data = elem.getAttribute( name );

    if ( typeof data === "string" ) {
        try {
                //布爾型轉(zhuǎn)換
            data = data === "true" ? true :
                data === "false" ? false :
                //null型轉(zhuǎn)換
                data === "null" ? null :
                // 僅當(dāng)將其轉(zhuǎn)換成數(shù)字時(shí),其字符值相對(duì)原字符值不變時(shí),進(jìn)行number型轉(zhuǎn)換
                +data + "" === data ? +data :
                //json字符串到object的轉(zhuǎn)換,rbrace = /^(?:{[wW]*}|[[wW]*])$/
                rbrace.test( data ) ? jQuery.parseJSON( data ) :
                data;
            } catch( e ) {}

            // Make sure we set the data so it isn"t changed later
            jQuery.data( elem, key, data );

        } else {
            data = undefined;
        }
    }

通過我注釋的部分可以很容易看出,jquery在調(diào)用jquery.data()前,會(huì)對(duì)傳入的data值進(jìn)行類型轉(zhuǎn)換,其中轉(zhuǎn)換為number的部分就是造成引子中提到到bug的原因。當(dāng)然,jQuery這里完全是為了方便大家使用,我這里說采坑,純屬強(qiáng)行甩鍋給jquery。

當(dāng)然,我們上面測(cè)試過原生的javascript通過dataset或者getAttribute都不會(huì)做這種類型轉(zhuǎn)換。
舉個(gè)栗子

$(div).data("foo-bar")
//123,number型
$(div).data("fooBar")
//123
div.dataset["fooBar"]
//"123",字符型
大小寫轉(zhuǎn)換坑

在上面代碼中,我們注意到這么一段

//rmultiDash = /([A-Z])/g;
var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
    data = elem.getAttribute( name );

這里現(xiàn)將key中所有的大寫字母前加“-”,然后統(tǒng)一轉(zhuǎn)換為小寫。
再舉個(gè)栗子

var div = document.createElement("div"),
    key = "ID",
    id = 123;
div.setAttribute("data-"+key, id);
$(div).data(key);
//undefined

當(dāng)然前面也已經(jīng)講過,即使使用dataset這種結(jié)果。把這個(gè)“坑”,算在jquery的頭上實(shí)在是不講道理。不過這里,也是給像我這樣比較粗心的前端童鞋,提個(gè)醒,直接寫在html里的data-*中記得要用小寫,避免不必要的bug。

緩存坑

在jquery.data中核心的internalData函數(shù)里,進(jìn)行了主要的cache讀寫操作。我們調(diào)用$(selector).data(key,value)的時(shí)候,進(jìn)行的流程大致如下

key,value格式化處理

檢查elem是否有elem[internalKey],若有則作為cache中對(duì)應(yīng)得id,若無則做如下處理

id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++;
//deletedIds默認(rèn)為[]記錄被鏟除的id的數(shù)組
//guid是默認(rèn)為1的計(jì)數(shù)器
//這樣可以保證被刪除的元素的id能夠被放到deletedIds再利用,而不是無線遞增guid造成枯竭

拿到id之后,檢查jQuery.cache[id]是否存在,若不存在則jQuery.cache[id] = {}

將key為傳入key的camelCase形式,value為做相應(yīng)處理的value的鍵值對(duì)放入jQuery.cache[id]中

返回jQuery.cache[id]

  

注:internalKey = jQuery.expando = "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /D/g, "" )

同理,調(diào)用$(selector).data(key)時(shí),也是現(xiàn)做key處理,id處理,去jQuery.cache[id]這個(gè)Object中拿到對(duì)應(yīng)key的value,或返回undefined。

由于jquery這種cache機(jī)制,導(dǎo)致如果一個(gè)DOM節(jié)點(diǎn)上存在internalKey,且其剛好對(duì)應(yīng)一個(gè)可以命中的cacheID,則無法通過jQuery.data()方法拿到data-*對(duì)應(yīng)的值,而是cache對(duì)應(yīng)的值。

這種情形最容易在類似reactjs這種virtual-DOM在對(duì)一組元素做部分刪除操作時(shí)出現(xiàn)。因?yàn)関irtual-DOM是做增量更新,刪除的virtual-DOM并不一定是將我們主觀視覺上看到的那個(gè)DOM節(jié)點(diǎn),而是將相鄰DOM節(jié)點(diǎn)進(jìn)行增量更新,此時(shí)雖然data-*屬性仍是原來的值,但整個(gè)DOM卻是那個(gè)本來已經(jīng)被刪除的元素,所以如果那個(gè)被刪除的DOM元素曾經(jīng)調(diào)用過data方法,保留了iternalKey的話,那么恭喜你,你碰到我說的緩存坑了。

當(dāng)然上面這種情況,也很容易通過getAttribute("data-*")處理解決掉,不是上面大問題,無須擔(dān)心。

后記

這篇blog寫在愚安我離職的第二天,在星巴克坐了一下午,無聊寫的,延續(xù)了我以往寫東西狂貼代碼湊字?jǐn)?shù)的原則??梢宰鳛閖Query.data()的一個(gè)小解讀,也可以算是對(duì)我前段時(shí)間項(xiàng)目中遇到的一些小問題的記錄。感謝大家閱讀,如有錯(cuò)誤,歡迎指出。

  

作者博客原文地址

參考資料:

https://developer.mozilla.org/zh-CN/docs/Web/Guide/HTML/Using_data_att...

https://github.com/jquery/jquery/blob/1.11-stable/src/data.js

http://blog.rx836.tw/blog/jquery-data-cache/

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/85661.html

相關(guān)文章

  • Skypack布局前端基建實(shí)現(xiàn)過程詳解

      用vite作為項(xiàng)目打包工具,這是為什么?其中最主要的原因是 ——vite在開發(fā)環(huán)境基于ESM規(guī)范實(shí)現(xiàn)的Nobundle模式,節(jié)省了代碼打包的時(shí)間。  當(dāng)前打包的需求任然有,且ESM規(guī)范兼容性越來越好,進(jìn)入生產(chǎn)環(huán)境大面積可用的狀態(tài)也不是不可能?! ‘?dāng)生產(chǎn)環(huán)境打包將不再是剛需時(shí)?! ×硪环矫?,從HTTP協(xié)議的角度看,在HTTP/1.1時(shí)代,多個(gè)模塊被打包成一個(gè)文件能減少瀏覽器并發(fā)請(qǐng)求數(shù),達(dá)到優(yōu)化目...

    3403771864 評(píng)論0 收藏0
  • 活動(dòng)實(shí)錄|拒絕"刪庫到跑路",探究餓了么數(shù)據(jù)安全保障體系

    摘要:數(shù)人云告別人肉運(yùn)維上海的實(shí)錄第二彈來啦本次分享的嘉賓是餓了么團(tuán)隊(duì)負(fù)責(zé)人虢國飛。虢國飛餓了么團(tuán)隊(duì)負(fù)責(zé)人從事數(shù)據(jù)庫領(lǐng)域年,主要關(guān)注于數(shù)據(jù)庫管理自動(dòng)化建設(shè)和等領(lǐng)域的研究。本次主題關(guān)于數(shù)據(jù)安全的保障。在這一層,餓了么做了一些數(shù)據(jù)方面相關(guān)的保護(hù)。 數(shù)人云告別人肉運(yùn)維上海Meetup的實(shí)錄第二彈來啦!本次分享的嘉賓是餓了么DBA團(tuán)隊(duì)負(fù)責(zé)人虢國飛。實(shí)錄將從用戶訪問、數(shù)據(jù)庫架構(gòu)體系、數(shù)據(jù)備份、數(shù)據(jù)流轉(zhuǎn)...

    xiaowugui666 評(píng)論0 收藏0
  • 活動(dòng)實(shí)錄|拒絕"刪庫到跑路",探究餓了么數(shù)據(jù)安全保障體系

    摘要:數(shù)人云告別人肉運(yùn)維上海的實(shí)錄第二彈來啦本次分享的嘉賓是餓了么團(tuán)隊(duì)負(fù)責(zé)人虢國飛。虢國飛餓了么團(tuán)隊(duì)負(fù)責(zé)人從事數(shù)據(jù)庫領(lǐng)域年,主要關(guān)注于數(shù)據(jù)庫管理自動(dòng)化建設(shè)和等領(lǐng)域的研究。本次主題關(guān)于數(shù)據(jù)安全的保障。在這一層,餓了么做了一些數(shù)據(jù)方面相關(guān)的保護(hù)。 數(shù)人云告別人肉運(yùn)維上海Meetup的實(shí)錄第二彈來啦!本次分享的嘉賓是餓了么DBA團(tuán)隊(duì)負(fù)責(zé)人虢國飛。實(shí)錄將從用戶訪問、數(shù)據(jù)庫架構(gòu)體系、數(shù)據(jù)備份、數(shù)據(jù)流轉(zhuǎn)...

    qc1iu 評(píng)論0 收藏0
  • vue導(dǎo)入處理Excel表格功能步驟實(shí)例

      1. 前言  本篇文章就是為大家講講前端導(dǎo)入并處理excel表格的情況,順便講講vue導(dǎo)入并處理excel數(shù)據(jù);也總結(jié)下使用工具?! ?.vue導(dǎo)入Excel表格  vue導(dǎo)入Excel表格主要有兩種常用的方法,一個(gè)是借助ElementUI文件上傳進(jìn)行表格導(dǎo)入,另一個(gè)是自帶的input做文件上傳;以下對(duì)兩個(gè)方法做詳細(xì)介紹;  2.1 使用ElementUI中的upload組件  安裝Eleme...

    3403771864 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<