最近在项目碰到了一个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而言,应该有三个基础知识点: