Today, I read the tutorial about components on Vue official website. I feel the content is quite much. Now, I will sort out the basic knowledge of components.


Basic use of components

Certified components

To register a component, you use the Vue.component() method, passing in the name of a custom component and then the configuration of that component.



 Vue.component('mycomponent', {template: This is a custom component ,
    data () {
      return {
        message: 'hello world'}}})Copy the code

As shown above, you have created a custom component that can then be used in the DOM element on which the Vue instance hangs.



 <div id="app">
    <mycomponent></mycomponent>
    <my-component></my-component>
</div>
<script>
  var app = new Vue({
    el: '#app'.data: {},components: {
      'my-component': {
        template: '
       
This is a local custom component that can only be used in the current Vue instance
,}}}) </script>Copy the code

Components created directly with Vue.component() are available to all Vue instances. You can also register components in a Vue instance that only you can use.



var app = new Vue({
    el: '#app'.data: {},components: {
      'my-component': {
        template: '
       
This is a local custom component that can only be used in the current Vue instance
,}}})Copy the code

Requirements for templates

Note: A component’s template can have only one root element. The following situation is not allowed.



template: '
       
This is a local custom component that can only be used in the current Vue instance with
.Copy the code

Data in a component must be a function

As you can see, the configuration passed in when registering a component is similar to creating a Vue instance, but there are differences, one of which is that the data property must be a function. This is because if you pass in an object, as in a Vue instance, the variables of the object type in JS actually hold references to the object, so when there are multiple such components, the data will be shared, causing changes in the data of one component to cause changes in the data of other components.

With a function that returns an object, a new object is created each time you use the component, so you don’t have to share data.

Parsing DOM templates

When using the DOM as a template (for example, mounting the EL option on an existing element), you are limited by HTML because Vue can’t get the template content until the browser parses and standardizes the HTML. In particular, elements like

    , < OL >,

    , and

Using these restricted elements in custom components can cause problems, such as:



<table>
  <my-row>.</my-row>
</table>Copy the code

The custom component

is considered invalid and therefore causes an error when rendering. The special IS attribute should be used:



<table>
  <tr is="my-row"></tr>
</table>Copy the code

That is, in standard HTML, some elements can only have specific child elements, and some elements can only have specific parent elements. For example, table cannot have div, tr cannot have a parent element div, etc. So, when you use custom tags, the tag name is still the name of the tag, but you can fill in the name of the custom component in the TAG’s IS attribute.

It should be noted that these restrictions do not apply if you use a string template from one of the following sources:

  • <script type="text/x-template">

  • JavaScript inline template string

  • . Vue component The first two templates are not officially recommended by VUE, so in most cases, only single-file components are available.

Component properties and events

When you use elements in HTML, you have attributes like class and ID, you can also bind events, and you can also customize components. When other custom components are used within a component, the properties and events of the child component are used to communicate data with the parent component.

As shown above, the communication between parent and child components is props Down and Events UP. The parent component passes data to the child component through the properties props, and the child component sends messages to the parent component through events. For example, if a child component needs some data, it internally defines a prop property, and the parent component passes its data property to the child’s property just as it would to an HTML element. When something happens inside a child component, a custom event is used to expose the data involved in the event for the parent component to process.



<my-component v-bind:foo="baz" v-on:event-a="doThis(arg1,... arg2)"></my-component>Copy the code

In the code above,

  • Foo is a prop property defined inside the < my-Component > component, baz is a data property of the parent component,

  • Event-a is an event defined by the child and doThis is a method of the parent.

  • The parent component passes baz data to foo of the child component through prop;

  • The child component gets the value of foo internally and can do something about it;

  • When something changes inside the child component and you want the parent component to know about it, the code fires the Event-A event to send some data

  • The parent binds the event handler to the doThis method, and the data sent by the child is passed in as an argument to the doThis method

  • The parent component can then do something with that data

Properties Props

The Vue component declares a property of its own through the props property, to which the parent component can pass data.



Vue.component('mycomponent', {template: '
       
This is a custom component, the parent component passes me: {{myMessage}}
'
.props: ['myMessage'], data () { return { message: 'hello world'}}})Copy the code

This component is then invoked



<div id="app">
    <mycomponent my-message="hello"></mycomponent>
</div>Copy the code

Note that since HTML is case insensitive, myMessage should be converted to kebab-case (dash separated)my-message=”hello” when passing attribute values.

V-bind Specifies the binding attribute value

Here’s one feature of v-bind binding attribute values: Normally, when v-bind is used to pass values to attribute elements, Vue treats the contents of “” as an expression. Such as:



