最近在項目碰到了一個vue組件更新致使style異常的問題。下面記錄一下我本身的解決思路。javascript
因爲公司項目業務複雜,就不具體描述了。簡單說一下問題,就是項目使用vue框架,在一個頁面中根據a值來顯示不一樣組件,當a = true
時顯示A組件,不然就顯示B組件。示例代碼以下css
<template>
<div>
<div v-if="a" :style="getBackground('a')">a組件</div>
<div v-else :style="getBackground('b')">b組件</div>
</div>
</template>
<script>
export default {
name:'Example',
data: {
a: false
},
computed: {
getBackground: function(type) {
return {
background: `url(https://${type}.png) no-repeat`,
backgroundSize: '100% 100%',
}
}
}
mounted() {
setTimeout(() => { this.a = true }, 1000)
}
}
</script>
複製代碼
如上代碼,頁面加載時,顯示 a組件,且它的背景樣式是設置了backgroundImage和backgroundSize爲"100% 100%",一秒以後,a 變爲false了,這是顯示 b組件,預期之中,它也是應該設置了backgroundImage和backgroundSize爲"100% 100%",可是呢,在顯示 b組件,它的樣式,backgroundSize並非"100% 100%",而是默認的"initial",這樣致使樣式並不是咱們預期想要的。究竟爲何在顯示 b組件 時,這個backgroundSize不是咱們在getBackground
中返回的100%呢?html
爲何顯示 b 組件 時樣式不是咱們預期的呢,這裏,能夠看到 a組件 和 b組件 都是 div
標籤,根據vue官方文檔描述,它們在更新時會被複用的,就是說只會建立 a組件 的div元素,在更新b組件時,會複用 a組件 建立出來的div元素的。而且翻看了vue更新組件部分源碼,也確實會先判斷是不是相同的元素類型,若是是,就只是更新,而不會從新建立。可是,就算是複用,那也不該該把backgroundSize覆蓋了"initial"呀?況且這2個組件都設置的backgroundSize是"100% 100%"。vue
接着,我又翻看了更新style部分的源碼才發現了緣由出在哪。下面貼出vue更新stye部分的源碼以下java
// 獲取待更新vnode的style綁定值
const newStyle = getStyle(vnode, true)
// 若是在舊的vnode中且不在新的vnode的style中,則刪除
for (name in oldStyle) {
if (isUndef(newStyle[name])) {
setProp(el, name, '')
}
}
// 若是在新的vnode中,且不等於舊的vnode中值,則更新爲新的vnode中style值
for (name in newStyle) {
cur = newStyle[name]
if (cur !== oldStyle[name]) {
// ie9 setting to null has no effect, must use empty string
setProp(el, name, cur == null ? '' : cur)
}
}
複製代碼
源碼邏輯很簡單,就是先刪除了在舊的vnode中style而不在新的vnode中style的值,接着設置在新的vnode中且不等於舊的vnode中值的。結合上面咱們問題代碼,邏輯應該是,node
這樣一來,因爲 a 組件 和 b組件 是複用的同一個div元素,咱們再來具體看一下div元素style 被更新的過程,git
先是在 a組件 中,div被設置的應該是以下樣式github
div {
background: "url(https://a.png) no-repeat",
backgroundSize: '100% 100%',
}
複製代碼
咱們知道,只設置background的話,它的backgroundSize默認值是"initial",可是後面的backgroundSize會覆蓋background 中默認值,因此這時沒有毛病,顯示正常web
接着,更新爲 b組件 了,div被設置的樣式應該以下框架
div {
//background: "url(https://a.png) no-repeat", //a組件中設置樣式
backgroundSize: '100% 100%', //a組件中設置樣式
background: "url(https://b.png) no-repeat", //b組件中設置樣式
}
複製代碼
這個時候,咱們發現,實際上,設置的background會用默認值"initial"覆蓋掉以前a組件中設置的backgroundSize的"100% 100%",因此這個時候,在顯示 b組件 時,backgroundSize變爲了默認值"initial"。坑爹呀,😢。
知道問題是出如今組件複用和background設置順序問題上,那麼解決的辦法就很是簡單了,
The property is a shorthand that sets the following properties in a single declaration:
background-clip
,background-color
,background-image
,background-origin
,background-position
,background-repeat
,background-size
, andbackground-attachment
.
就業務背景而言,業務上是不可能出現頁面內a會變化的,也就是說,用戶打開頁面,那麼頁面根據a來選擇顯示哪一個組件,以後是不會變的。可是就有某種特殊狀況下,a在頁面未刷新狀況下,變化了,致使更新爲顯示另外一個組件了。本身在作業務需求時,代碼邏輯必定要多加嚴謹,同時要深刻理解框架的底層實現原理,才能更好的避免未知bug。
就這個bug而言,應該有三個基礎知識點: