跟我一起学Vue3——组件基础

727 阅读5分钟

前言

⭐️ 本专栏主要内容为「 Vue3官方文档 」的学习过程 ✍🏼

⭐️ 本专栏适合人群:Vue小白、学完一遍Vue的掘友 👨🏼‍💻

⭐️ 本专栏的阅读顺序和官方文档顺序相同 📖

⭐️ 英雄不问出处,这个专栏一定让你收获满满 🥳!

⭐️ 静心,思考,实操,坚持,巩固,满载而归 🥰!

⭐️ 欢迎各位掘友在评论区交流 🤡

第十二章 组件基础

基本示例

我在前面说过了应用实例和组件实例,挂载到应用实例上的组件实例是一个根组件实例。

组件是带有名称的可复用实例。在典型的Vue应用中,我们通常使用单文件组件而不是字符串模板。

<div id="components-demo">
  <button-counter></button-counter>
</div>
// app 是应用实例, vm 是根组件实例
const vm = app.mount('#components-demo')

在这个例子中 <button-counter> 是一个可复用组件实例。我们可以把这个组件作为一个根实例中的自定义元素来使用

组件复用

将组件进行任意次数的复用

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

注意,复用的组件会维护它自己的 property。每用一次组件,就有一个他的新实例被创建。

组件组织

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

组件注册有两种类型: 全局注册和局部注册

全局注册: 通过 app.component 方法进行全局注册

全局注册的组件可以在应用中的任何组件的模板中使用

局部注册: 在一个组件实例中使用一个组件,那么这个组件就是局部注册,因为这个组件只能在这个组件实例中访问到。

父组件传值

通过 prop 选项向子组件传值

const app = Vue.createApp({})

app.component('son', {
  props: ['title'],
  template: `<h1>{{ title }}</h1>`
})

app.mount('#father')

props 可以为这个组件创建一个 attribute, 使用 son 元素的 title attribute 传值

<div id="father">
  <son title="Vue Learning"></son>
  <son title="Jue Jin"></son>
  <son title="shang"></son>
</div>

我们也可以动态传递 prop ,这就需要 v-bind 指令

const App = {
  data() {
    return {
      list: [
        { id: 1, title: 'Vue Learning'},
        { id: 2, title: 'Jue Jin'},
        { id: 3, title: 'shang'}
      ]
    }
  }
}

const app = Vue.createApp(App)
app.component('son', {
  props: ['title'],
  template: `<h1>{{ title }}</h1>`
})

app.mount('#father')
<div id="father">
  <son
    v-for="item in list"
    :key="item.id"
    :title="item.title">
  </son>
</div>

一个组件可以拥有多个 prop, 并且在默认情况下,无论任何值都可以传递给 prop,当然我们可以对 prop 的值类型做限制

app.component('son', {
  props: {
    title: {
      type: Object,	// 值类型是对象
      required: true  // 必填项
    }
  },
  template: `<h1>{{ title }}</h1>`
})

监听子组件的事件

官方文档给了一个博客的例子,推荐大家看一看 监听子组件,我在这里写的和他差不多。

这个例子是子组件内做一个使父组件中字号放大功能的按钮,然后如何按子组件内的按钮改变父组件的值。这里用到了 $emit 方法

const App = {
  data() {
    return {
      postFontSize: 1
    }
  },
  methods: {
    addFontSize() {
      this.postFontSize++
    }
  }
}

const app = Vue.createApp(App)
app.mount('#father')
<div id="father" :style="{ fontSize: postFontSize + 'em'}">
  <son @add="addFontSize"></son>
</div>
// son
app.component('son', {
  props: ['title']
  template: `
    <div>
      <h1>{{ title }}</h1>
      <button @click='$emit('add')'> Enlarge </button>
    </div>
	`
})

因为存在 @add="addFontSize" 监听器,父级组件能够接收事件并更新 postFontSize 的值。

我们也可以在组件的 emits 选项中列出已抛出的事件:

// son
app.component('son', {
  props: ['title']
  emits: ['add']
  template: `
    <div>
      <h1>{{ title }}</h1>
      <button @click='addMethod'> Enlarge </button>
    </div>
	`,
	methods: {
    addMethod() {
      this.$emit('add')
    }
  }
})

使用事件抛出一个值

使用 $emit 第二个参数来提供值。

<button @click="$emit('add', 0.5)">
  Enlarge
</button>

然后父组件监听这个事件时,取值

表达式取值

通过 $event 访问子组件传过来的值。

<son @add="postFontSize += $event"></son>

方法取值

@add 的事件处理函数是一个方法,子组件传过来的值会作为第一个参数传入这个方法

methods: {
  addFontSize(amount) {
    this.postFontSize += amount
  }
}

通过插槽分发内容

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

<alert-box> Something bad happened </alert-box>
app.component('alert-box', {
  template: `
  	<div>
     <strong> Error!</strong>
     <slot></slot>
    </div>
  `
})

效果: **Error!**Something bad happened

我们使用 <slot> 作为我们想要插入内容的占位符——就这么简单!

到目前为止,关于插槽你需要了解的大概就这些了

动态组件

在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里。

通过 Vue 的 <component> 元素加一个特殊的 is attribute 来实现

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

currentTabComponent 可以包括:

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

你也可以使用 is attribute 来创建常规的 HTML 元素。

解析DOM模板时的注意事项

在 DOM 中直接书写 Vue 模板,Vue 将不得不从 DOM 中获取字符串。这会因为浏览器的原生 HTML 解析行为而导致一些小问题。

下面讨论的限制仅适用于直接在 DOM 中编写模板的情况。它们不适用于以下来源的字符串模板:

  • 字符串模板 (比如 template: '...')
  • 单文件组件
  • <script type="text/x-template">

元素位置受限

有些 HTML 元素,诸如 <ul><ol><table><select>,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li><tr><option>,只能出现在其它某些特定的元素内部。

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

这个自定义组件 <blog-post-row> 会被作为无效的内容提升到外部,并导致最终渲染结果出错。我们可以使用特殊的 is attribute 作为一个变通的办法:

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

当它用于原生 HTML 元素时,is 的值必须以 vue: 开头,才可以被解释为 Vue 组件。这是避免和原生自定义元素混淆。

大小写不敏感

HTML attribute 名不区分大小写,因此浏览器将所有大写字符解释为小写。这意味着当你在 DOM 模板中使用时,驼峰 prop 名称和 event 处理器参数需要使用它们的 kebab-cased (横线字符分隔) 等效值:

//  在 JavaScript 中是驼峰式
app.component('blog-post', {
  props: ['postTitle'],
  template: `
    <h3>{{ postTitle }}</h3>
  `
})
<!-- 在 HTML 中则是横线字符分割 -->
<blog-post post-title="hello!"></blog-post>

结语

专栏同步代码:Github

掘金社区:跟我一起学Vue3

作者简介:

一个满脑子奇怪知识的小商同学,在校ing,懂点设计,懂点排版,为成为一名优秀的前端工程师而努力。