Vue- component details

examineOriginal site, more expanded content and better reading experience!

Component details

Components and Reuse

Vue components need to be registered before they can be used. There are two ways of registration: global registration and local registration.

Global registration

Vue.component('my-component', {});

To use this component in the parent instance, you must register before the instance is created and then use it<my-component></my-component>To use components in the form of.

Vue.component('my-component', {
    template: `<div>这是一个组件</div>`
});

templateThe DOM structure of must be contained by an element, missing<div></div>Can’t render and report an error.

In the Vue instance, thecomponentsOption can register components locally, and the registered components are only valid under the scope of the instance.

Can also be used in componentscomponentsOption to register components so that they can be nested.

var Child = {
    template: `<div>局部注册组件的内容</div>`
};

new Vue({
    el: '#app',
    components: {
        'my-component': Child
    },
});

Templates for Vue components are limited by HTML under certain circumstances, such as<table>The internal regulations only allow<tr><td><th>Such as these table elements, so in<table>Invalid when directly using components within.In this case, specialisProperty to mount the component.

<div id="app">
    <table>
        <tbody is="my-component"></tbody>
    </table>
</div>

Vue.component('my-component', {
    template: `<div>这里是组件内容</div>`
});

Common limiting elements are<ul><ol><select>.

Except ..templateIn addition to the options, other options can be used in the component like Vue instances, such asdatacomputedmethodsWait.

But in usedataAt that time,dataMust be a function and then transfer the datareturngo out.

JavaScript objects are reference relationships ifreturnThe object of refers to an external object, then this object is shared and any modification will be synchronized.

UsepropsTransfer data

Components not only reuse the contents of templates, but also communicate with each other.

Usually, the template of the parent component contains subcomponents. The parent component will forward data or parameters to the subcomponents. The subcomponents will render different contents or perform operations according to different parameters. This process of transferring data forward passes throughpropsTo achieve.

In the assembly, use optionspropsDeclare data that needs to be received from the parent.propsThe value of can be two kinds, one isString arrayOne isObject.

<my-component message="来自父组件的数据"></my-component>

props: ['message'],
template: `<div>{{message}}</div>`,

propsData and components declared indataIn functionreturnThe main difference is thatpropsThe data of is from the parent, whiledataIn the is the component’s own data, scope is the component itself, the two kinds of data can be in the templatetemplateAnd calculation attributescomputedAnd methodsmethodsTo be used in.

Because HTML features are not case sensitive, humps are named when DOM templates are usedpropsThe name should be changed to a dash split name.

<my-component warning-text="提示信息"></my-component>

Sometimes, the data passed is not directly written to death, but is dynamic data from the parent, at which time instructions can be used.v-bindDynamic bindingpropsWhen the data of the parent component changes, the child component is also passed.

<div id="app">
    <input type="text" v-model="parentMessage">
    <my-component :message="parentMessage"></my-component>
</div>

props: ['message'],
template: `<div>{{message}}</div>`,

data: {
    parentMessage: ''
}

For use herev-modelThe data of the parent is boundparentMessage, when any input through the input box, the subcomponent receivedprops["message"]It also responds in real time and updates component templates.

Unidirectional data flow

Two kinds of changes are often encountered in the business.propOne is that the parent component passes the initial value in and the child component saves it as the initial value, which can be used and modified at will under its own scope. This can be done in componentsdataWithin the declaration of a data, reference the parent component’sprop.

<div id="app">
    <my-component :init-count="1"></my-component>
</div>

Vue.component('my-component', {
    props: ['initCount'],
    template: `<div>{{count}}</div>`,
    data() {
        return {
            count:this.initCount
        }
    }
});

Data is declared in the componentcountThat gets the from the parent component when the component is initializedinitCount, then has nothing to do with it, only maintenancecountIn this way, direct operation can be avoided.initCount.

Another situation ispropAs the original value that needs to be transformed, this case can be passed in by calculating the attribute.

<div id="app">
    <my-component :width="100"></my-component>
</div>

