前言
⭐️ 本专栏主要内容为「 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,懂点设计,懂点排版,为成为一名优秀的前端工程师而努力。