Vue Component System

Since vue.js is a framework, you can't just accomplish the tasks of the Data Template Engine; it also provides the functionality of page layouts.This article details the vue.js component system, a powerful tool for page layout using vue.js.

Every new technology is created to solve specific problems.Components appear to solve a series of problems such as page layout.There are two kinds of components in vue, global component and local component.

I. Registration of Global Components

After creating a global component with Vue.component(), we can use it as a custom element in a Vue root instance created with new Vue.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="../statics/vue.min.js"></script>
</head>
<body>
  <div id="app">
    <!--Step 2, Use-->
    <global_component></global_component>
  </div>
  <script>
    // Step 1, Register
    Vue.component("global_component", {
      template: `
        <div>
            <h2>Hello Vue</h2>
        </div>
      `
    });

    new Vue({
      el: "#app",
    });
  </script>
</body>
</html>

1. Component parameters

Because components are reusable Vue instances, they receive the same options as new Vue, such as data, computed, watch, methods, life cycle hooks, and so on.The only exception is a root instance-specific option like el.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="../statics/vue.min.js"></script>
</head>
<body>
  <div id="app">
    <!--Step 2, Use-->
    <global_component></global_component>
  </div>
  <script>
    // Step 1, Register
    Vue.component("global_component", {
      data: function () {
        return {
          count: 0
        }
      },
      template: `<button v-on:click="count++">You clicked me {{ count }} times.</button>`
    });

    new Vue({
      el: "#app",
    });
    
  </script>
</body>
</html>

2. Component Reuse

Each instance maintains its own separate piece of data.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="../statics/vue.min.js"></script>
</head>
<body>
  <div id="app">
    <!--Step 2, Use-->
    <global_component></global_component>
    <global_component></global_component>
    <global_component></global_component>
  </div>
  <script>
    // Step 1, Register
    Vue.component("global_component", {
      data: function () {
        return {
          count: 0
        }
      },
      template: `<button v-on:click="count++">You clicked me {{ count }} times.</button>`
    });

    new Vue({
      el: "#app",
    });
    
  </script>
</body>
</html>

Note that each component maintains its count independently when the button is clicked.Because every time you use a component, a new instance of it is created.

3. Data must be a function

data must be a function, so each instance can maintain a separate copy of the returned object or be written as follows

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="../statics/vue.min.js"></script>
</head>
<body>
  <div id="app">
    <!--Step 2, Use-->
    <global_component></global_component>
    <global_component></global_component>
    <global_component></global_component>
  </div>
  <script>
    // Step 1, Register
    Vue.component("global_component", {
      data(){
        return {
          count: 0
        }
      },
      template: `<button v-on:click="count++">You clicked me {{ count }} times.</button>`
    });

    new Vue({
      el: "#app",
    });

  </script>
</body>
</html>


2. Registration of Local Components

Global registration is often not ideal.For example, if you use a build system like webpack, registering all components globally means that even if you no longer use a component, it will still be included in your final build.This has resulted in an unnecessary increase in JavaScript downloads by users.

Global components always exist, and unless the program ends, the program takes up more space and consumes more performance if the components get larger and larger.


1. First way to use local components

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="../statics/vue.min.js"></script>
</head>
<body>
  <div id="component-demo">
    <!--Finally use it in the root element-->
    <!--The first way to use it will be div Render in DOM-->
    <my-header></my-header>
  </div>
  <script>
    // Defining a local component is a variable, and it is an object type
    // Properties are the same as global components
    let Header = {
      template: `
        <button @click="count++">{{ count }}</button>
      `,
      data() {
        return {
          count: 0
        }
      }
    };

    new Vue({
      el: "#component-demo",
      // Second, you need to use it in the root instance
      components: {
        'my-header': Header
      }
    });
  </script>
</body>
</html>

2. Second way of using local components

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="../statics/vue.min.js"></script>
</head>
<body>
  <div id="component-demo">
  </div>
  <script>
    // Defining a local component is a variable, and it is an object type
    // Properties are the same as global components
    let Header = {
      template: `
        <button @click="count++">{{ count }}</button>
      `,
      data() {
        return {
          count: 0
        }
      }
    };

    new Vue({
      el: "#component-demo",
      // The second way of using div s, which do not render into the DOM, is to use template s as the root element
      template: `<my-header></my-header>`,
      // The second step is to use it in the root instance
      components: {
        'my-header': Header
      }
    });
  </script>
</body>
</html>

For each property in the components object, its property name is the name of the custom element, and its property value is the component's option object.

3. Use subcomponents in local components

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="../statics/vue.min.js"></script>
  <style>
    body {
      margin: 0;
    }
    .box {
      width: 100%;
      height: 50px;
      background-color: #2aabd2;
    }

  </style>
</head>
<body>
  <div id="component-demo">
  </div>
  <script>
    // Defining a local component is a variable, and it is an object type
    // This object has the same properties as the global component (except the el property)

    let Fcontent = {
      template: `
        <div>
          <span>This is the headline</span>

        </div>
      `
    };

    let Header = {
      template: `
        <div v-bind:class='{box: isBox}'>
          <button @click="count++">{{ count }}</button>
          <first-content></first-content>
        </div>
      `,
      data() {
        return {
          count: 0,
          isBox: true
        }
      },
      components: {
        'first-content': Fcontent
      }
    };

    new Vue({
      el: "#component-demo",
      // The second way of using div s, which do not render into the DOM, is to use template s as the root element
      template: `<my-header></my-header>`,
      // The second step is to use it in the root instance
      components: {
        'my-header': Header
      }
    });
  </script>
</body>
</html>


3. Communication between parent and child components

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="../statics/vue.min.js"></script>
  <style>
    body {
      margin: 0;
    }
    .box {
      width: 100%;
      height: 50px;
      background-color: #2aabd2;
    }

  </style>
</head>
<body>
  <div id="component-demo">
  </div>
  <script>
    // Defining a local component is a variable, and it is an object type
    // Properties are the same as global components

    let Fcontent = {
      template: `
        <div>
          <span>This is the headline</span>
          {{ fdata }}
        </div>
      `,
      props: ['fdata']
    };

    let Header = {
      template: `
        <div v-bind:class='{box: isBox}'>
          <button @click="count++">{{ count }}</button>
          <first-content :fdata="fathData"></first-content>
        </div>
      `,
      data() {
        return {
          count: 0,
          isBox: true,
          fathData: "I'm your dad~~~"
        }
      },
      components: {
        'first-content': Fcontent
      }
    };

    new Vue({
      el: "#component-demo",
      // The second way of using div s, which do not render into the DOM, is to use template s as the root element
      template: `<my-header></my-header>`,
      // The second step is to use it in the root instance
      components: {
        'my-header': Header
      }
    });
  </script>
</body>
</html>


4. Communication between son and father

The parent component listens for a custom event while mounted.

After binding a click event to a child component, trigger an event on the parent component via the built-in method $emit, which is a custom event that the parent component listens for.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="../statics/vue.min.js"></script>
</head>
<body>

  <div id="app">


  </div>

  <script>
    let myFooter = {
      template: `
            <div>
              <h1>I am a son</h1>
              <button v-on:click="changeFatherSize">Click to modify Dad's font</button>
            </div>
          `,
      methods: {
        changeFatherSize: function () {
          this.$emit('change-font', 1);
        }
      },
    };

    let myHeader = {
      template: `
            <div>
              <my-footer v-on:change-font="changeSize"></my-footer>
              <span :style="{ fontSize: fontSize + 'px'}">I'm daddy</span>
            </div>
        `,
      data(){
        return {
          fontSize: 26,
        }
      },
      methods: {
        changeSize: function (value) {
          console.log(value);
          this.fontSize += value;
        }
      },
      components: {
        'my-footer': myFooter
      }
    };

    let App = {
      template: `
        <div>
          <my-header></my-header>
        </div>
      `,
      components: {
        'my-header': myHeader,
      },
    };

    new Vue({
      el: "#app",
      template: `<App></App>`,
      components: {
        App
      }
    })
  </script>

</body>
</html>


5. Communication between parallel components

Parallel components can communicate through an intermediate Vue instance.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../statics/vue.min.js"></script>
</head>
<body>
    <div id="app">
            <com-main></com-main>
    </div>

    <script>

        let bus = new Vue();

        let dogfa = {
            template: `
                <div>
                    <button @click="dogfaClick">Click to djb apologize</button>
                </div>
            `,
            methods: {
                dogfaClick: function () {

                    bus.$emit("dogfa_apo", "Forgive me, please take great care of yourself~~~");
                }
            },
        };

        let djb = {
            template: `
                <div v-show="isShow">Forgive you~~~</div>
            `,
            mounted () {
                bus.$on("dogfa_apo", (dogfasay)=> {
                    if ( dogfasay ) {
                        console.log("Forgive you~~~");
                    }
                });
            },
            data () {
                return {
                    isShow: false
                };
            }
        };

        let App = {
            template: `
                <div id="app">
                    <dogfa></dogfa>
                    <djb></djb>
                </div>
            `,
            components: {
                dogfa,
                djb
            }
        };

        new Vue({
            el: "#app",
            template: '<App></App>',
            components: {
                App
            },
        })

    </script>

</body>
</html>


6. Mixing

Mixing improves code reuse

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="../statics/vue.min.js"></script>
</head>
<body>

  <div id="app">


  </div>

  <script>
    let mixins = {
      methods: {
        show: function (name) {
          console.log(`${name} is here`);
        },
        hide: function (name) {
          console.log(`${name} is here`);
        }
      }
    };

    let myAlex = {
      template: `
        <div>
          <button @click="show('alex')">Click me to show alex</button>
          <button @click="hide('alex')">Hide me alex</button>
        </div>
      `,
      mixins: [mixins]
    };

    let myPeiQi = {
      template: `
        <div>
          <button @click="show('peiqi')">Click me to show peiqi</button>
          <button @click="hide('peiqi')">Hide me peiqi</button>
        </div>
      `,
      mixins: [mixins],
    };

    let App = {
      template: `
        <div>
          <my-alex></my-alex>
          <my-peiqi></my-peiqi>
        </div>
      `,
      components: {
        'my-alex': myAlex,
        'my-peiqi': myPeiQi,
      },
    };

    new Vue({
      el: "#app",
      template: `<App></App>`,
      components: {
        App
      }
    })
  </script>

</body>
</html>


7. Slots

Sometimes we need to pass some data to the component, so we can use slots

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .nav-link {
            width: 100px;
            height: 100px;
            background-color: #2aabd2;
            float: left;
            margin-left: 5px;
            text-align: center;
            line-height: 100px;
        }

    </style>
    <script src="../statics/vue.js"></script>
</head>
<body>
    <div id="app01">
        <com-content>Sign in</com-content>
        <com-content>register</com-content>
        <com-content>Hottest</com-content>
        <com-content>Paragraph</com-content>
        <com-content>42 area</com-content>
        <com-content>picture</com-content>
    </div>

    <script>
        Vue.component('com-content', {
          template: `
            <div class="nav-link">
              <slot></slot>
            </div>
          `
        });

        new Vue({
            el: "#app01",
        })
    </script>

</body>
</html>


8. Named slots

Operation uses component reuse, if we write different pages within the same component?At this point, we need multiple slots and names for different content.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .nav-link {
            width: 100px;
            height: 100px;
            background-color: #2aabd2;
            float: left;
            margin-left: 5px;
            text-align: center;
            line-height: 100px;
        }
    </style>
    <script src="../statics/vue.js"></script>
</head>
<body>
    <div id="app01">
        <base-layout>
            <template slot="header">
                <h1>This is the title bar</h1>
            </template>
            <template>
                <h2>This is the content</h2>
            </template>
            <template slot="footer">
                <h3>This is the footer</h3>
            </template>
        </base-layout>
    </div>

    <script>
        let baseLayout = {
            template: `
                <div class="container">
                  <header>
                      <slot name="header"></slot>
                  </header>
                  <main><slot></slot></main>
                  <footer>
                      <slot name="footer"></slot>
                  </footer>
                </div>
            `
        };

        new Vue({
            el: "#app01",
            components: {
                "base-layout": baseLayout
            }

        })
    </script>

</body>
</html>

We can still keep an unnamed slot, which is the default slot, meaning it serves as a uniform outlet for all unmatched slots.


9. Using v-model on components

Custom events can also be used to create custom input components that support v-model.Remember:

`<input v-model="searchText">`

Equivalent to:

<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

When used on components, the v-model looks like this:

<custom-input
  v-bind:value="searchText"
  v-on:input="searchText = $event"
></custom-input>

In order for it to work properly, <input>within this component must:

Bind its value attribute to a prop named value
 When its input event is triggered, throw a new value through a custom input event

After writing the code, it looks like this:

Vue.component('custom-input', {
  props: ['value'],
  template: `
    <input
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
  `
})

Now the v-model should work perfectly on this component:

<custom-input v-model="searchText"></custom-input>

The following is an example of using v-model in a component:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="../statics/vue.min.js"></script>
</head>
<body>
  <div id="app">
  </div>

  <script>
    let Model = {
      template: `
        <div>
            <input
              v-bind:value="value"
              v-on:input="$emit('input', $event.target.value)"
            />
            <h1>{{ value }}</h1>
      `,
      props: ['value']
    };

    let App = {
      template: `
        <div>
            <custom-input v-model="searchText"></custom-input>
      `,
      components: {
        'custom-input': Model,
      },
      data(){
        return {
          searchText: "",
        }
      }
    };

    new Vue({
      el: "#app",
      template: `<App></App>`,
      components: {
        App,
      }
    })
  </script>
</body>
</html>


10. Cautions for using components

1. Note 1: Single root element

When building a component for a content page, our component may contain multiple HTML tags.

<h1>Hello World</h1>
<h2>Hello Vue</h2>

However, if you try to write this in a template, Vue displays an error and explains every component must have a single root element (each component must have only one root element).You can fix this problem by wrapping the contents of a template within a parent element, for example:

<div>
  <h1>Hello World</h1>
  <h2>Hello Vue</h2>
</div>


2. Note 2: Parse special HTML elements

Some HTML elements, such as <ul>, <ol>, <table>, and <select>, have strict restrictions on which elements can appear within them.Some elements, such as <li>, <tr>, and <option>, can only appear inside certain other elements.

This can cause some problems when we use these constrained elements.For example:

<table>
  <blog-post-row></blog-post-row>
</table>

This custom component <blog-post-row>is promoted externally as invalid content and results in an error in the final rendering result.Fortunately, this particular is feature gives us a workaround:

<table>
  <tr is="blog-post-row"></tr>
</table>

It is important to note that this limitation does not exist if we use templates from the following sources:

String (for example: template:'...')
Single File Component (.vue)
<script type="text/x-template">




Tags: Javascript Vue Webpack Attribute

Posted on Wed, 04 Sep 2019 09:51:51 -0700 by CMellor