Vue.component('my-component', {
    props: ['width'],
    template: `<div :style="style">组件内容</div>`,
    computed: {
        style: function () {
            return {
                width: this.width + 'px'
            }
        }
    }
});

Because CSS is used to transfer width with units (px) and numerical calculation is generally without units, calculation attributes are uniformly used within components.

In JavaScript, when referring to an object and an array, the type points to the same memory space, sopropsWhen it is an object or an array, changes within the child component will affect the parent component.

Array validation

WhenpropWhen verification is required, object writing is required.

Generally, when components need to be provided to others for use, data verification is recommended. For example, some data must be of numeric type. If a string is passed in, a warning will pop up in the console.

Vue.component('my-component', {
    props: {
        // 必须是数字
        propA: Number,
        // 必须是字符串或数字类型
        propB: [String, Number],
        // 布尔值,如果没有定义,默认值是true
        propC: {
            type: Boolean,
            default: true
        },
        // 数字,而且是必传
        propD: {
            type: Number,
            default: true
        },
        // 如果是数组或对象,默认值必须是一个函数来返回
        propE: {
            type: Array,
            default: function () {
                return []
            }
        },
        // 自定义一个验证函数
        propF: {
            validator: function (value) {
                return value > 10
            }
        }
    }
});

VerifiedtypeThe type can be:

  • String
  • Number
  • Boolean
  • Object
  • Array
  • Function

typeIt can also be a custom constructor that uses theinstanceofTesting.

Component communication

Component relationships can be divided into parent component communication, sibling component communication, and cross-level component communication.

Custom event

Custom events are used when child components need to pass data to parent components.

v-onIn addition to listening for DOM events, it can also be used for custom events between components.

JavaScript Design Pattern-Observer Pattern Method;

  • dispatchEvent
  • addEventListener

For subcomponents of Vue components$emit()To trigger the event, the parent component uses the$on()To listen for sub-component events.

The parent component can also be used directly on the custom label of the child component.v-onTo listen for custom events triggered by subcomponents.

<div id="app">
    <p>总数:{{total}}</p>
    <my-component
            @increase="handleGetTotal" @reduce="handleGetTotal"></my-component>
</div>

Vue.component('my-component', {
    template: `
        <div>
            <button @click="handleIncrease">+</button>
            <button @click="handlereduce">-</button>
        </div>
        `,
    data() {
        return {
            counter: 0
        }
    },
    methods: {
        handleIncrease: function () {
            this.counter++;
            this.$emit('increase', this.counter);
        },
        handlereduce: function () {
            this.counter--;
            this.$emit('reduce', this.counter)
        }
    }
});

new Vue({
    el: '#app',
    data: {
        total: 0
    },
    methods: {
        handleGetTotal: function (total) {
            this.total = total;
        }
    }
});

In changing the component’sdata "counter"After, through$emit()Then pass it to the parent component, which uses the@increaseAnd@reduce.$emit()The first parameter of the method is the name of the custom event.

In addition to usingv-onIn addition to listening for custom events on the component, you can also listen for DOM events, at which point you can use the.nativeModifier indicates a native event when listening, listening to the component’s root element.

<my-component @click:native="handleClick"></my-component>

Usev-model

Vue can be used on custom componentsv-modelInstructions.

<my-component v-model="total"></my-component>

Component$emit()The event name of is specialinput, which is using the parent of the component, does not exist in the<my-component>Use on@input="handler", but directly usedv-modelOne data boundtotal.

<my-component @input="handleGetTotal"></my-component>

v-modelIt can also be used to create custom form input components for data bidirectional binding.

<div id="app">
    <p>总数:{{total}}</p>
    <my-component v-model="total"></my-component>
    <button @click="handleReduce">-</button>
</div>

Vue.component('my-component', {
    props: ['value'],
    template: `<input :value="value" @input="updateValue">`,
    methods: {
        updateValue: function () {
            this.$emit('input', event.target.value)
        }
    }
});

new Vue({
    el: '#app',
    data: {
        total: 10
    },
    methods: {
        handleReduce: function () {
            this.total--;
        }
    }
});

