In the actual development of a project, I used the Element-UI library, because the default color of the modal box in this library is only white, and the project needs other colors. After trying to modify the color of the original UI, I found that the color would flash during the initial rendering, so I gave up this method. And began to use JS to operate DOM directly with setTimeout function, to achieve the final effect, but we all know that JS to operate DOM is very performance loss, and the use of timer may cause some problems such as memory leakage.

Although the effect has been shown, but due to their own can not ignore the “cost” mentioned above, so I started to implement a simple version of the modal box, in which also encountered some problems ignored before, the following according to the code.

Let’s first copy the Github address of this modal box component.

First, define a component

The two main features of vue.js are two-way data binding and component systems

So here are some component basics:

Vue componentsCore options:

  • Template: A template declares a mapping between the data and the DOM that is ultimately presented to the user.

  • Initial data: the initial data state of a component. For reusable components, this is usually a private state.

  • 3. Accepted external parameters (props) : Parameters are used to transfer and share data between components.

  • 4. Methods: Changes to data are generally performed in the component’s methods.

  • Lifecycle hooks: A component fires multiple lifecycle hook functions. The latest version 2.0 has significantly changed the names of lifecycle hooks.

  • 6. Private Resources (assets) : In vue. js, user-defined instructions, filters, components, etc., are collectively referred to as resources. A component can declare its own private resources. A private resource can be invoked only by this component and its children. And so on.

How do YOU write a reusable component?

Components are developed not just to decouple code and make it independent of one part, but to reuse it.

The vue. js component API comes from three parts: prop, Event, and Slot:

  • Prop allows external environments to pass data to components, and vuEX can also be used to pass data in vuE-CLI projects.

  • The event allows a component to trigger an action for the external environment

  • Slot allows the external environment to insert content into a component’s view structure.

HTML section (Basic layout)

<template>
  <div class="dialog">
    <div class="mask"></div>
    <div class="dialog-content"> <! --> <h3 class="title">{{ modal.title }}</h3> <! -- Custom content --> <slot name="content"></slot> <! Button group --> <div class="btn-group">
        <div class="btn" @click="cancel">{{ modal.cancelButtonText }}</div>
        <div class="btn" @click="submit">{{ modal.confirmButtonText }}</div>
      </div>
    </div>
  </div>
</template>
Copy the code

The content section uses named slots, which allow the external environment to insert content into the component’s view structure (insert content can be anything, such as a table). Input box. Picture). This is no longer limited to what is defined in the component, making it easier to use and more manufacturable.

CSS section (Styles)

After the layout, to achieve the realization of this part of the content, we can first ignore the core part, first show the basic style, the following section of code using less, the purpose of using CSS is nothing more logical, more convenient.

Note: If your vue is installed using vuE-cli 3.x, you need to install less and less-loader yourself, and configure less-loader in webpack. If vue-CLI 3.x, you do not need to install name manually.

<style scoped lang="less">
.dialog {
  position: relative;
  .dialog-content {
    position: fixed;
    z-index: 2;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    box-sizing: border-box;
    background: white;
    border-radius: 5px;
    padding: 20px;
    min-height: 140px;
    .title {
      font-size: 16px;
      font-weight: 600;
      line-height: 30px;
    }
    .text {
      font-size: 14px;
      line-height: 30px;
      color: # 555;
    }
    .btn-group {
      display: flex;
      position: absolute;
      right: 0;
      bottom: 10px;
      .btn {
        padding: 10px 20px;
        font-size: 14px;
        &:last-child {
          color: #76d49b;
        }
      }
    }
  }
  .mask {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 1;
  }
}
Copy the code

The whole code above is mainly the basic layout, nothing more to say, in order to enable more readers to read more quickly, pick out two small parts for a brief explanation:

 left: 50%;
 top: 50%;
 transform: translate(-50%, -50%);
Copy the code

The purpose of this code is to center the modal box vertically and horizontally. If you don’t know, here I recommend an article that explains the basic layout commonly used, in great detail.

 z-index: 1;
Copy the code

This property specifies the Z-order of an element with a positioning attribute and its descendants. When elements overlap, the Z-order determines which element is displayed above the rest. Usually the larger z-index element overwrites the smaller one.

JS part

