基本示例

组件的定义:

// 定义一个名为 button-counter 的新组件
Vue.component('button-counter',{
    data:function(){
        return{
            count:0
        }
    },
    template:'<button v-on:click="count++">点击次数{{count}}</button>'
})

组件的使用:

<div id="components-demo">
  <button-counter></button-counter>
</div>

组件的实例化:

new Vue({ el: '#components-demo' })

因为组件是可复用的Vue实例,所以与new Vue接收的参数相同,例如data、computed、watch、methods等。唯一的不同是 el 这样根实例特有的选项。

组件的复用

你可以将组件进行任意次数的复用:

<div id="components-demo">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>

点击按钮时,每个组件都会独立维护它的data值,因为每用一次组件就会有个新的实例被创建。

#data必须是一个函数

一个组件的data选项必须是一个函数,每个实例单独维护一份返回对象独立的拷贝。

这样才能不影响到其他实例的data属性值。

组件的组织

通常一个应用会以一棵嵌套的组件树的形式来组织:

image
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。

为了能在模板中使用,这些组件必须先进行注册才能被识别,这里有 全局注册局部注册 两种注册方式。我们的组件都是通过Vue.component进行全局注册的。

Vue.component('my-component-name', {
  // ... options ...
})

全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。

通过Prop向子组件传递数据

Prop 是你可以在组件上注册的一些自定义特性。当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性。

Vue.component('test-counter',{
    props:["msg"],
    template:'<p>{{msg}}</p>'
})

一个组件默认可以有任意数量的Prop,任何值都可以传递给Prop,在组件实例中访问这个值就像访问data中的值一样。

<div id="test" >
    <test-counter msg="test01"></test-counter>
    <test-counter msg="test02"></test-counter>
</div>

也可以使用v-for动态渲染元素

<div id="test" >
    <test-counter v-for="post in posts" v-bind:msg="post.msg"></test-counter>
</div>
new Vue({
    el:"#test",
    data:{
        posts:[
            {msg:"test01"},
            {msg:"test02"},
            {msg:"test03"}
        ]
    }
})

单个根元素

模板中的元素必须包含在一个根节点里面,否则Vue 会显示一个错误,并解释道 every component must have a single root element (每个组件必须只有一个根元素)可以将模板的内容包裹在一个父元素内:

<div class="blog-post">
  <h3>{{ title }}</h3>
  <div v-html="content"></div>
</div>

当组件变得越来越复杂的时候,为每个相关信息定义一个prop会变得很麻烦,我们可以重构成一个单独的porp属性:

Vue.component('test-counter',{
    props:["port"],
    template:'<p>{{port.title}}-{{port.auth}}</p>'
})
<test-counter v-for="post in posts" v-bind:port="post"></test-counter>
new Vue({
    el:"#test",
    data:{
        post:[
            {title:"test01",auth:"aush1"},
            {title:"test02",auth:"aush1"},
            {title:"test03",auth:"aush2"}
        ]
    }
})

现在,不论何时为 post 对象添加一个新的属性,它都会自动地在 内可用。

监听子组件事件

我们开发组件的时候,部分子组件会有和父组件沟通的需求,但是子组件不能直接使用父组件的属性和方法。

Vue提供了一个自定义事件的系统来解决这个问题,父级组件可以像处理 native DOM 事件一样通过v-on来监听子组件实例的任意事件:

<div id="test" v-bind:style="{fontSize:fontSize+'px'}" >
    <test-counter v-on:add-size="fontSize+=1" v-for="post in posts" v-bind:port="post"></test-counter>
</div>

同时子组件可以调用内建的 $emit 方法 并传入事件名称来触发一个事件,方法名称必须为全小写,Vue内部会转换大小写:

Vue.component('test-counter',{
    props:["port"],
    template:'<p>{{port.title}}-{{port.auth}}<button v-on:click="$emit(\'add-size\')">add</button></p>'
})

实例化的时候父级定义一个属性值

new Vue({
    el:"#test",
    data:{
        posts:[
            {title:"test01",auth:"aush1"},
            {title:"test02",auth:"aush1"},
            {title:"test03",auth:"aush2"}
        ],
        fontSize:12
    }
})

#使用事件抛出一个值

有时候需要在子组件中抛出特定值,这时可以使用 $emit 函数的第二的参数来提供这个值

<button v-on:click="$emit('add-size',1)">add</button>

父组件监听事件的时候,通过 $event 访问抛出的这个值:

<test-counter 
v-on:add-size="fontSize+=$event"
...
></test-counter>

如果事件处理函数是一个方法

<test-counter v-on:add-size="addFontSize" v-for="post in posts" v-bind:port="post"></test-counter>

那么这个值将会作为第一个参数传入这个方法:

methods:{
    addFontSize:function(size){
        this.fontSize+=size;
    }
}

#组件上使用v-model

自定义事件可以用于创建支持 v-model 的自定义输入组件。

<input v-model="searchText">

等价于:

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

当运用到组件上时:

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

为了让其正常工作这个组件内的input必须

  • 将其value属性绑定到名称叫做value的prop上
  • 在其input事件被触发的时候,通过自定义的input事件抛出

代码如下:

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

现在 v-model 就应该可以在这个组件上完美地工作起来了:

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

通过插槽分发内容

和 HTML 元素一样,我们经常需要向一个组件传递内容

<alert-box>
  Something bad happened.
</alert-box>

Error! Something bad happened.

Vue 自定义的 ==== 元素让这变得非常简单

Vue.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})

我们只要在需要的地方加入插槽就行了

动态组件

有时候在不同组件之间动态切换是非常有用的,比如在多标签界面中。

可以通过component元素加上特殊的is特性来实现

<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>

例子中currentTabComponent 可以包括:

  • 已注册组件的名字,或
  • 一个组件的选项对象

解析dom模板注意事项

在html中如<ul><select><ol>、和<table>等标签对于哪些元素可以出现在内部有严格限制,而有些元素如<li><option><tr>等职能出现在特定元素内部。

这会导致我们使用这些约束性的元素时遇到一些问题

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

这个自定义组件<blog-post-row>会被作为无效的内容提升到外部,并导致最终渲染结果出错,我们可以使用特殊的is特性来解决这个问题

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

若我们 从以下来源使用模板就没有这种限制

  • 字符串(如:template:'')
  • 单文件组件(.vue)
  • <script type="text/x-template">
最后修改:2019 年 08 月 02 日
如果觉得我的文章对你有用,请随意赞赏