一个有趣的问题:
在我们编写react组件时,若该组件中的style中同时含有简写的css属性和具体的css属性名,例如同时包含background和backgroundColor属性。在每一次属性值变更时,react组件的表现是什么样子的呢?
style = {
background: "lightpink center",
backgroundColor: "lightblue"
};
// update
setStyle({
background: "lightcoral center",
backgroundColor: "lightblue"
})
首次渲染时,组件的背景色应该是lightblue,和我们预期一样;在style更新时,是不是认为backgroundColor的值会覆盖background的值,组件的背景色还应该是lightblue?
那真实情况其实是:更新后的组件背景色是lightcoral。
有兴趣童鞋可以参考这个代码链接:codesandbox.io/s/react-mix…
问题分析:
其实不仅仅是background属性会引起意想不到的问题,还包含border、 margin、 top等等其他简写的css属性。
且这个问题不会发生在初始渲染中,而是会发生在后续的update渲染阶段。
还是以上个例子为例,按照我们的理解逻辑,从
style = {
background: "lightpink center",
backgroundColor: "lightblue"
};
到
style = {
background: "lightcoral center",
backgroundColor: "lightblue"
}
应该将后出现的属性backgroundColor做merge操作到background属性中,最终得出的样式值应该为:
style = {
background: "lightblue center",
}
而其实在react的更新逻辑中,是对比lastProps和nextProps的差异做一些patch更新操作。简单来讲,react发现后一次style对比前一次style, backgroundColor的值没有改变,那backgroundColor这个属性不纳入更新范围;但是background这个属性变化了,所以本次只更新background这个属性。 那在进行background属性更新过程中,自然会把之前的backgroundColor这个属性覆盖掉。这里贴上sophie大佬给到的源码链接:github.com/facebook/re…
【小tips】 源码中主要关注两次props对比后计算出的styleUpdates这个变量。该变量会被推入updatePayload变量中。在updateDOMProperties这个函数中,会处理updatePayload变量。最后调用CSSPropertyOperations.setValueForStyles(domElement, propValue, getStack)这个函数来更新style。这个函数没有做特别的操作。只是遍历styleUpdates变量值,对dom做更新操作。
所以在组件中若有动态更新样式的操作,且涉及到简写属性时,react团队的建议是需要避免这种写法,将简写属性拆分为单个的css属性来处理。具体参考后面的issue链接。
参考:
这里有大佬的讨论。关于问题产生原因、react后续是否会解决以及建议的解决方式都包含了。建议大家认真阅读哦~