Implementation of such a two-way bindingv-modelComponents must meet the following two requirements:

  1. Receive onevalueAttribute
  2. There are new onesvalueTime triggerinputEvents

Non-parent-child component communication

In actual business, besides parent-child component communication, there are many scenarios of non-parent-child component communication. Generally, there are two types of non-parent-child components.Brother componentAndAcross multi-level components.

InVue 1.xIn the version, in addition to$emit()The method also provides¥dispatch()And$broadcast().

$dispatch()It is used to distribute events to superiors. As long as it is its parent (one or more levels), it can be used in Vue instanceeventsReceive within options.

This instance is only valid in VUE version 1.x:

<div id="app">
    <p>{{message}}</p>
    <my-component></my-component>
</div>

Vue.component('my-component', {
    template: `<button @click="handleDispatch">派发事件</button>`,
    methods: {
        handleDispatch: function () {
            this.$dispatch('on-message', '来自内部组件的数据')
        }
    }
});
new Vue({
    el: '#app',
    data: {
        message: ''
    },
    events: {
        'on-message': function (msg) {
            this.message = msg;
        }
    }
});

$broadcast()It broadcasts events from the higher level to the lower level. The usage is exactly the same and the direction is opposite.

Once these two methods issue an event, any component can receive it. The principle of proximity will be followed, and bubbling will stop after the first receipt unless it returns.true.

These methods have been abandoned in vue2.x.

In Vue 2.x, any empty Vue instance is recommended as the central event bus (bus), which is an intermediary.

<div id="app">
    <p>{{message}}</p>
    <component-a></component-a>
</div>

var bus = new Vue();

Vue.component('component-a', {
    template: `<button @click="handleEvent">传递事件</button>`,
    methods: {
        handleEvent: function () {
            bus.$emit('on-message', '来自组件component-a的内容')
        }
    }
});
var app = new Vue({
    el: '#app',
    data: {
        message: ''
    },
    mounted: function () {
        var _this = this;
        // 在实例初始化时,监听来自bus实例的事件
        bus.$on('on-message', function (msg) {
            _this.message = msg;
        })
    }
});

First of all, we created a system calledbusEmpty Vue instance of; The components are then defined globallycomponent-a; Finally, Vue instance is created.app.

InappDuring initialization, that is, during the life cyclemountedThe hook function is listening forbusEvents ofon-messageAnd in the assemblycomponent-a, click on the button will passbusTake an incidenton-messageSend it out. At this timeappYou will receive information frombusTo complete its own business logic in the callback.

This method skillfully and lightly realizes the communication between any component, including father and son, brother and cross-level.

If used in depth, it can be expanded.busInstance, add to itdatamethodscomputedSuch as options, these are all public.

It is very useful in business, especially in collaborative development, because it is often necessary to share some common information, such as the user’s login nickname, gender, email, etc., as well as the user’s authorization.tokenWait.

Just let thebusOnce obtained, any component can be used directly from it at any time, which is very practical in single page rich application (SPA).

In addition to the central event busbusIn addition, there are two methods to achieve communication between components: parent chain and child component index.

Parent chain

In the subassembly, use thethis.$parentThe parent instance or component of the component can be accessed directly, and the parent component can also be accessed through thethis.$childrenAccess all its subcomponents, and can recursively access up or down indefinitely until the root instance or innermost component.

<div id="app">
    <p>{{message}}</p>
    <component-a></component-a>
</div>

Vue.component('component-a', {
    template: `<button @click="handleEvent">通过父链直接修改数据</button>`,
    methods: {
        handleEvent: function () {
            this.$parent.message = '来自组件component-a的内容'
        }
    }
});
var app = new Vue({
    el: '#app',
    data: {
        message: ''
    }
});

Although Vue allows this operation, in business, subcomponents should avoid relying on the data of the parent component as much as possible, and should not actively modify its data, because this makes the parent-child component tightly coupled, only looking at the parent component, it is difficult to understand the state of the parent component, because it can be modified by any component, and ideally, only the component itself can modify its state.

Parent-child components are best passed throughpropsAnd$emit()To communicate.

Subcomponent index