<div attr="message">hello</div>Copy the code

In this case, the attr property value of the div element is message.

And such



<div v-bind:attr="message">hello</div>Copy the code

Message should be an attribute of the Vue instance’s data, so the attR attribute value of the div element is the value of the message attribute.

I say generic because the class and style features are not. Passing in a normal class name with v-bind:class and class has the same effect, because Vue uses the merge rather than replace principle for both features.

Dynamically binding property values

According to the above, if you want to bind the properties of the parent component to the child component, you should use V-bind so that changes in the parent component are reflected in the child component. Note that depending on the type of property that the parent passes to the child, two things happen when you change this property in the child:

  • When the property passed by the parent component is a reference type, changing the corresponding property in the child component causes the corresponding property of the parent component to change.



    <div id="app2">
    <div>This is the parent component's parentArray: {{parentArray}}</div>
    <my-component :child-array="parentArray"></my-component>
    </div>
    <script>
    Vue.component('my-component', {
     template: '
             
    This is the childArray of the child component that received the value passed by the parent: {{childArray}}

    '
    .props: ['childArray'], data () { return { counter: 1}},methods: { changeArray () { this.childArray.push(this.counter++) } } }) new Vue({ el: '#app2'.data: { parentArray: []}}) </script>Copy the code
  • Changing this property in a child component raises an error when the parent component passes a value of a primitive type. Instead, add the.sync modifier to the binding property value in the parent component.



    <my-component :child-array.sync="parentArray"></my-component>Copy the code

    Then change the corresponding property in the child component



    methods: {
    changeArray () {
     this.counter++
     this.$emit('update:childArray'.this.counter)
    }
    }Copy the code

The child component wants to act on the prop passed in

In general, it is not recommended to operate on attributes passed from the parent in a child component. If there is a real need, do this:

  1. If the parent component passes a primitive type value, you can create a new property in the child component, initialize it with the passed value, and then manipulate the new property



    props: ['initialCounter'].data: function () {
    return { counter: this.initialCounter }
    }Copy the code
  2. The parent component passes a reference type value, and to avoid changing the corresponding data in the parent component, it is best to copy the reference type. In complex cases, it must be deep copy.

Pass the correct type of value to the child component

For the same reason, statically passing a value to a child component’s property will treat it as a string.



<! -- Pass a string "1" -->
<comp some-prop="1"></comp>Copy the code

In the child component, the value of the property is the string “1” instead of number 1. If you want to pass the correct value, you should use v-bind, which treats the passed value as an expression, not as a string.



<! Pass the actual number 1 -->
<comp v-bind:some-prop="1"></comp>Copy the code

Prop validation

We can add validation to the props property of the component so that Vue will warn when the incoming data does not meet the requirements.



Vue.component('example', {
  props: {
    // Base type detection (' null 'means any type can be used)
    propA: Number.// Multiple types
    propB: [String.Number].// Must be a string
    propC: {
      type: String.required: true
    },
    // Numbers, with default values
    propD: {
      type: Number.default: 100
    },
    // The default value of array/object should be returned by a factory function
    propE: {
      type: Object.default: function () {
        return { message: 'hello'}}},// Customize the validation function
    propF: {
      validator: function (value) {
        return value > 10}}}})Copy the code

Type can be the following native constructor:

  • String

  • Number

  • Boolean

  • Function

  • Object

  • Array

  • Symbol

