浏览代码

fix(v-show): v-show takes higher priority than style attribute (#3230)

fix #2757
HcySunYang 5 年之前
父节点
当前提交
5ad4036e29

+ 63 - 2
packages/runtime-dom/__tests__/directives/vShow.spec.ts

@@ -3,9 +3,11 @@ import {
   defineComponent,
   h,
   nextTick,
-  VNode
+  VNode,
+  ref,
+  watch
 } from '@vue/runtime-core'
-import { render, vShow } from '@vue/runtime-dom'
+import { render, Transition, vShow } from '@vue/runtime-dom'
 
 const withVShow = (node: VNode, exp: any) =>
   withDirectives(node, [[vShow, exp]])
@@ -124,4 +126,63 @@ describe('runtime-dom: v-show directive', () => {
     await nextTick()
     expect($div.style.display).toEqual('block')
   })
+
+  // #2583
+  test('the value of `display` set by v-show should not be overwritten by the style attribute when updated', async () => {
+    const style = ref('width: 100px')
+    const display = ref(false)
+    const component = defineComponent({
+      render() {
+        return withVShow(h('div', { style: style.value }), display.value)
+      }
+    })
+    render(h(component), root)
+
+    const $div = root.querySelector('div')
+
+    expect($div.style.display).toEqual('none')
+
+    style.value = 'width: 50px'
+    await nextTick()
+    expect($div.style.display).toEqual('none')
+
+    display.value = true
+    await nextTick()
+    expect($div.style.display).toEqual('')
+  })
+
+  // #2583, #2757
+  test('the value of `display` set by v-show should not be overwritten by the style attribute when updated (with Transition)', async () => {
+    const style = ref('width: 100px')
+    const display = ref(false)
+    const component = defineComponent({
+      setup() {
+        const innerValue = ref(false)
+        watch(display, val => {
+          innerValue.value = val
+        })
+        return () => {
+          return h(Transition, () =>
+            withVShow(
+              h('div', { style: style.value }, innerValue.value),
+              display.value
+            )
+          )
+        }
+      }
+    })
+    render(h(component), root)
+
+    const $div = root.querySelector('div')
+
+    expect($div.style.display).toEqual('none')
+
+    style.value = 'width: 50px'
+    await nextTick()
+    expect($div.style.display).toEqual('none')
+
+    display.value = true
+    await nextTick()
+    expect($div.style.display).toEqual('')
+  })
 })

+ 2 - 1
packages/runtime-dom/src/directives/vShow.ts

@@ -20,7 +20,8 @@ export const vShow: ObjectDirective<VShowElement> = {
     }
   },
   updated(el, { value, oldValue }, { transition }) {
-    if (transition && value !== oldValue) {
+    if (!value === !oldValue) return
+    if (transition) {
       if (value) {
         transition.beforeEnter(el)
         setDisplay(el, true)

+ 7 - 0
packages/runtime-dom/src/modules/style.ts

@@ -9,7 +9,14 @@ export function patchStyle(el: Element, prev: Style, next: Style) {
     el.removeAttribute('style')
   } else if (isString(next)) {
     if (prev !== next) {
+      const current = style.display
       style.cssText = next
+      // indicates that the `display` of the element is controlled by `v-show`,
+      // so we always keep the current `display` value regardless of the `style` value,
+      // thus handing over control to `v-show`.
+      if ('_vod' in el) {
+        style.display = current
+      }
     }
   } else {
     for (const key in next) {