# 组件基础

# 基本示例

组件是可复用的,需要在Vue实例里面使用,组件除了没有el等特殊选项,其他的选项都可以使用:比如 computed、watch、methods

<div id="app">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>
<script>
  Vue.component('button-counter', {
    data: function() {
      return {
        count: 0
      }
    },
    template: '<button @click="count++">点击了{{ count }}次</button>'
  })
  var vm = new Vue({
    el: '#app',
    data: {
    }
  })
</script>

# 组件内部的data需要用函数包裹

如果不用函数包裹,组件复用时,多个组件会共享一个data。用闭包后,每个组件的data都是一块独立的区域

data: function() {
  return {
    count: 0
  }
},

# 通过props向组件内部传值

<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>
<script>
  Vue.component('blog-post', {
    props: ['title'],
    template: '<h3>{{ title }}</h3>'
  })
</script>

传入的属性可以是动态的值,用v-for获取博客列表

<!-- <ul><li id="t1">标题1</li><li id="t2">标题2</li><li id="t3">标题3</li></ul> -->
<div id="app">
  <ul>
    <blog-list 
      v-for="item in blogs"
      :title="item.title" 
      :key="item.id"
      :index="item.id"
    ></blog-list>
  </ul>
</div>
<script>
  Vue.component('blog-list', {
    props: ['title', 'index'], 
    template: `<li :id="'t'+index">{{ title }}</li>`
  })
  var vm = new Vue({
    el: '#app',
    data: {
      blogs: [
        { id: 1, title: '标题1' },
        { id: 2, title: '标题2' },
        { id: 3, title: '标题3' },
      ]
    }
  })
</script>

# 向组件内部传入一个对象

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:post="post"
></blog-post>

<script>
Vue.component('blog-post', {
  props: ['post'],
  template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <div v-html="post.content"></div>
    </div>
  `
})
</script>

# 使用组件时,监听组件内部的事件

在使用组件时,传入对应事件名称,以及事件处理函数。子组件内部使用$emit('事件名称'),即可触发事件处理函数,注意: 为遵循HTML规范,事件名称需要是小写

<!-- 使用组件 -->
<blog-list @事件名称="对应事件处理函数">

<!-- 组件内部 -->
<button @click="$emit('事件名称', 参数)">将事件传到组件外部处理</button>

实例

<div id="app">
  <ul>
    <blog-list 
      v-for="item in blogs"
      :title="item.title" 
      :key="item.id"
      :index="item.id" 
      @sendmsg="sendMsg" 
    ></blog-list>
    <!-- 
      如果sendmsg是内嵌js写法,可以用$event获取传过来的值
      @sendmsg="count += $evnet"  
    -->
  </ul>
</div>
<script>
  Vue.component('blog-list', {
    props: ['title', 'index'], 
    data: function() {
      return {
        name: '我是组件内部的变量'
      }
    },
    template: `
      <li :id="'t'+index">
        {{ title }}
        <button @click="greet">触发组件自己的方法</button>
        <button @click="$emit('sendmsg', name)">触发组件外部的方法</button>
      </li>
    `,
    methods: {
      greet: function() {
        alert('Hello')
      }
    }
  })
  var vm = new Vue({
    el: '#app',
    data: {
      blogs: [
        { id: 1, title: '标题1' },
        { id: 2, title: '标题2' },
        { id: 3, title: '标题3' },
      ],
      count: 0
    },
    methods: {
      sendMsg: function(name) {
        alert('接收到组件的的值:'+ name);
      }
    }
  })
</script>

# 在组件上使用v-model

<input v-model="searchText">
<!-- 等价于 -->
<input :value="searchText" @input="searchText=$event.target.value">

<!-- v-model用在子组件上时 -->
<custom-input
  v-bind:value="searchText"
  v-on:input="searchText = $event"
></custom-input>
<script>
  Vue.component('custom-input', {
    props: ['value'],
    template: `
      <input
        v-bind:value="value"
        v-on:input="$emit('input', $event.target.value)"
      >
    `
  })
</script>

# 通过插槽分发内容

之前一直没用到自定义组件的元素内容,其实在组件内部可以通过插槽来获取内容

<!-- 自定义组件 alert-box -->
<alert-box>这里面是自定义组件元素内容</alert-box>

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

<!-- 自定义元素内容会覆盖到组件的<slot></slot>位置 
  <div class="demo-alert-box">
    <strong>Error!</strong>
    这里面是自定义组件元素内容
  </div>
-->

# 解析DOM模板时注意事项

HTML元素对于子元素有限制,比如ul的子元素只能是li,table的子元素需要是thead或tr等,如果使用了自定义组件,会被作为无效的内容提升到外部,并导致最终渲染结果出错,可以使用is来解决这个问题

<!-- 在.html里会出错 -->
<table>
  <blog-post-row></blog-post-row>
</table>

<!-- 替代写法-->
<table>
  <tr is="blog-post-row"></tr>
</table>

注意: 如果从以下来源使用模板的话,这条限制是不存在的:

  • 字符串 (例如:template: '...')
  • 单文件组件 (.vue)
  • <script type="text/x-template">
上次更新: 2020/10/29 22:59:19