When there are many subcomponents, passthis.$childrenIt is difficult to traverse a required component instance, especially when components are rendered dynamically, their sequence is not fixed.

Vue provides a method for indexing subcomponents with special propertiesrefTo specify an index name for the subcomponent.

<div id="app">
    <button @click="handleRef">通过ref获取子组件实例</button>
    <component-a ref="comA"></component-a>
</div>

Vue.component('component-a', {
    template: `<div>子组件</div>`,
    data() {
        return {
            message: '子组件内容'
        }
    },
});
var app = new Vue({
    el: '#app',
    methods: {
        handleRef: function () {
            // 通过$refs来访问指定的实例
            var msg = this.$refs.comA.message;
            console.log(msg);
        }
    }
});

In the parent component template, use on the child component labelrefSpecify a name and pass within the parent componentthis.$refsTo access the subcomponent with the specified name.

$refsFill only after the component rendering is complete, and it is non-responsive. It is only used as an emergency plan to directly access subcomponents and should be avoided in templates or calculation properties$refs.

Vue 2.x willv-elAndv-refMerge intorefVue will automatically determine whether it is a normal label or a component.

UseslotDistribute content

This is used when you need to combine components and mix the contents of the parent component with the templates of the child component.slotThis process is calledContent distribution.

  • <app>The component does not know what its mount point will contain. The contents of the mount point are determined by<app>Is determined by the parent component of the.
  • <app>The component most likely has its own template.

propsTransmitting data,eventsTrigger events andslotContent distribution constitutes the three API sources of Vue components, and the most complex components are also composed of these three parts.

action scope

Templates in parent component:

<child-component>
    {{message}}
</child-component>

HeremessageJust oneslotHowever, it binds the data of the parent component, not the component<child-component>The data of.

The contents of the parent component template are compiled within the scope of the parent component, while the contents of the child component template are compiled within the scope of the child component.

<div id="app">
    <child-component v-modle="showChild"></child-component>
</div>

Vue.component('child-component', {
    template: `<div>子组件1</div>`,
});
var app = new Vue({
    el: '#app',
    data: {
        showChild: true
    }
});

The state hereshowChildBinding is the data of the parent component.

Bind data on subcomponents:

<div id="app">
    <child-component></child-component>
</div>

Vue.component('child-component', {
    template: `<div v-model="showChild">子组件</div>`,
    data() {
        return {
            showChild: true
        }
    }
});
var app = new Vue({
    el: '#app',
});

Therefore,slotThe content distributed is scoped on the parent component.

singleslot

Use special<slot>Element can open a for this componentslot(slot), in the parent component template, everything inserted in the child component label will replace the child component’s<slot>The label and its contents.

<div id="app">
    <child-component>
        <p>分发的内容</p>
        <p>更多分发的内容</p>
    </child-component>
</div>

Vue.component('child-component', {
    template: `
    <div>
        <slot>
           <p>如果没有父组件插入内容,我将作为默认出现。</p>
        </slot>
    </div>
            `,
});
var app = new Vue({
    el: '#app',
});

Subassemblychild-componentIs defined in the template of<slot>Element, and use a<p>As the default content, it is not used in the parent component.slotThe default text will be rendered when the. If it is writtenslot, it will replace the whole<slot>.

Subassembly<slot>The scope of which is the subcomponent itself.

NamedSlot

to<slot>Element specifies anameAfter that, multiple contents can be distributed and named.slotCan be associated with a singleslotCoexistence.

<div id="app">
    <child-component>
        <h2 slot="header">标题</h2>
        <p>正文的内容</p>
        <p>更多正文的内容</p>
        <div slot="footer">底部信息</div>
    </child-component>
</div>

Vue.component('child-component', {
    template: `
<div class="container">
    <div class="header">
        <slot name="header"></slot>
    </div>
    <div class="main">
        <slot></slot>
    </div>
    <div class="footer">
        <slot name="footer"></slot>
    </div>
</div>    
    `,
});
var app = new Vue({
    el: '#app',
});

Three are declared within the subassembly<slot>Element, in which<div class="main">Internal<slot>Not usednameProperty, which will be the defaultslotAppears, parent component is not in useslotThe elements and contents of the feature will appear here.

