Problem of repetition

First, let’s look at the view Design official website’s interpretation of the Modal component transfer property: whether to place the shell layer in the body. This means that when transfer is true, the Modal component is placed in the body by default, otherwise it is placed in the corresponding node of the component. Transfer is true by default.

Normally we use the Modal v-Model property to control whether or not Modal is displayed, and this process will always exist in the Dom, but in some cases we might want to remove Modal from the Dom, which leads to the following code (which can be debugger in Codesandbox: Codesandbox example) :

<template>
  <div>
    <div class="margin: 10px">{{ displayModal ? "Remove Modal from the DOM :" :" Add Modal to the DOM :"}}<Button type="primary" @click="toggleModal">{{ displayModal ? Remove Modal: Add Modal}}</Button>
    </div>
    <div v-if="displayModal" style="margin: 10px">Display through v-Model control popup:<Button type="primary" @click="modal1 = true">Show bounced</Button>
    </div>
    <div>
      <Modal
        v-if="displayModal"
        v-model="modal1"
        title="Popover test"
        @on-ok="ok"
        @on-cancel="cancel"
      >
        <p>Content of dialog</p>
        <p>Content of dialog</p>
        <p>Content of dialog</p>
      </Modal>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      displayModal: true.modal1: false}; },methods: {
    ok() {
      this.$Message.info("Clicked ok");
    },
    cancel() {
      this.$Message.info("Clicked cancel");
    },
    toggleModal() {
      this.displayModal = !this.displayModal; ,}}};</script>
Copy the code

Run the above code and click the “Show the popup” button, and the popup will be displayed, close the popup and then click “Remove the popup” to remove the Modal from the DOM, and then click “Add the Popup” to add it back to the DOM, and then click “Show the popup” and the popup will not be displayed. Huh? The popup is added back to the Dom, so it should not affect the display.

So the solution is, take the “v-if” =”displayModal” in Modal and move it to the div that wraps it around or set the transfer property of Modal to false and you’ll see that everything is fine

<div v-if="displayModal">
  <Modal
    v-model="modal1"
    title="Popover test"
    @on-ok="ok"
    @on-cancel="cancel"
  >
    <p>Content of dialog</p>
    <p>Content of dialog</p>
    <p>Content of dialog</p>
  </Modal>
</div>
Copy the code

You can probably guess from this that the problem is that by default, when transfer is true, the Modal component is placed in the body, and when displayModal is false, vue removes the Modal and finds that the node doesn’t exist, and then we have a problem. But since View-design has put Modal in the body, it must be moved back before removing the element. In order to figure out the specific details, we directly stem the source code, eager partners can directly see the conclusion.

Source code analysis

By reading the modal source code, we can see that the outermost div of the modal component uses a custom instruction v-transfer-dom (see here for those unfamiliar with custom components). Continuing through the source code of the Transfer-DOM directive, we can see that while Modal is inserted into the DOM, inserted moves Modal into the body and registers the parent node before the move, unbind is performed when Modal is removed (important point here), Unbind checks to see if the parent still exists, and if so adds Modal back to the parent.

When “v-if=”displayModal”” is written on a Modal component, the parent of the Modal is the div that wrapped it, and that div doesn’t get destroyed, so after the Modal is removed, unbind adds it back to that div, which causes the Modal not to be removed properly

When “v-if=”displayModal”” is written on the div that is wrapping Modal, it is the Modal parent that is removed, and unbind sees that the parent is not there, so it is not added again

If transfer is set to true, there is no Modal shift to move around, of course there is no problem

. inserted (el, { value }, vnode) {if( el.dataset && el.dataset.transfer ! = ='true') return false;
        el.className = el.className ? el.className + ' v-transfer-dom' : 'v-transfer-dom';
        const parentNode = el.parentNode;
        if(! parentNode)return;
        const home = document.createComment(' ');
        let hasMovedOut = false;

        if(value ! = =false) {
            parentNode.replaceChild(home, el); // moving out, el is no longer in the document
            getTarget(value).appendChild(el); // moving into new place
            hasMovedOut = true
        }
        if(! el.__transferDomData) { el.__transferDomData = {parentNode: parentNode,
                home: home,
                target: getTarget(value),
                hasMovedOut: hasMovedOut
            }
        }
    },
...
    unbind (el) {
        if(el.dataset && el.dataset.transfer ! = ='true') return false;
        el.className = el.className.replace('v-transfer-dom'.' ');
        const ref$1 = el.__transferDomData;
        if(! ref$1) return;
        if (el.__transferDomData.hasMovedOut === true) {
            el.__transferDomData.parentNode && el.__transferDomData.parentNode.appendChild(el)
        }
        el.__transferDomData = null}...Copy the code

conclusion

When the transfer property is true, it triggers the Transfer-DOM instruction of the Modal component, which will fetch the Modal parent when the Modal is inserted into the DOM and move the Modal under the body, and then when the Modal is removed it will determine if the parent that was retrieved is still there, If it’s still there, the component will be added back.

To avoid this, try not to use Modal with V-if or V-for (which causes Modal destruction and addition), and if you must put it in the parent div of the Modal. Of course, the best way is to set transfer to false, there is no problem of moving around