Vue component encapsulation

1. Why package components (component development)

The benefits of component development (encapsulation components)


The benefits are obvious, which can increase code reusability and flexibility, thereby improving development efficiency. Imagine if a pop-up box can be used on many pages in a project, if you write a set of pop-up box structure style and corresponding js logic on each page. In this case, the development efficiency will be greatly reduced; of course, there are many excellent component libraries now. But we are only familiar with the encapsulation of components through this case

Three skill points are currently used in component encapsulation:


1 Passing values ​​from parent to child
2. Passing values ​​from child to parent
3. Slot technology (introduced at the end of the article)
In addition, there are some trivial knowledge points in this article: The property modifier .sync allows our child components to modify the values ​​​​passed by the parent component Attribute
Time Modifier.self .stop
The built-in animation tag in vue <transition>
The parent component modifies the data in the child component

Two, dialog component structure construction


The dialog box has an animation effect as a whole. The animation effect of vue uses transition to wrap the elements that need to be animated. Then this element will automatically add some class names when it is displayed/hidden. For this example, see the follow-up code for details.

Requirements :

●The pop-up box pops up or closes to set the transition animation.
●Use the default slot as the main content area.
●Use the named slot to specify the content of the head and bottom of the pop-up frame.
●Open or close the background of the pop-up frame
mask layer. The layer controls whether the bullet box is closed or not
. Whether to display the closed cross icon
. Customize the title of the bullet box.

Analysis :
The dialog box is divided into three parts:
1. Head: the left side is the title, using the named slot title to occupy the place, and the right side is the button/icon (closed)
2. The main content, using the unnamed slot to occupy the place
3 , Bottom: generally some operations, use the named slot footer to occupy the place, usually the content is the cancel/confirm button

Parameters that need to be passed in:
title: header title
width: dialog width (I think it can also be used in the parent level deep specified)
top: the distance from the top of the dialog box
to visible: the display/hide of the dialog box
 

Specific implementation code:
1. Create a new Dialog.vue file in components under the src root directory

<template> 
  <!-- Open the animation of the pop-up box --> 
  <!-- Vue's built-in animation tag will form 6 styles according to the name attribute. The 
    animation enters three states 
    dialog-fade-enter ready to enter 
    dialog-fade -enter-active In the process of entering 
    dialog-fade-enter-to The animation enters the end 

    dialog-fade-leave The animation is ready to leave 
     dialog-fade-leave-active The animation is in the process of leaving 
      dialog-fade-leave-to The animation leaves complete 
    --> 
  <transition name="dialog-fade"> 
    <!-- translucent shadow of div for the entire screen--> 
    <div 
      class="dialogBox" 
      :class="{ isShowMask: mask == true }" 
      v-show="isShowDialog " 
      @click.self="clickMaskCloseFn" 
    > 
      <!-- popup box --> 
      <div class="dialogBoxContent" @click.stop> 
        <div class="headhead">
          <!-- The default value of the named slot with the slot: This can be written so that if there is a passed title, the passed title will be used. 
          If there is a passed slot, the slot will prevail --> 
          < slot name="header"> 
            <span>{ 
   
   { title }}</span> 
          </slot> 
          <i class="el-icon-close" @click="close" v-show="showCloseIcon">X < /i> 
        </div> 
        <div class="bodybody"> 
          <!-- We use the default slot in the content area--> 
          <slot></slot> 
        </div> 
        <div class="footfoot"> 
          <! -- Use named slots at the bottom --> 
          <slot name="footer"></slot> 
        </div>
      </div>
    </div>
  </transition>
</template>
export default {

<script> 
  },
  name: 'dialogComponent', 
  props: { 
    // Control whether to show or hide the dialog 
    isShowDialog: { 
      type: Boolean, 
      default: false 
    }, 
    // The title value passed from the parent component 
    title: { 
      type: String, 
      default: '' 
    }, 
    // Whether to display the small close icon 
    showCloseIcon: { 
      type: Boolean, 
      default: true 
    }, 
    // Whether to enable the background mask layer 
    mask: { 
      type: Boolean, 
      default: true 
    }, 
    // Whether to click the mask layer mask to close Popup box 
    clickMaskClose: { 
      type: Boolean, 
      default: false 
    } 
  data () { 
    return {} 
  }, 
  methods: { 
    // close the popup box 
    close () { 
      this.$emit('update:isShowDialog', false) 
    }, 
    // click the mask layer to close the popup box 
    clickMaskCloseFn () { 
      if (this.clickMaskClose === true) { 
        this.$emit('update:isShowDialog', false) 
      } else { 
        /* Here we need to control the bubbling event. Note that the tenth line uses @click.stop 
           to not control the bubbling , clicking the content area will also cause the popup box to close*/ } 
      } 
    } 
  } 
</script> 
< 

style lang="scss" scoped> 
.dialogBox { 
  width: 100%; 
  height: 100%; 
  position: fixed; 
  top: 0; 
  left: 0; 
  display: flex;
  justify-content: center;
  align-items: center;
  .dialogBoxContent {
    width: 500px;
    height: 220px;
    border: 2px solid #e9e9e9;
    border-radius: 20px;
    background-color: #fff;
    .headhead {
      width: 100%;
      height: 60px;
      line-height: 60px;
      border-bottom: 1px solid #e9e9e9;
      box-sizing: border-box;
      padding: 20px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      span {
        font-size: 24px;
      }
      i {
        font-size: 24px;
        cursor: pointer;
      }
    }
    .bodybody {
      width: 100%;
      height: calc(100% - 120px);
    }
    .footfoot {
      width: 100%;
      height: 60px;
      line-height: 60px;
      box-sizing: border-box;
      border-top: 1px solid #e9e9e9;
      padding: 0 20px;
      .el-button {
        margin-left: 12px;
      }
    }
  }
}
.isShowMask {
  background-color: rgba(0, 0, 0, 0.3);
}
.dialog-fade-enter,
.dialog-fade-leave-to {
  opacity: 0;
}
.dialog-fade-enter-active,
.dialog-fade-leave-active { 
  transition: opacity 0.3s; 
// opacity: 1;
}

// You can also define animation like this 
// enter animation 
// .dialog-fade-enter-active { 
// animation: dialog-fade-in 0.4s; 
// } 
// // leave animation 
// .dialog-fade -leave-active { 
// animation: dialog-fade-out 0.4s; 
// } 

// @keyframes dialog-fade-in { 
// 0% { 
// transform: translate3d(0, -20px, 0); 
/ / opacity: 0; 
// } 
// 100% { 
// transform: translate3d(0, 0, 0); 
// opacity: 1; 
// } 
// } 

// @keyframes dialog-fade-out { 
// 0% { 
// transform: translate3d(0, 0, 0); 
// } 
// 100% { 
// transform: translate3d(0, -20px, 0);
//     opacity: 0;
//   }
// }
</style>


2 Globally registered components (main.js)

import NewDialog from '@/components/Dialog' 
Vue.component('myDialog', NewDialog) 


3. Calls in business components
<!-- 
      sync: event modifier, is a grammatical sugar writing method, which realizes that the child component modifies the props passed in by the parent component parent 
      : visible.sync="visible" 
      child: this.$emit("update:visible", false) 
    --> 
    <my-dialog 
      :isShowDialog.sync="isShowDialog" 
      title="Set title" 
      :showCloseIcon="true" 
      :mask="true" 
      :clickMaskClose="true" 
    > 
      <!-- The named plug-in to be connected with the component Slot correspondence --> 
      <template #header> Named slot</template> 
      <template> Default slot</template> 
      <!-- To correspond to the slot of the subcomponent --> 
      <template #footer> 
        <van- button size="small" @click="isShowDialog = false">Cancel</van-button> 
      > 取消</van-button>
        <van-button type="primary" size="small" @click="isShowDialog = false"
          Confirm</van-button> 
        </template> 
    </my-dialog> 
    <van-button @click="isShowDialog = true" type="primary">modal box</van-button>

3. Review and summary of knowledge points

why there are slots


The birth of the slot api comes from the needs of Vue data transfer, because usually we use props to transfer data from parent to child, and the transferred data are all "js-type data" such as objects, arrays, and strings. What should we do when we want to pass a large amount of fragment data of type html?
There is such a demand, so the slot technology came into being.
In componentized programming, css does not need to be passed, because we can use deep scope selectors, such as /deep/, to select the dom element in the child component in the parent component to set the style

Classification of slots


●Default slot (also known as: ordinary slot, single slot, anonymous slot. That is, without a name, no need to set the name attribute <slot></slot>)
●Named slot (with a name< slot name="footer"></slot>, which has the name attribute)
Scope slot (this is a slightly advanced usage of the slot, as far as the case is concerned, it is useful in the table in the Ele.me UI To)
slots can be seen in components encapsulated in Ele.me UI or antD. Take el-dialog as an example, which uses default slots and named slots. It can be said that the socket technology is basically used in UI components. If you are free, you can go and see the source code of Ele.me UI’s package components
. There are a lot of packages/components, find the packages under element-ui, which are all packaged component
scope slots by Ele.me. This article will not be used for the time being, so press the button first, and then write a separate article about the functions Domain Slot Articles

Anonymous (default) slot usage

Subcomponents place anonymous slots

<template> 
  <div class="box"> 
    <h1>I am a subcomponent</h1> 
<!-- The first step, in the subcomponent, find a place to insert a slot, because the slot can hold things, and Because the child component will be introduced into the parent component 
, the specific content of the slot inserted by the child component must be loaded by the parent component. The parent component loads the html fragment, and the 
html fragment can be obtained in the child component. So: the slot realizes the effect of the parent component passing data to the child component 
  --> 
  <slot></slot> 
<!-- the child component writes such a slot tag to receive the html fragment passed by the parent component. If you don't write it, the parent component will be passed in vain, that is, 
DOM will not be generated, and it will not be rendered 
  --> 
  </div> 
</template> 

<script> 
  export default { 
name: "DemoChildslot", 
}; 
</script> 

< style lang="less" scoped> 
  .box { 
    width: 200px; 
    height: 200px; 
    background-color: #baf;

Parent component passes HTML using anonymous slot

<template> 
  <div id="app"> 
    <!-- The second step 
            is to use subcomponents and write code in the middle of the subcomponent tags to realize the data transfer of the slot. --> 
    <child-slot> 
      <i>I passed it from the parent component</i> 
      <!-- 
        It can also be written like this, because the default is slot="default", but it is generally not written like this, it is a little troublesome 
        <i slot="default">I was passed by the parent component</i> 
      --> 
    </child-slot> 
  </div> 
</template> 

<script> 
// Introduce the child component 
import childSlot from "./childSlot" ; 
export default { 
  components: { 
    childSlot, // register child component 
  }, 
}; 
</script> 

<style lang="less" scoped>
}
</style>

renderings

Use of named slots
For example, if we want to package a bullet box component, after the bullet box pops up, in the content area of ​​the bullet box, there are the content area of ​​the head of the bullet box, the main content area of ​​the bullet box, and the bottom content area of ​​the bullet box. We can use anonymous slots in the main content area of ​​the bullet box. If the bullet box needs to specify the content of the header and the bottom content of the bullet box, you can specify the specific content through the named slot. The structure code is as follows:

Subcomponents place named slots

<template> 
  <div class="box"> 
    <h1>Poll box</h1> 
    <div class="dialogHead"> 
        <!-- A slot with a name, the function is to specify the slot position --> 
        <slot name ="headhead"></slot> 
    </div> 
    <div class="dialigMain"> 
        <slot></slot> 
    </div> 
    <div class="dialogFoot"> 
        <!-- a slot with a name, The function is to specify the slot position --> 
        <slot name="footfoot"></slot> 
    </div> 
  </div> 
</template> 

<script> 
export default { 
  name: "DemoChildslot", 
}; 
</script >

<style lang="less" scoped>
.box {
  width: 170px;
  height: 200px;
  background-color: #baf;
}
</style>

Use named slots to pass HTML in the parent component

<template> 
  <div id="app"> 
    <child-slot> 
      <!-- Writing method 1: The slot attribute is written on the label. 
              When the named slot passes data in the parent component, the position does not matter, because the named slot is passed The data will 
              find the named slot in the corresponding subcomponent according to the name of the named slot. 
       --> 
      <!-- <p slot="footfoot">Bottom bottom</p> 
      <i>I am passed by the parent component</i> 
      <p slot="headhead">head head</p > --> 

      <!-- Writing method 2: The slot attribute is written on the template tag, and the template tag will not be rendered into DOM --> 
        <template slot="headhead"> 
          <p>Template tag head</p> 
        </ template> 
        <i>I was passed by the parent component</i> 
        <template slot="footfoot"> 
          <p>Bottom of the template tag</p>

import childSlot from "./childSlot";
export default {
  components: {
    childSlot, // 注册子组件
  },
};
</script>

<style lang="less" scoped>
#app {
  width: 100%;
  min-height: 100vh;
  box-sizing: border-box;
  padding: 50px;
}
</style>

renderings

Guess you like

Origin blog.csdn.net/asfasfaf122/article/details/128787963