This title can be a mouthful, so allow me to explain it briefly.

When we were developing with Vue, the Dom’s final rendering structure was consistent with the template nested logic, with a strict parent-child relationship.

What if we had a special need to render the Dom of a viewModel outside of its parent?

Is there really such a need?

You may be surprised to read the previous introduction and realize that you have never had such a requirement in your development experience.

What kind of scenarios need to transfer the Dom?

If you’ve had any experience with UI component development, you’ve probably noticed that as modules get smaller and deeper, the Dom of some components doesn’t fit into the Dom they belong to.

Examples include the usual back to the top button, hover ads, and the following three more vivid examples.

  • dialog
  • Right-click menu
  • User prompt component

Take ten seconds to think about the features of these components.

start

10

9

8

7

6

5

4

3

2

1

The end of the

These modules are user interaction components, not part of the static layout of the page, and require user action to wake them up.

And limited by overflow Settings at all levels of the page, it is easy to render incomplete if the embedded structure is too deep. Z-index management is also more confusing, and frequent creation and destruction can cause unnecessary local redraws.

Therefore, when developing UI user interaction components, consider moving the Dom outside of the component if necessary, which may be easier to manage.

Two, encountered what egg pain function?

The reason why I think about this question is that recently the team where Xiaoju works is developing a new product. It is not convenient to disclose the specific product name for the time being. I will look for an opportunity to talk with you after the launch.

Since the interactive nature of the product required a “right-click menu” feature, it was customary to start looking for previously published open source packages on Npm. The API support and component initialization methods may differ greatly from expectations, so I decided to develop a right-click menu component.

Fortunately, Jiro wrote a right-click menu component back in the days of jQuery, so the main logic didn’t take much time.

Combined with the characteristics of user interaction components mentioned above, the Dom of the right-click menu should be removed from the trigger element level to ensure that the menu is not restricted by overflow of various levels and to facilitate the management of z-index level.

It is because of the development of this feature and the relevant code comparison that Xiaoju concludes this article.

How to transfer Dom?

After my own practice and research on previous code, I found that there are about two schemes to realize Dom transfer, which are as follows:

  • Pre-render transfervnode
  • Post-render transfer$elnode

There are many ways to use a vnode before rendering. Here we use new Vue to create a new node as an example.

Moving the $EL node after rendering is relatively easy to understand, as illustrated in the following two examples.

3.1, with the help ofnew VueCreating a New node

Here is the scheme used by small play, is a transfer vNode before rendering, the implementation logic is more stupid. Leaving the logic behind the right-click menu, let’s just talk about how to transfer the Dom via New Vue.

Let’s start with two pieces of code, and we’ll go through them later.

Code 1: Define the core module

// Provides a menu definition component for user use
Vue.component('contextmenu', {
  name: 'contextmenu-collect',
  render () {
    return null}})// Define directives
Vue.directive('menu', {
  inserted (el, binding, vnode) {
    let vm = vnode.context
    let refKey = binding.arg
    // Listen for right-click menu events
    el.addEventListener('contextmenu', event => {
      createMenu(event, vm.$refs[refKey].$slots.default)
      event.preventDefault()
    })
  }
})
// Create a menu
function createMenu (event, vnode) {
  let newNode = document.createElement('div')
  document.body.appendChild(newNode)
  new Vue({
    el: newNode,
    render(createElement) {
      createElement(
      	'div',
        {},
        vnode
      )
    }
  })
}
Copy the code

Code two: user side use

<button v-menu:menu-node>Right click on me</button>
<contextmenu ref="menu-node">
    <div>I'm an element in the menu</div>
    <span>I'm an element in the menu</span>
</contextmenu >
Copy the code

Let’s look at the simplest module: ContextMenu, which doesn’t handle any logic, even from the render function, which doesn’t render any Dom nodes.

Look at the Menu directive, which is the core of all functionality, blocking browser default events by listening for element right-click menu events and creating mock menus based on user-defined menu contents.

Inside the createMenu method, a new node is created under the body node by default, and the vnode is transferred to the outside via new Vue, which acts as a Dom transfer.

Small thinking

The Render method of the ContentMenu module does not render anything, so why can vNode be found?