<script>
/* eslint-disable */
export default {
  name: "MyModal",
  props: {
    dialogOption: Object
  },
  data() {
    return {
      resolve: "",
      reject: "",
      promise: ""// Save the promise object; }, computed: {modal() {
      let option = this.dialogOption;
      return {
        title: option.title || "Tip",
        text: option.text,
        cancelButtonText: option.cancelButtonText
          ? option.cancelButtonText
          : "Cancel",
        confirmButtonText: option.confirmButtonText
          ? option.confirmButtonText
          : "Sure"}; }}, methods: {// make a promise complete statesubmit() { this.resolve(); }, // reject the promisecancel() { this.reject(); }, // Display confirm pop-up and create a Promise object to call to the parent componentconfirm() {
      this.promise = new Promise((resolve, reject) => {
        console.log(resolve, reject);
        this.resolve = resolve;
        this.reject = reject;
      });
      returnthis.promise; // Return a Promise object to call the parent component}}};Copy the code

Props is an object that receives a parameter passed by the parent component. The object contains the title and button name properties, and the purpose of this object is to make it easier to change these properties.

The emphasis is on the three methods in methods. Because part of the content of the parent component needs to be combined here, the author first explains the use of the parent component and then explains the functions of this part in detail.

Use of the parent component

<! -- Custom modal box component --> <my-modal :dialogOption="dialogOption" v-show="isShow" ref="dialog">
      <div slot="content"> <label> User name </label> <inputtype="text" placeholder="Please enter user name" v-model="username"> <br> <label> Password </label> <inputtype="password" placeholder="Please enter your password" v-model="password">
      </div>
    </my-modal>
    
    <script>
/* eslint-disable */
import MyModal from "./components/MyModal.vue";

export default {
  name: "app",
  components: {
    MyModal
  },
  data() {
    return {
      isShow: false,
      dialogOption: {
        title: "View details"
      },
      username: "",
      password: ""
    };
  },
  methods: {
    open() {
      this.isShow = true;
      this.$refs.dialog
        .confirm()
        .then(() => {
          this.isShow = false;
        })
        .catch(() => {
          this.isShow = false; }); }}}; </script>Copy the code

First look at this sentence

, dialogOption this property value is to pass the value for the subcomponent, very simple, V-show =”isShow” this is the control mode box show and hide. Ref =”dialog” gets the component instance.

Note: if ref is used on a normal DOM element, the reference refers to the DOM element. If on a child component, the reference points to the component instance.

So when we call the open method below, this.$ref.dialog gets an instance of the modal box component, which accesses the methods in that instance, and calls the cofirm in that instance because the cofirm in that component returns a Promise object, So you call its then and catch methods to catch resolve and reject, respectively. Resolve and reject are assigned to confirm (resolve and REJECT are assigned to confirm). Therefore, when the two attributes are used in submit and Cancel, You need to execute these two functions, implement your actual function, and finally close the modal box.

The above is the core of the modal box, carefully understand down is not difficult.

Here are some of the pitfalls I encountered in my implementation:

When controlling the mode box to show and hide, I use V-if in my mind. Let’s first say the difference between the two:

v-ifv-show

Thing in common:

Both v-if and V-show display DOM elements dynamically.

The difference between:

  • 1. Compilation process: V-IF is true conditional rendering because it ensures that event listeners and subcomponents within the conditional block are properly destroyed and rebuilt during the switch. V-show elements are always rendered and retained in the DOM. V-show simply toggles the element’s CSS attribute display.

  • 2. Compile conditions: V-if is lazy: if the condition is false during the initial render, nothing is done. The conditional block is not rendered until the condition is true for the first time. V-show Regardless of initial conditions, elements are always rendered and simply switched based on CSS.

  • 3. Performance consumption: V-IF has higher switching consumption. V-show has a higher initial render cost.

  • 4. Application Scenarios: V-IF is suitable for use when run-time conditions rarely change. V-show is suitable for frequent switching.

The worst part aside, using V-show here is a better choice for performance optimization, given the difference between the two.

In addition, if we use V-if, we will find that when we use ref to get the component instance, we will leave an empty object. The reason is that when we call open to open the modal box, the modal box component is not actually rendered to the DOM node, so ref can’t access the component instance. Instead, if we use v-if, regardless of whether we initially see the modal box component, it’s already rendered on the DOM node, so it doesn’t cause the problem that we just saw.

So, when we choose v-if or V-show, we consider their use and the potential for unpredictable errors in addition to performance.

Here, the implementation of the modal box component, the use of the problems encountered by the author have been explained above, I hope that the lovely people who read this article have harvest ~~~~