The introduction

It is a common requirement for pages to refer to popup components. If popup components are forced into the page, although it works functionally, there is no decoupling between the components and the page, which is not conducive to later maintenance and function expansion. Here’s an example to illustrate the downside.

< template > < div > < button @ click = "openModal ()" > click < / button > < Modal: is_open = "is_open @ close" = "close ()" / > < / div > </template> <script> import Modal from ".. /components/Modal/Modal"; Export default {components: {Modal,}, data(){return {is_open:false}}, methods: {openModal() {// display pop-up this.is_open = true; }, close(){this.is_open = false; this.is_open = false; }}}; </script>Copy the code

Modal is an externally introduced popup component, and the parent controls the popup hiding and showing through IS_open. A careful analysis of the above structure presents the following problems.

  • Modal components are hardcoded to force on the parent componentcomponentsWhich is registered and rendered in the parent component’s template<Modal />Imagine that a popup component needs to be written once in the parent component, and each of the five popups needs to be written five times in the parent template. The popup component should be decoupled from the parent component. It should not be written in the parent’s template.
  • The parent component needs to set a separate stateis_openTo control the pop-up display and hide, if the parent component needs to introduce more than one pop-up, it is bound to define more than one state to control the pop-up.

In order to decouple the popup from the parent, the ideal way is to use the idea of functional programming, where you only need to call a function within the parent to make the popup appear. Let’s see how to do that.

Pop-up component processing

Let’s implement a very simple but powerful utility function that encapsulates the popup component. If the parent component needs to use which popup component to call the function directly can be easily shown or hidden.

implementation

import Vue from 'vue';
export const createModal = (Component, props) => {
  const vm = new Vue({
    render: (h) =>
      h(Component, {
        props,
      }),
  }).$mount();
  document.body.appendChild(vm.$el);
  const ele = vm.$children[0];
  ele.destroy = function() {
    vm.$el.remove();
    ele.$destroy();
    vm.$destroy();
  };
  return ele;
};
Copy the code
  • ComponentIs the popup component called by the parent component and passed in here as an argument.propsIs ultimately passed to the inside of the popup componentprops
  • newaVueInstance,renderIn the function corresponding to the property,hIs used to turn popup components into virtual DOM
  • $mountBe sure to call, which converts the virtual DOM into real DOM elements
  • vm.$elIs the component that corresponds to the popup that comes inComponentTo render the real DOM, mount it under the body, and the page will display a pop-up box
  • It is not enough to show the popup, we also need to create a destroy method for the popup componentdestroy, includingvm.$children[0]The corresponding is the popup componentvueInstance, which can be calleddestroyFinally, the instance is returned for an external call through which the properties and methods inside the popup component can be called.

application

As a test Demo, the component structure of the pop-up box is as follows. The content of the template is very simple. Render a header title and content. Define two methods show() and hide() to manipulate the is_open state to control the display and hide of the popup.

<template> <div class="modal" v-if="is_open"> <div class="content"> <p class="close" @click="hide()">close</p> <p class="title">{{ title }}</p> <div>{{ content }}</div> </div> </div> </template> <script> export default { props: ["title", "content"], data() { return { is_open: false, }; }, methods: { show() { this.is_open = true; }, hide() { this.is_open = false; ,}}}; <style lang="scss" scoped> .modal { position: fixed; top: 0; left: 0; right: 0; bottom: 0; Background-color: rgba(0, 0, 0, 0.6); .content { width: 200px; height: 200px; background-color: #fff; margin: 0px auto; margin-top: 200px; text-align: center; font-size: 14px; color: #333; padding: 5px; .title { margin-bottom: 20px; font-size: 16px; } .close { text-align: right; } } } </style>Copy the code

Page components

<template> <div class="test-v2"> < button@click ="openModal()"> Click </button> </div> </template> <script> import Modal from ".. /.. /components/Modal/Modal"; import { createModal } from ".. /.. /util/Modal"; Export default {methods: {openModal() {this.ele = createModal(Modal, {title: "popup ", content:" content ",}); this.ele.show(); }}};Copy the code

The page parent component can obtain the Modal instance this.ele of the popup component by calling the createModal method. This.ele gives you access to all the properties and methods inside the popup component, including show() and hide().

  • After the above modification, the decoupling between the popup component and the parent component is realized. Pop-up components do not need to be registered in the parent component and rendered within the template.
  • If the parent component needs to pass data to the popup component, you can usecreateModalThe second argument object, which ends up withpropsIs injected into the interior of the pop-up component.
  • show()andhide()Methods are defined inside the popup box and can be called directly by the parent component to control their display and hiding. In addition to the page destruction to call oncethis.ele.destroy()To prevent memory leaks.

It is clear from the final DOM structure diagram that the popup is mounted below the body, not inside the page component. This makes it easier and more convenient to define some styles related to CSS positioning in the pop-up box, without being affected and interfered with by the page components.

extension

There are many other things that can be done on top of this, such as the message prompt.

The message prompt box is also a pop-up box. As a best practice, simply write a line of code Alert(“Hello World “) and the page will immediately pop up with a Hello World message. The effect is as follows.

implementation

The parent page structure looks like this. Call the Alert() function and the page displays a prompt box.

<template> <div class="test-v2"> <button @click="alert()">Alert</button> </div> </template> <script> import { Alert } from ".. /.. /util/Modal"; export default { methods: { alert() { Alert("Hello world"); ,}}}; </script>Copy the code

The Alert function is implemented as follows.

const alert_array = []; Export const Alert = (MSG, duration = 3000) => {let top = 100; If (alert_array. Length > 0) {const index = alert_array. Length; top = top + index * 50; } const ele = createModal(AlertComponent, { title: msg, top, }); alert_array.push(ele); const timer = setTimeout(() => { clearTimeout(timer); const index = alert_array.indexOf(ele); index ! == -1 && alert_array.splice(index, 1); ele.destroy(); }, duration); };Copy the code
  • AlertComponentIs a custom disappear prompt component (to be introduced), calledcreateModal()Get an instance of each prompt box stored in an arrayalert_arrayIn the.
  • Click the button once to bring up a message box, click the button a second time, the second message box should appear below the first box, so according to the arrayalert_arrayDynamically calculates the absolute positioning top value, which is passed as an argument when creating the popup instance.
  • Timer control Removes the pop-up box after 3 seconds by default.

AlertComponent The contents of the AlertComponent are as follows: Top_value = “this.top-30” > < p style = “padding-top: 0px; padding-top: 0px; padding-top: 0px;

<template> <div class="alert-component" :style="{ top: `${top_value}px`, opacity: opacity }" > {{ title }} </div> </template> <script> export default { props: ["title", "top"], data() { return { top_value: this.top - 30, opacity: 0, }; }, mounted() { const timer = setTimeout(() => { clearTimeout(timer); this.top_value = this.top; this.opacity = 1; }); }}; </script> <style> .alert-component { height: 20px; border-radius: 4px; position: absolute; min-width: 300px; left: 50%; transform: translateX(-50%); background-color: #f0f9eb; color: #67c23a; align-items: center; padding: 10px 16px; The transition: all 0.25 s linear; opacity: 0; } </style>Copy the code

At the end

With the createModal tool function, not only the message prompt box, but also the message confirmation box, dynamic form mode box can be further encapsulated to simplify processing. When the pop-up box is decoupled from the page, the overall code logic becomes clearer, which is a huge benefit for later maintenance and extension.