3.2 manual appendChild mode

I don’t know if you’ve ever used the Element UI framework. Her Dialog Dialog has a prop parameter called Append-to-body, and as soon as you add this parameter, the Dialog will be rendered under the body node of the page.

This simple parameter can solve a number of problems such as popover nesting, let’s see how it is implemented.

Dialog code: github.com/… / dialog /…

 mounted() {
   if (this.visible) {
     this.rendered = true;
     this.open();
     if (this.appendToBody) {
       document.body.appendChild(this.$el);
     }
   }
 },
 destroyed() {
   // if appendToBody is true, remove DOM node after destroy
   if (this.appendToBody && this.$el && this.$el.parentNode) {
     this.$el.parentNode.removeChild(this.$el); }}Copy the code

Element’s IMPLEMENTATION of the Dom transfer to Dialog is simpler.

In the Mounted stage, the Dom has been rendered. In accordance with the conditions, the Dom is forcibly moved to the body, and then the Dom node is manually removed when the module is destroyed.

The transition of the Dom is accomplished through the operation of one or two lifecycle hooks.

tip

AppendChild is a native Dom manipulation that inserts nodes and removes them from the source Dom node without creating new nodes.

3.3 Instruction encapsulation mode

Thanks a lot for pangkun’s reminding, let me find this interesting Dom transfer method.

Directive encapsulation is essentially the appendChild model, distilling Dom transfer operations into directives that can be called when needed.

Instead of going into the code, let’s talk about some of the interesting things that Xiaoju found while studying the instruction encapsulation model.

Hint: Start telling stories

In the small play will be right menu function after writing, smugly in front of fat kun show off the evil Dom transfer implementation.

Fat kun understated a sentence: iView source also has a Dom transfer method, you can take a look.

Inner frustration, small drama found the source of iView, along the code to find the following file, logic is quite concise, and no third party dependence.

Github.com/iview/iview…

The previous code was clean and neat, and allowed for the need to move the Dom to the specified node.

As She was about to copy the code for later use, she noticed two lines of comments at the top of the file.

// Thanks to: https://github.com/airyland/vux/blob/v2/src/directives/transfer-dom/index.js
// Thanks to: https://github.com/calebroseland/vue-dom-portal
Copy the code

Open the link on the first line and you’ll see another comment:

// Thanks to: https://github.com/calebroseland/vue-dom-portal
Copy the code

It’s like a Russian doll operation.

Although these three codes are not quite the same in naming and some details, the overall idea and implementation are surprisingly consistent.

As can be seen from the comments of Thanks to, the code copy path is very clear. Iview borrows from the transfer-DOM implementation of VUX, and vUX transfer-DOM is modified based on vue-dom-portal.

That is, the root of this code is in the Vue-dom-portal repository.

So here’s the question:

Since the roots are invue-dom-portal, why did the borrowing partner rename ittransfer-dom?

The answer to this question was soon found in the vue-dom-Portal documentation.

Similar to vue-transfer-dom, but updated for [email protected].

Because at [email protected], someone else wrote a directive called vue-transfer-dom and published it to Npm.

However, the original authors did not develop a version that supports [email protected], so the new code is named vue-dom-portal to differentiate it.

Vux and iView have been upgraded from [email protected], so the old name is kept, but the new code is used.

Hint: End of story

There are four warehouses mentioned in the story.

The earliest version of vue-Transfer-dom is older and no longer usable.

The other two pieces of code with “Thanks to” are just snippets. In case of matryoshka dolls, you are not advised to copy “Thanks to”.

So if you want to transfer the Dom using an command-wrapped schema, you can use vue-dom-portal directly.

4. Is Dom transfer recommended in the project?

Use at your own risk! No tests have been written, but it seems to be working.

The above quote is taken from the vue-dom-Portal documentation.

No matter which method you use to transfer the Dom, Vue modularity is not the way it was designed.

You need to ensure logical integrity and avoid memory leaks due to data sharing.

Therefore, manual Dom transfer is not recommended for normal project development, unless you encounter scenarios like those described in this article.

Hope this article stumped you.



Author: Dramatis personae

Originally published by: Xiaoju Inn

Link: bh-lay.com/blog/ottsc8…