起因
在某个项目中,部分电脑上会出现保费以及价值等文本元素在视图上没有发生改变,但是数据是已经更新了的。
在排查过程中,发现其他的元素的显隐规则是能够正常运行的,并且在更改了保费元素的类型为input元素后,也是能够正常更新视图的。
在查看页面元素时,发现chrome开启了翻译功能,导致文本元素的节点发生了变化,如下面所示:
1 | <!-- 原元素 --> |
这个项目在html文件上没有指定语言为中文,翻译后才会有节点的变化。
根据这个元素的变化,初步确定是Vue更新视图时因为元素发生变化,导致无法正常更新视图的问题。
Vue patch算法更新视图失败的原因
Vue的patch算法,是先对vdom进行比对,判断是相同、新增还是删除节点而做出不同的操作。因为是对vdom进行比对,所以chrome翻译导致的新增节点,无法重新映射回vdom节点。
所以在比对时,vue会认为本次更新时,这个文本元素只有值的变化,所以最终只更改了span标签下文本节点的textContent,但这时span标签下并没有这个文本节点,所以即使更改了textContent,也是无用的,因为它并不能映射到真实元素上面。
解决办法
- html需要指定正确的语言,这样在同个语言环境下,即使强行翻译页面,chrome也不会在文本元素下增加新的元素。(但是在不同语言环境下,如果将页面翻译成其他的语言,也依旧会存在视图更新失败的问题)
- 将模板语法两个大花括号改为v-text,虽然大部分资料上写的是两个大花括和v-text没有什么区别,但是vue的模板编译和patch算法对这两种语法的处理形式是不同的。
- 模板语法在编译过后,就是一个正常的节点关系,在patch的时候也依赖于vdom和真实dom的对应层级关系。
- v-text编译后本质上是在该节点上将textContent属性和值绑定在一起了,并且在patch时,v-text会将该节点下的子节点都清空,然后再更新textContent属性。这也是为什么Chrome翻译页面后,在文本节点下新增子节点,Vue也能够正常更新视图的原因。
模板语法与v-text编译后的区别
1 | <div id="app"> |
1 | function render() { |