系列文章目录
第六章 自定义组件
文章目录
- 系列文章目录
- 一、定义属性
- 二、自定义事件
- 三、定义v-model
- 四、插槽
- 1. 基本使用:
- 2. 具名插槽:
一、定义属性
有时候我们定义一个组件后,需要让调用者传入参数,那么就可以将参数定义成属性。示例代码如下:
Person组件代码:
<script setup name="Person">javascript">
// Person组件
import {defineProps} from "vue";// 1. 使用数组的形式
//const props = defineProps(['username'])
// 2. 使用对象的形式
const props = defineProps({gender: {type: String,default: '男'},username: String,age: Number,body: {height: Number,weight: Number}
})</script><template>
<p>用户名:{{ props.username }},年龄:{{ props.age }},身高:{{ props.body.height }},体重:{{ props.body.weight }}
</p>
</template>
App.vue组件代码:.
<script setup>javascript">
import Person from "./components/Person.vue"
</script><template>
<Person :age="18" username="张三" :body="{height: 180, weight: 140}"></Person>
</template>
子组件不能修改父组件传过来的props ,否则会引起错误。
二、自定义事件
子组件可以自定义事件,然后父组件可以实现发生这个事件的处理函数。示例代码如下:
Person.vue组件:
<script setup name="Person">javascript">
import {defineEmits, ref} from "vue";let steps = ref(0);const emit = defineEmits(['change']);const onUpdate = () => {steps.value += 10emit('change', steps.value);
}</script><template>
<button @click="onUpdate">行走10步</button>
</template>
App.vue组件:
<script setup>javascript">
import Person from "./components/Person.vue"const onPersonChange = (steps) => {console.log("步行了:", steps);
}
</script><template>
<Person @change="onPersonChange"></Person>
</template>
三、定义v-model
使用v-model 可以定义一个在父组件和子组件中双向绑定的对象。示例代码如下:
Person.vue组件代码:
<script setup name="Person">javascript">
import {defineModel, watch} from "vue";let steps = defineModel();
const onUpdate = () => {steps.value += 10;
}
watch(steps, (newValue, oldValue) => {console.log("Person中监听到steps:", newValue);
})</script><template>
<button @click="onUpdate">行走10步</button>
</template>
App.vue组件代码:
<script setup>javascript">
import {ref, watch} from "vue";
import Person from "./components/Person.vue"let steps = ref(0);
watch(steps, (newValue, oldValue) => {console.log("App中监听到的:", newValue);
})
</script><template>
<Person v-model="steps"></Person>
</template>
四、插槽
插槽(Slots)在Vue中是一种非常重要的内容组织方式,它能够让你在子组件中留下预先定义的位置,然后在父组件中填充这些位置。
1. 基本使用:
示例代码如下:
SubmitButton.vue代码:
<template>
<button type="submit"><slot></slot>
</button>
</template>
App.vue代码:
<template>
<SubmitButton>登录</SubmitButton>
</template>
“登录”这个文本就会填充在SubmitButton的slot标签处。或者也可以在定义插槽的时候指定默认值,比如:
// SubmitButton.vue代码
<template>
<button type="submit"><slot>提交</slot>
</button>
</template>
那么以后在使用SubmitButton组件的时候,使用 也会有提交两个文字。
2. 具名插槽:
如果在一个组件中想要定义多个插槽,则可以使用“具名”插槽,就是给每个插槽指定一个名称,父组件使用的时候,也要指明相应代码要放到哪个插槽下,比如我们定义一个BaseLayout 组件:
<div class="container"><header><!-- 标题内容放这里 --></header><main><!-- 主要内容放这里 --></main><footer><!-- 底部内容放这里 --></footer>
</div>
其中header 、main 、footer 是由父组件自由指定的,那么组件的代码可以修改为:
<div class="container"><header><slot name="header"></slot></header><main><slot></slot></main><footer><slot name="footer"></slot></footer>
</div>
其中有三个插槽,分别是header、默认的插槽、以及footer插槽。那么在父组件中可以按照如下方式使用:
<BaseLayout><template #header><h1>Here might be a page title</h1></template><template #default><p>A paragraph for the main content.</p><p>And another one.</p></template><template #footer><p>Here's some contact info</p></template>
</BaseLayout>
对于default插槽,可以不用指明名称,因此也可以如下使用:
<BaseLayout><template #header><h1>Here might be a page title</h1></template><!-- 隐式的默认插槽 --><p>A paragraph for the main content.</p><p>And another one.</p><template #footer><p>Here's some contact info</p></template>
</BaseLayout>
- 插槽作用域:
在父组件中,如果使用插槽,则插槽中的代码是无法访问子组件中的变量的。如果想要访问,那么必须在定义插槽的时候显示指定。示例代码如下:
<div class="container"><header><slot name="header" :tab="1"></slot></header><main><slot :card="2"></slot></main><footer><slot name="footer" :box="3"></slot></footer>
</div>
BaseLayout.vue组件代码:
<div class="container"><header><slot name="header" :tab="1"></slot></header><main><slot :card="2"></slot></main><footer><slot name="footer" :box="3"></slot></footer>
</div>
App.vue组件代码:以后在父组件中,可以通过以下方式,获取到子组件中传递过来的插槽信息:
<BaseLayout><template #header="headerProps">{{ headerProps }}</template><template #default="defaultProps">{{ defaultProps }}</template><template #footer="footerProps">{{ footerProps }}</template>
</BaseLayout>