首先咱们来这么一个问题, 这里是完整的 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
就是空对象,就不会出现上面的问题了。