Vue.js 渲染简写样式存在的问题
引出问题
首先我们来这么一个问题, 这里是完整的 jsfiddle demo or codepen demo
给一个元素绑定两个边框样式, 右侧和底部都为1px的红色边框
styleA: {
borderBottom: '1px solid red',
borderRight: '1px solid red'
};
然后用一个按钮(或者任何方式)将样式换成下面的样式, 一个1px的绿色边框,和1px的红色右侧边框。
styleB: {
border: '1px solid green',
borderRight: '1px solid red'
};
我们期望的结果应该是右侧边框是红色的,其余三边的边框是绿色的,但实际结果却是所有边都是绿色的, 这里已经出现了问题, 然后再点击按钮,将样式切换回去, 此时期望的结果应该是跟一开始一样: 右侧和底部都为1px的红色边框, 但实际结果却是只剩下底部的边框是红色的,右侧的边框就像消失了一样。
那么, 右侧的边框样式是不是真的消失了呢? 是不是从第一次切换就消失了呢?(这好像也能符合第一次全都是绿色边框的表现),是CSS
的bug吗?
这个style
的替换过程是在Vue
里帮我们实现的,是跟虚拟节点vNode
的渲染有关,接下来让我们去Vue
的源码看一下这个问题到底是怎么样造成的。
Vue更新视图机制
首先,vue视图的更新通过updateComponent
进行, updateComponent
会执行一个update
的方法进行更新视图,update会从根节点进行patch
操作, patch
操作会依次遍历虚拟节点树的所有vnode节点,深度优先的遍历方式。
通常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
, 那么这个方法里做了什么呢?
看一下核心逻辑:
可以看到这段代码的主要逻辑是用新的样式覆盖旧的样式,这里的setProp是对element.style
进行修改,也就是原生CSSStyleDeclaration
对象的实例。
- 首先将不存在于newStyle中的oldStyle的样式设置为
''
, - 然后再设置与oldStyle中样式值不相等的newStyle的样式,
看起来没什么问题,一切都很符合逻辑,那么是什么造成了上面的现象呢?
一切的罪魁祸首都在这个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
,但也给出了有效的解决方案:
- 给这个元素一个用样式生成的hash值作为
key
, 当样式有任何变化的时候,key
就会变化,在Vue
的更新渲染逻辑中,如果元素的key
发生变化,那么oldstyle
就是空对象,就不会出现上面的问题了。
原文地址:https://segmentfault.com/a/1190000016895358
Vue.js 渲染简写样式存在的问题的更多相关文章
- vue.js 渲染完成回调
vue.js渲染完成后,想触发一些事情,写在哪里呢? 答案是mounted 例子: new Vue({ el:'#demo', data:{ text:'Hello' }, mounted:funct ...
- Vue.js 控制css样式
<script src="https://unpkg.com/vue/dist/vue.js"></script> <style type=" ...
- Vue.js 渲染函数, JSX(未掌握,未学完)
渲染函数 , JSX(没完成学习) 基础: 实例属性:vm.$slots default 属性包括了所有没有被包含在具名插槽中的节点. 渲染函数: render: function(createEle ...
- Vue.js 内联样式绑定style
html <div class="Menu" v-bind:style="{height:clientHeight}"> </div> ...
- 前端云原生,以 Kubernetes 为基础设施的高可用 SSR(Vue.js) 渲染微服务初探(开源 Demo)
背景 笔者在逛掘金的时候,有幸看到掘友狼族小狈开源的 genesis - 一个可以支持 SSR 和 CSR 渲染的微服务解决方案.总体来说思想不错,但是基于 Kubernetes 云原生部署方面一直没 ...
- 用Vue.js开发微信小程序:开源框架mpvue解析
前言 mpvue 是一款使用 Vue.js 开发微信小程序的前端框架.使用此框架,开发者将得到完整的 Vue.js 开发体验,同时为 H5 和小程序提供了代码复用的能力.如果想将 H5 项目改造为小程 ...
- 学习Vue.js之vue移动端框架到底哪家强
官网:https://cn.vuejs.org/. 转载:http://www.cnblogs.com/8899man/p/6514212.html Weex 2016年4月21日,阿里巴巴在Qcon ...
- 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十七 ║Vue基础:使用Vue.js 来画博客首页+指令(一)
缘起 书说前两篇文章<十五 ║ Vue前篇:JS对象&字面量&this>和 <十六 ║ Vue前篇:ES6初体验 & 模块化编程>,已经通过对js面向对 ...
- vue.js使用详解
1.什么是vue.jsvue.js是一款数据驱动型的js框架.何为数据驱动型?html视图层定义模板,vue定义数据.html和vue数据,通过标签id关联. 2.vue.js引入<script ...
随机推荐
- jQuery中的closest()和parents()的差别
jQuery中的closest()和parents()的差别 jQuery中closest()和parents()的作用非常类似,都是向上寻找符合选择器条件的元素,可是他们之间有一些细微的差别,官网也 ...
- HTML5的未来
2014年10月29日,万维网联盟(W3C)宣布,经过差点儿8年的艰辛努力.该标准规范终于终于制定完毕.之所以是8年,由于在1999年HTML4的规范制定以后,W3C对于HTML的发展.貌似就不再那么 ...
- Codeforces444A_DZY Loves Physics
DZY Loves Physics time limit per test 1 second memory limit per test 256 megabytes input standard in ...
- HDU 5412 CRB and Queries(区间第K大 树套树 按值建树)
题目链接:http://acm.hdu.edu.cn/showproblem.php? pid=5412 Problem Description There are N boys in CodeLan ...
- luogu3942 将军令 贪心
题目大意:给你一个地图(树),共有1~n个驿站(点),编号分别为1~n,告诉你第ui个驿站与第vi个驿站有一条长度为1的路(边),每个小队(可以放在任意驿站上)最多有k的覆盖长度,问最多要放置多少个小 ...
- Camera-hal参数调整
路径: vendor/mediatek/proproetary/custom/mt6735/hal/D1/imgsensor/对应的SENSOR目录 .../D1/flashlight/flash_t ...
- nyoj--171--聪明的kk(动态规划--博弈)
聪明的kk 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描述 聪明的"KK" 非洲某国展馆的设计灵感源于富有传奇色彩的沙漠中陡然起伏的沙丘,体现出本国不 ...
- 【撸码caffe 二】 blob.hpp
Blob类是caffe中对处理和传递的实际数据的封装,是caffe中基本的数据存储单元,包括前向传播中的图像数据,反向传播中的梯度数据以及网络层间的中间数据变量(包括权值,偏置等),训练模型的参数等等 ...
- 谷歌浏览器(chrome) —— 扩展应用程序
工具的使用--谷歌浏览器(chrome) (二) 1. 设置和下载方法 右上角菜单按钮 ⇒ 更多工具(more tools) ⇒ 扩展(Extensions) 打开该页面之后,会首先进入扩展(已安装应 ...
- [CQOI 2007] 涂色
[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1260 [算法] 区间DP [代码] #include<bits/stdc++. ...