If no default anonymity is specifiedslotThe extra content in the parent component will be discarded.

When using components in combination, the content distribution API is crucial.

Scope slot

Scope slots are specialslotTo replace rendered elements with a reusable template.

<div id="app">
    <child-component>
        <template scope="props">
            <p>来自父组件的内容</p>
            <p>{{props.msg}}</p>
        </template>
    </child-component>
</div>

Vue.component('child-component', {
    template: `
<div class="container">
    <slot msg="来自子组件的内容"></slot>
</div> 
    `,
});
var app = new Vue({
    el: '#app',
});

Templates for subcomponents, in<slot>There is a similarity on the elementpropsWriting Method for Passing Data to Componentsmsg="xxx"To pass data to the slot.

Used in parent component<template>Element and has ascope="props"The characteristics of, herepropsIs a temporary variable.

templateCan be passed through temporary variablespropsAccess data from subcomponent slotsmsg.

A more representative use case for scope slots is the list component, which allows the component to customize how each item in the list should be rendered.

<div id="app">
    <my-list :book="books">
        <!--作用域插槽也可以是具名的Slot-->
        <template slot="book" scope="props">
            <li>{{props.bookName}}</li>
        </template>
    </my-list>
</div>

Vue.component('my-list', {
    props: {
        books: {
            type: Array,
            default: function () {
                return [];

            }
        }
    },
    template: `
<ul>
    <slot name="book" v-for="book in books" :book-name="book.name"></slot>
</ul>
    `,
});

Subassemblymy-listReceive a from the parentproparraybooksAnd put it in thenameForbookTheslotUse onv-forInstruction loops, exposing a variable at the same timebookName.

The usage scenario of scope slot can reuse sub-componentsslot, and can makeslotThe content is inconsistent.

Accessslot

Vue 2.x provides access toslotMethods of distributing content$slots.

<div id="app">
    <child-component>
        <h2 slot="header">标题</h2>
        <p>正文的内容</p>
        <p>更多正文的内容</p>
        <div slot="footer">底部信息</div>
    </child-component>
</div>

Vue.component('child-component', {
    template: `
<div class="container">
    <div class="header">
        <slot name="header"></slot>
    </div>
    <div class="main">
        <slot></slot>
    </div>
    <div class="footer">
        <slot name="footer"></slot>
    </div>
</div>    
    `,
    mounted: function () {
        var header = this.$slots.header;
        var main = this.$slots.default;
        var footer = this.$slots.footer;
        console.log(footer);
        console.log(footer[0].elm.innerHTML);
    }
});
var app = new Vue({
    el: '#app',
});

via$slotsCan access a namedslot,this.$slots.defaultIncluding all those not included in the nameslotThe node in the.

Advanced usage of components

Recursive component

Set up componentsnameOption, a component can recursively call itself within its template.

<div id="app">
    <child-component :count="1"></child-component>
</div>

Vue.component('child-component', {
    name: 'child-component',
    props: {
        count: {
            type: Number,
            default: 1
        }
    },
    template: `
<div class="child">
    <child-component :count="count+1" v-if="count<3"></child-component>
</div>
    `,
});

Recursive use of components can be used to develop independent components with unknown levels of shutdown, such as cascade selectors and tree controls.

Inline template

The templates for components are usually found intemplateAs defined in the option, Vue provides the function of an inline template, which is used for component labels when using components.inline-templateFeature, the component will use its content as a template instead of distributing it as content, which makes the template more flexible.

<div id="app">
    <child-component inline-template>
        <div>
            <h2>在父组件中定义子组件的模板</h2>
            <p>{{message}}</p>
            <p>{{msg}}</p>
        </div>
    </child-component>
</div>

Vue.component('child-component', {
    data() {
        return {
            msg: '在子组件中声明的数据'
        }
    }
});
var app = new Vue({
    el: '#app',
    data: {
        message: '在父组件中声明的数据'
    }
});

