首先咱們來這麼一個問題, 這裏是完整的 jsfiddle demo or codepen demovue
給一個元素綁定兩個邊框樣式, 右側和底部都爲1px的紅色邊框node
styleA: { borderBottom: '1px solid red', borderRight: '1px solid red' };
而後用一個按鈕(或者任何方式)將樣式換成下面的樣式, 一個1px的綠色邊框,和1px的紅色右側邊框。git
styleB: { border: '1px solid green', borderRight: '1px solid red' };
咱們指望的結果應該是右側邊框是紅色的,其他三邊的邊框是綠色的,但實際結果倒是全部邊都是綠色的, 這裏已經出現了問題, 而後再點擊按鈕,將樣式切換回去, 此時指望的結果應該是跟一開始同樣: 右側和底部都爲1px的紅色邊框, 但實際結果倒是隻剩下底部的邊框是紅色的,右側的邊框就像消失了同樣。github
那麼, 右側的邊框樣式是否是真的消失了呢? 是否是從第一次切換就消失了呢?(這好像也能符合第一次全都是綠色邊框的表現),是CSS
的bug嗎?spa
這個style
的替換過程是在Vue
裏幫咱們實現的,是跟虛擬節點vNode
的渲染有關,接下來讓咱們去Vue
的源碼看一下這個問題究竟是怎麼樣形成的。.net
首先,vue視圖的更新經過updateComponent
進行, updateComponent
會執行一個update
的方法進行更新視圖,update會從根節點進行patch
操做, patch
操做會依次遍歷虛擬節點樹的全部vnode節點,深度優先的遍歷方式。code
一般patch
操做會update如下幾個部分對象
0: ƒ updateAttrs(oldVnode, vnode) 1: ƒ updateClass(oldVnode, vnode) 2: ƒ updateDOMListeners(oldVnode, vnode) 3: ƒ updateDOMProps(oldVnode, vnode) 4: ƒ updateStyle(oldVnode, vnode) 5: ƒ update(oldVnode, vnode) 6: ƒ updateDirectives(oldVnode, vnode)
這裏咱們只須要關注第5個方法:updateStyle
, 那麼這個方法裏作了什麼呢?
看一下核心邏輯: ip
能夠看到這段代碼的主要邏輯是用新的樣式覆蓋舊的樣式,這裏的setProp是對element.style
進行修改,也就是原生CSSStyleDeclaration
對象的實例。element
''
,看起來沒什麼問題,一切都很符合邏輯,那麼是什麼形成了上面的現象呢?
一切的罪魁禍首都在這個border
樣式的簡寫屬性(shorthand property)上。
簡寫屬性有什麼特殊的地方呢?
最直接的就是當對一個簡寫屬性賦值,例如:
border: 1px solid green;
這個賦值會被轉換爲:
borderWidth: "1px" borderStyle: "solid" borderColor: "green" borderTop: "1px solid green" borderTopColor: "green" borderTopStyle: "solid" borderTopWidth: "1px" borderRight: "1px solid green" borderRightColor: "green" borderRightStyle: "solid" borderRightWidth: "1px" borderLeft: "1px solid green" borderLeftColor: "green" borderLeftStyle: "solid" borderLeftWidth: "1px" borderBottom: "1px solid green" borderBottomColor: "green" borderBottomStyle: "solid" borderBottomWidth: "1px"
也就是說borderTop
, borderLeft
, borderRight
, borderBottom
也都被賦值了.
因此,回到上面的那個切換過程,根據updateStyle
源碼進行分析:
從styleA
切換爲styleB
時,
for
循環, borderBottom
不在 oldStyle 中,被清空,borderRight
在 oldStyle 中,保留了下來。for
循環, border
不在 oldStyle 中,設置border
的值,注意此時borderTop
, borderLeft
, borderRight
, borderBottom
也都被賦值了,而後borderRight
與 oldStyle 中保留下來的值相等, 跳過此次賦值。borderTop
, borderLeft
, borderRight
, borderBottom
都顯示 border
的值。從styleB
切換回爲styleA
時,
for
循環, border
不在 oldStyle 中,border
的值被清空,此時borderTop
, borderLeft
, borderRight
, borderBottom
也都被清空,而後borderRight
在 oldStyle 中, 跳過此次賦值。for
循環, borderBottom
不在 oldStyle 中,borderBottom
被賦值,borderRight
與 oldStyle 中保留下來的值相等, 跳過此次賦值borderBottom
的值。那麼,原理搞清楚了,有什麼好的解決方案呢? 這個問題在Vue的github上已經被提過issue了,看下尤雨溪的官方回覆
這個問題被定性爲了一個wontfix
,但也給出了有效的解決方案:
key
, 當樣式有任何變化的時候,key
就會變化,在Vue
的更新渲染邏輯中,若是元素的key
發生變化,那麼oldstyle
就是空對象,就不會出現上面的問題了。