组件

目前为止,我们只使用了单个组件。真正的 Vue 应用往往是由嵌套组件创建的。

父组件可以在模板中渲染另一个组件作为子组件。要使用子组件,我们需要先导入它:

import ChildComp from './ChildComp.js'

createApp({
  components: {
    ChildComp
  }
})

我们还需要使用 components 选项注册组件。这里我们使用对象属性的简写形式在 ChildComp 键下注册 ChildComp 组件。

因为我们是在 DOM 中编写模板语法,因此需要遵循浏览器的大小写敏感的标签解析规则。所以,我们需要使用 kebab-case 的名字来引用子组件:

<child-comp></child-comp>

Props

子组件可以通过 props 从父组件接受动态数据。首先,需要声明它所接受的 props:

// 在子组件中
export default {
  props: {
    msg: String
  }
}

一旦声明,msg prop 就会暴露在 this 上,并可以在子组件的模板中使用。

父组件可以像声明 HTML attributes 一样传递 props。若要传递动态值,也可以使用 v-bind 语法:

<child-comp :msg="greeting"></child-comp>

例如:

<script type="module">
import { createApp } from 'vue'
import ChildComp from './ChildComp.js'

createApp({
  components: {
    ChildComp
  },
  data() {
    return {
      greeting: 'Hello from parent'
    }
  }
}).mount('#app')
</script>

<div id="app">
  <child-comp :msg="greeting"></child-comp>
</div>

ChildComp.js

export default {
  props: {
    msg: String
  },
  template: `
  <h2>{{ msg || 'No props passed yet' }}</h2>
  `
}

Emits

除了接收 props,子组件还可以向父组件触发事件:

export default {
  // 声明触发的事件
  emits: ['response'],
  created() {
    // 带参数触发
    this.$emit('response', 'hello from child')
  }
}

this.$emit() 的第一个参数是事件的名称。其他所有参数都将传递给事件监听器。

父组件可以使用 v-on 监听子组件触发的事件——这里的处理函数接收了子组件触发事件时的额外参数并将它赋值给了本地状态:

<child-comp @response="(msg) => childMsg = msg"></child-comp>

例如:

<script type="module">
import { createApp } from 'vue'
import ChildComp from './ChildComp.js'

createApp({
  components: {
    ChildComp
  },
  data() {
    return {
      childMsg: 'No child msg yet'
    }
  }
}).mount('#app')
</script>

<div id="app">
  <child-comp @response="(msg) => childMsg = msg"></child-comp>
  <p>{{ childMsg }}</p>
</div>
export default {
  emits: ['response'],
  created() {
    this.$emit('response', 'hello from child')
  },
  template: `
  <h2>Child component</h2>
  `
}

插槽

除了通过 props 传递数据外,父组件还可以通过插槽 (slots) 将模板片段传递给子组件:

<child-comp>
  This is some slot content!
</child-comp>

在子组件中,可以使用 <slot> 元素作为插槽出口 (slot outlet) 渲染父组件中的插槽内容 (slot content):

<!-- 在子组件的模板中 -->
<slot></slot>
<slot> 插口中的内容将被当作

<slot>插口中的内容将被当作“默认”内容:它会在父组件没有传递任何插槽内容时显示:

<slot>Fallback content</slot>

例如:

<script type="module">
import { createApp } from 'vue'
import ChildComp from './ChildComp.js'

createApp({
  components: {
    ChildComp
  },
  data() {
    return {
      msg: 'from parent'
    }
  }
}).mount('#app')
</script>

<div id="app">
  <child-comp>Message: {{ msg }}</child-comp>
</div>

ChildComp.js

export default {
  template: `
  <slot>Fallback content</slot>
  `
}