Type can also be a custom constructor function that uses instanceof detection.



 // Customize the Person constructor
 function Person(name, age) {
    this.name = name
    this.age = age
  }
  Vue.component('my-component', {
    template: '
       
Name: {{person-prop.name}}, age: {{person-prop.age}}
'
.props: { person-prop: { type: Person // Specify the type}}})new Vue({ el: '#app2'.data: { person: 2 // An error is reported when the Number type is passed}})Copy the code

Properties not of type Prop

You can also add arbitrary attributes to a custom component just as you add custom attributes beginning with data- to an HTML tag. Instead of being limited to the data-* form, Vue places this attribute on the root element of the custom component. A template for a custom component can have only one root element.

Override non-prop properties

If a parent component passes a value to a child component’s non-prop property, that value overrides the feature in the child component template.



<div id="app3">
    <my-component2 att="helloParent"></my-component2>
</div>
<script>
  Vue.component('my-component2', {
    template: The '
       
child component's original features are overridden by
'
}) new Vue({ el: '#app3'})"/script>Copy the code

As a result, the att attribute of the div is helloParent. Note: As mentioned earlier, the override principle does not apply to classes and styles. Instead, the merge principle is used.



<div id="app3">
    <my-component2 att="helloParent" class="class2" style="color: red;"></my-component2>
</div>
<script>
  Vue.component('my-component2', {
    template: `
       
'overrides the original features of the > child component
}) new Vue({ el: '#app3'})"/script>Copy the code

Class1 class2 The result of the render above is that the div class name is class1 class2 and the inline style is color:red; background:yellow; .

Custom events

Through the PROP property, the parent component can pass data to the child component, and the child’s custom events are used to report internal data to the parent component.



<div id="app3">
    <my-component2 v-on:myclick="onClick"></my-component2>
</div>
<script>
  Vue.component('my-component2', {
    template: '
       
'
.methods: { childClick () { this.$emit('myclick'.'Here's the data I exposed.'.'This is my exposed data 2'.)}}})new Vue({ el: '#app3'.methods: { onClick () { console.log(arguments}}}) </script>Copy the code

As shown above, it is divided into the following steps:

  1. The child component sends custom events in its own methods, along with data that needs to be emitted, through the following code



     this.$emit('myclick'.'Here's the data I exposed.'.'This is my exposed data 2'.)Copy the code
    • The first parameter is the name of the custom event

    • The following parameters are the data that you want to send in turn

  2. The parent component uses V-ON to bind the event handler



    <my-component2 v-on:myclick="onClick"></my-component2>Copy the code

    This way, the parameters passed in can be called in the Methods method of the Vue instance

Note: When using the V-ON binding event handling method, you should not pass in any parameters, but write v-ON :myclick=”onClick”, otherwise the data exposed by the child component will not be available

Binding native Events

If you want to listen for a native event on the root element of a component. You can use the. Native modifier v-on



<my-component v-on:click.native="doTheThing"></my-component>Copy the code

To explore thev-model

V-model can achieve bidirectional data binding for form controls, and its principle is to use binding properties and events to achieve. Take the input control. Instead of using the V-Model, two-way data binding can be implemented as follows:



<div id="app4">
    <input type="text" v-bind:value="text" v-on:input="changeValue($event.target.value)">
    {{text}}
  </div>
  <script>
      new Vue({
        el: '#app4'.data: {
          text: '444'
        },
        methods: {
          changeValue (value) {
            this.text = value
          }
        }
      })
  </script>Copy the code

The code above also implements bidirectional data binding. Its essence is:

  • Bind the value property of the input to the text property of the Vue instance. If the text changes, the content in the input changes too

  • You then set the form’s input event handler to a method of the Vue instance that changes the value of text in the Vue based on the input parameters

  • Accordingly, an input event is fired when input is entered, passing event.target.value to the method and changing the bound data.

V-model is the syntax sugar of the above way, which is to encapsulate the above writing method, convenient for us to use.

Create custom form input components using custom events

Now that you understand the ins and out of the V-Model, you can apply this effect to custom form components. To implement a simple form input component that can only type Hello.



<div id="app5">
    <my-component3 v-model="hello"></my-component3>
    <div>{{hello}}</div>
</div>
<script>
  Vue.component('my-component3', {
    template: `<input ref="input" type="text" :value="value" @input="checkInput($event.target.value)">`.props: ['value'].methods: {
      checkInput (value) {
        var hello = 'hello'
        if(! hello.includes(value)) {this.$emit('input', hello)
          this.$refs.input.value = hello
        } else {
          this.$emit('input', value)
        }
      }
    }
  })
  new Vue({
    el: '#app5'.data: {
      hello: ' '}})"/script>Copy the code

Custom componentv-model

By default, a component’s V-Model uses the value attribute and input events, but input types such as checkboxes and checkboxes may use the value attribute for other purposes. The Model option sidesteps such conflicts:



Vue.component('my-checkbox', {
  model: {
    prop: 'checked'.// Change the entered property to checked
    event: 'change'        // The custom event type is change
  },
  props: {
    checked: Boolean.// this allows using the `value` prop for a different purpose
    value: String}})Copy the code

If I set it up like this,



<my-checkbox v-model="foo" value="some value"></my-checkbox>Copy the code

The code above is equivalent to



<my-checkbox :checked="foo" @change="val => { foo = val }" value="some value"></my-checkbox>Copy the code

In practice, the differences are as follows:

  1. Change the prop property in the child component that receives external data to Checked

  2. When an event is emitted to the parent component, the event type should be changed to change



Vue.component('my-component3', {
    template: `<input ref="input" type="text" :value="checked" @input="checkInput($event.target.value)">`.props: ['checked'].// Attribute name changed
    model: {
      prop: 'checked'.event: 'change'
    },
    methods: {
      checkInput (value) {
        var hello = 'hello'
        if(! hello.includes(value)) {this.$emit('change', hello)   // The event type changes
          this.$refs.input.value = hello
        } else {
          this.$emit('change', value)  // The event type changes}}}})Copy the code

Dynamic components

By using the reserved < Component > element, dynamically bound to its IS feature, multiple components can use the same mount point and dynamically switch:



 <div id="app6">
    <select v-model="currentComponent">
      <option value="home">home</option>
      <option value="post">post</option>
      <option value="about">about</option>
    </select>
    <component :is="currentComponent"></component>
  </div> header>` }, post: { template: `<header> This is the POST component </header> '}, about: {template: '
       
This is about component </
header>` } } }) Copy the code

You can also bind directly to a component object:



var Home = {
  template: `<header>This is the home component </header> '}new Vue({
  el: '#app6',
  data: {
    currentComponent: Home
  }
})Copy the code

Keep the components switched out and avoid re-rendering

If you leave the switched out component in memory, you can preserve its state or avoid rerendering. To do this, add a keep-alive directive argument:



<keep-alive>
  <component :is="currentComponent">
    <! Inactive components will be cached! -->
  </component>
</keep-alive>Copy the code

Use slot to distribute content

Finally, the last of the basics. The official website is very detailed.

A single slot.

Many of the components used above are used like this:



 <component></component>Copy the code

That is, the component is empty, with no text or elements placed. But native HTML elements can be nested, divs with tables inside them. Custom components can also place content between open and close tags, but you need to use slots when defining components.

Slot means that a child component sets up a place to put things in between its open and close tags when it is called.

  1. When there is no slot in the child component, the items placed in the child component tag by the parent component are discarded.

  2. A slot tag of a child component can place content. If the parent component does not place content in a slot tag of a child component, the content in the slot will be rendered.

  3. When a parent component places content within a child component tag, the content in slot is discarded

Templates for child components:



<div>
  <h2>I am the title of the child component</h2><slot> is displayed only if there is no content to distribute. </slot>
</div>Copy the code

Parent component template:



<div>
  <h1>I am the title of the parent component</h1>
  <my-component>
    <p>So here's some initial stuff</p>
  </my-component>
</div>Copy the code

Render result:



<div>
  <h1>I am the title of the parent component</h1>
  <div>
    <h2>I am the title of the child component</h2><p> This is some initial content </p> </div>
</div>Copy the code

A named slot.

There can be many slots. So how does the excess content placed by the child component on the parent component fit into each slot? The method is that the child component gives each slot a name, and when the parent component places extra elements, it assigns a slot name to each element’s slot property. At that point, the excess content will find a slot element with the corresponding name based on its slot attribute.

Note:

  1. A child component can have an anonymous slot into which it will go if the distribution of excess content does not find a slot

  2. If the child component does not have an anonymous slot, the distribution of excess content will be discarded if the slot cannot be found

For example, suppose we have an app-Layout component whose template is:



<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main> 
       
</
slot> </footer> </div>Copy the code

Parent component template:



<app-layout>
  <h1 slot="header">This might be a page title</h1><p> A paragraph of the main content. </p> 

Another main paragraph. </

p> <p slot="footer">Here's some contact information</p> </app-layout>Copy the code

The render result is:



<div class="container">
  <header>
    <h1>This might be a page title</h1>
  </header>
  <main>
    <p>A paragraph of the main content.</p><p> Another major paragraph. </p> ain>
  <footer>
    <p>Here's some contact information</p>
  </footer>
</div>Copy the code

Scope slot

A scoped slot is also a slot slot, but it can pass data to specific elements of the parent component, which then decides how to render the data.

  1. First, the slots of the child components need to have some features (prop)



      Vue.component('my-component4', {
         template: `
             
    `
    , data () { return { hello: [1.'2']}}})Copy the code
  2. When a parent component calls a child component, it needs to add a template element that has the scope property



    <div id="app7">
      <my-component4>
        <template scope="props">
        </template>
      </my-component4>
     </div>Copy the code

    The value of the scope property represents the object composed of all the data passed by the child components. The equivalent of



    props = {
        text: ' '.message: ' '
    }Copy the code
  3. Finally, the parent component can render the data passed by the child component in the template



  <div id="app7">
   <my-component4>
     <template scope="props">
       <span>{{props.text}}</span>
       <span>{{props.message}}</span>
     </template>
   </my-component4>
  </div>Copy the code

A scoped slot is also a slot, but with extra features, and then the parent component does more processing.


This article first appeared on Zhu Qingguang’s blog