Subscribe on changes!

After moving an element to elsewhere in dom tree, the subsequent patching result seem to be affected. is this reasonable?

avatar
Nov 13th 2023

Vue version

3.3.4

Link to minimal reproduction

https://stackblitz.com/edit/vitejs-vite-prfqzh?file=src%2Fmain.js

Steps to reproduce

here is the simplified code:

// main.js 

import { createApp, defineComponent, h, onMounted, ref, toRaw } from 'vue'

const prevData = [
  { name: 'child-1' },
  {
    name: 'parent', children: [
      { name: 'child-2' },
    ]
  }
]
const nextData = [
  { name: 'child-1' },
  { name: 'child-2' },
]
const data = ref(prevData)

const Child = defineComponent({
  name: 'Child',
  props: {
    name: String,
    host: Object
  },
  setup(props) {
    return () => h('div', { id: props.name }, [props.name])
  }
})

const Parent = defineComponent({
  name: 'Parent',
  props: {
    name: String
  },
  setup(props, { slots }) {
    return () => h('div', { id: props.name }, [
      props.name,
      slots.default()
    ])
  }
})

createApp({
  name: "App",

  setup() {
    onMounted(() => {
      setTimeout(() => {
        /** in real scenario, it's drag and drop operation */
        const child2 = document.querySelector(`#child-2`)
        const container = document.querySelector('#container')
        container.append(child2)

        /** "nextData" may be fetched form the backend */
        data.value = nextData
      }, 1000)
    })

    return () => h('div', { id: "container" }, data.value.map((i) => {
      if (!i.children) {
        return h(Child, { key: i.name, name: i.name })
      }
      return h(Parent, { key: i.name, name: i.name }, {
        default: () => i.children.map((j) => h(Child, { key: j.name, name: j.name, host: i }))
      })
    }))
  }
}).mount('#app')

What is expected?

child-1
child-2

What is actually happening?

child-1
child-2
child-2

System Info

No response

Any additional comments?

No response

avatar
Nov 13th 2023

when vode "child-2" was unmounted due to the unmounting of vnode "parent", it's corresponding element didn't removed. Shouldn't a vnode preserve a reference to its corresponding element?
but why this example act as excepted: https://stackblitz.com/edit/vitejs-vite-pdzx73?file=src%2Fmain.js

avatar
Nov 13th 2023

when vode "child-2" was unmounted due to the unmounting of vnode "parent", it's corresponding element didn't removed. Shouldn't a vnode preserve a reference to its corresponding element?

When Vue unmounts parent, there is no need to do anything with child-2 as that is a child of parent, as far as Vue knows from the vdom. So child-2 would be removed alongside its parent anyway.

Generally: Manually moving around elements that Vue controls will usually mess up your rendering. Avoid doing that.