Data declared in the parent componentmessageAnd subcomponentsmsg, both of which can be rendered (if they have the same name, the data of the subcomponent is preferred).This is the disadvantage of inline templates, that is, the scope is difficult to understand. If it is not a very special scene, it is recommended not to use inline templates easily..

Dynamic component

Vue.js provides a special element<component>For dynamically mounting different components, use theisProperty to select the components to mount.

<div id="app">
    <button @click="handleChangeView('A')">切换到A</button>
    <button @click="handleChangeView('B')">切换到B</button>
    <button @click="handleChangeView('C')">切换到C</button>
    <component :is="currentView"></component>
</div>

var app = new Vue({
    el: '#app',
    data: {
        currentView: 'comA'
    },
    components: {
        comA: {
            template: `<div>组件A</div>`
        },
        comB: {
            template: `<div>组件B</div>`
        },
        comC: {
            template: `<div>组件C</div>`
        },
    },
    methods: {
        handleChangeView: function (component) {
            this.currentView = 'com' + component
        }
    }
});

You can bind directly to component objects:

<div id="app">
    <component :is="currentView"></component>
</div>

var Home = {
    template: `<p>Welcome home!</p>`
};

var app = new Vue({
    el: '#app',
    data: {
        currentView: Home
    }
});

Asynchronous component

Vue.js allows a component to be defined as a factory function that dynamically parses the component.

Vue.js only triggers factory functions when components need to be rendered, and caches the results for later rendering again.

<div id="app">
    <child-component></child-component>
</div>

Vue.component('child-component', function (resolve, reject) {
    window.setTimeout(function () {
        resolve({
            template: `<div>我是异步渲染的!</div>`
        })
    }, 1000)
});

var app = new Vue({
    el: '#app',
});

The factory function receives aresolveCallback, called when a component definition downloaded from the server is received. You can also callreject(reason)Indicates that the load failed.

Other

$nextTick

Asynchronous update queue

Vue does not directly update DOM when observing data changes, but opens a queue and buffers all data changes occurring in the same event cycle. Duplicate data is removed during buffering, thus avoiding unnecessary calculations and DOM operations. Then, in the next event cycletickIn, Vue flushes the queue and performs the actual (de-duplicated) work.

Vue will give priority to native browser environmentPromise.thenAndMutationObserver, if they are not supported, they will be adoptedsetTimeoutReplace.

$nextTickIs used to determine when DOM updates have been completed.

<div id="app">
    <div id="div" v-if="showDiv">这是一段文本</div>
    <button @click="getText">获取div内容</button>
</div>

var app = new Vue({
    el: '#app',
    data: {
        showDiv: false
    },
    methods: {
        getText: function () {
            this.showDiv = true;
            this.$nextTick(function () {
                var text = document.getElementById('div');
                console.log(text.innerHTML);
            })
        }
    }
});

X-Templates

Vue provides another way to define templates in<script>Used in labelstext/x-templateType and specify oneid, will thisidAssigned totemplate.

<div id="app">
    <my-component></my-component>
    <script type="text/x-template" id="my-component">
        <div>这是组件的内容</div>
    </script>
</div>

Vue.component('my-component', {
    template: `#my-component`,
});
var app = new Vue({
    el: '#app',
});

Mount the instance manually

In some very special cases, Vue instances need to be created dynamically. Vue providesVue.extendAnd$mountTwo methods to mount an instance manually.

Vue.extendIt is the basic Vue constructor, creating a “subclass”, and the parameter is an object containing component options.

If the Vue instance is not received at the time of instantiationelOption, it is in the “unmounted” state and has no DOM element associated with it. Can be used$mountManually mount an unmounted instance. This method returns the instance itself, so other instance methods can be called in a chain.

<div id="app"></div>

var MyComponent = Vue.extend({
    template: `<div>Hello {{name}}</div>`,
    data() {
        return {
            name: 'Andy'
        }
    }
});
new MyComponent().$mount('#app');

In addition to the above writing, there are two writing methods:

new MyComponent().$mount("#app");

new MyComponent({
    el: '#app'
})

Manually mounting instances (components) is an extreme advanced usage that is rarely used in the business and may only be used when developing some complex independent components.