20250307-vue组件基础1
简介
组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成一个层层嵌套的树状结构:
这和我们嵌套 HTML 元素的方式类似,Vue 实现了自己的组件模型,使我们可以在每个组件内封装自定义内容与逻辑。
定义一个组件
当使用构建步骤时,我们一般会将 Vue 组件定义在一个单独的 .vue 文件中,这被叫做单文件组件,简称 SFC:
<script>
export default {
data() {
return {
count: 0
}
}
}
</script>
<template>
<button @click="count++">点击了 {{ count }} 次.</button>
</template>
要使用一个子组件,我们需要在父组件中导入它。假设我们把计数器组件放在一个叫做 ButtonCounter.vue 的文件中,这个组件将会以默认到处的形式被暴露给外部。
<template>
<h1>子组件</h1>
<ButtonCounterVue />
</template>
<script>
import ButtonCounterVue from './ButtonCounter.vue';
export default {
data() {
return {
}
},
components: {
ButtonCounterVue
}
}
</script>
<style>
</style>
若要将导入的组件暴露给模板,我们需要在 components 选项上注册它。这个组件将会以其注册时的名字作为模板中的标签名。
当然,你也可以全局地注册一个组件,使得它在当前应用中的任何组件上都可以使用,而不需要额外再导入。
在单文件组件中,推荐为子组件使用 PascalCase 的标签名,以此来和原生 HTML 元素作区分。Vue 单文件组件是可以在编译中区分大小写的。
传递props
Prop 即是 property 属性的缩写。
Props 是一种特别的 attributes,你可以在组件上声明注册,在组件的 props 列表上声明属性。这里要用到 props 选项:
<!-- BlogPost.vue -->
<template>
<h4>{{title}}</h4>
</template>
<script>
export default {
props:['title']
}
</script>
<style>
</style>
当一个值被传递给 prop 时,它将成为该组件实例上的一个属性。该属性的值可以像其他组件属性一样,在模板和组件的 this 上下文中访问。
一个组件可以有任意多的 props,默认情况下,所有 prop 都接受任意类型的值。
当一个 prop 被注册后,可以像这样以自定义 attribute 的形式传递数据给它:
<template>
<BlogPost title="My journey with Vue" />
<BlogPost title="Blogging with Vue" />
<BlogPost title="Why Vue is so fun" />
</template>
<script>
import BlogPost from './BlogPost.vue';
export default {
data() {
return {
}
},
components: {
BlogPost
}
}
</script>
<style>
</style>
在实际应用中,我们可能在父组件中会有如下的一个文章数组:
export default {
// ...
data() {
return {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
}
}
这种情况下,我们可以使用 v-for
来渲染它们:
<BlogPost
v-for="post in posts"
:key="post.id"
:title="post.title"
/>
监听事件
有时候子组件需要与父组件进行交互。例如,博客文章的文字能够放大,而页面其余部分仍使用默认字号。
在父组件中,我们可以添加一个 postFontSize 数据属性来实现这个效果:
export default {
data() {
return {
posts:[
],
postFontSize:1
}
},
components: {
BlogPost
}
}
在模板中用它来控制所有博客文章的字体大小:
<template>
<div :style="{fontSize: postFontSize + 'em'}">
<BlogPost v-for="post in posts"
:key="post.id"
:title="post.title"
/>
</div>
</template>
然后,给 <BlogPost> 组件添加一个按钮:
<template>
<h4>{{title}}</h4>
<button>字体放大</button>
</template>
这个按钮目前还没有做任何事情,我们要想要点击这个按钮来告诉父组件它应该放大所有博客文章的文字。要解决这个问题,组件实例提供了一个自定义事件系统。父组件可以通过 v-on 或 @ 来选择性地监听子组件上抛的事件,就像监听原生 DOM 事件那样:
<template>
<div :style="{fontSize: postFontSize + 'em'}">
<BlogPost v-for="post in posts"
:key="post.id"
:title="post.title"
@enlarge-text="postFontSize += 0.1"
/>
</div>
</template>
子组件可以通过调用内置的 $emit 方法,通过传入事件名称来抛出一个事件:
<template>
<h4>{{title}}</h4>
<button @click="$emit('enlarge-text')">字体放大</button>
</template>
因为有了 @enlarge-text="postFontSize += 0.1" 的监听,父组件会接收这一事件,从而更新 postFontSize 的值。
父组件代码:
<template>
<div :style="{fontSize: postFontSize + 'em'}">
<BlogPost v-for="post in posts"
:key="post.id"
:title="post.title"
@enlarge-text="postFontSize += 0.1"
/>
</div>
</template>
<script>
import BlogPost from './BlogPost.vue';
export default {
data() {
return {
posts:[
{id:1,title:"标题1"},
{id:2,title:"标题2"},
{id:3,title:"标题3"},
],
postFontSize:1
}
},
components: {
BlogPost
}
}
</script>
<style>
</style>
我们可以通过 emits 选项来声明需要抛出的事件:
<script>
export default {
props:['title'],
emits:['enlarge-text']
}
</script>
这声明了一个组件可能触发的所有事件,还可以对事件的参数进行验证。同时,这还可以让 Vue 避免将它们作为原生事件监听器隐式地应用于子组件的根元素。