Vue 组件和插槽
Vue 组件组件是一个可复用的 Vue 实例且带有一个名字,所以要接收和 new Vue 相同的选项,例如 data 、methods 、computed 以及生命钩子函数等。注意:组件是一个全新的 Vue 实例,组件内部的选项不会相互影响。但是 data: {} 除外,因为如果 data 是一个对象,就不会构成作用域,每个组件的 data 都是共用一个存储地址,会互相影响。这时需要使用 data
Vue 组件
组件是一个可复用的 Vue 实例且带有一个名字,所以要接收和 new Vue
相同的选项,例如 data
、methods
、computed
以及生命钩子函数等。
注意:
- 组件是一个全新的 Vue 实例,组件内部的选项不会相互影响。但是
data: {}
除外,因为如果data
是一个对象,就不会构成作用域,每个组件的data
都是共用一个存储地址,会互相影响。这时需要使用data() { return {} }
的形式来形成作用域。 - 组件必须只有一个根元素
- 由于 HTML 不区分大小写,会把所有字母解释为小写,所以组件名中不要使用驼峰命名,需要使用短横线分割命名(直接引用 vue.js 方式使用 Vue 会有此问题,使用脚手架没事,因为 webpack 会进行预处理)
全局注册组件
Vue.component('my-component', {
data() {
return {
count = 0
}
},
methods: {
add() {
this.count++
}
},
template: '<button @click="add">点击了{{ count }}次</button>'
})
局部注册组件
let componentA = {
data() {
return {
count = 0
}
},
methods: {
add() {
this.count++
}
},
template: '<button @click="add">点击了{{ count }}次</button>'
}
new Vue({
el: '#app',
components: {
'my-component': componentA
}
})
组件传值
父向子传值
使用子组件的 props
属性,prop 是可以在组件上自定义的一些 attribute
,当一个值传递给一个 prop 时,他就变成了组件实例上的一个 property
,我们可以通过 props
属性定义这些 prop ,可以像使用 data
中的 property
一样来使用 prop。
// 注册组件
Vue.component('myButton', {
props: ['title'],
data() {
return {
count: 0
}
},
template: '<button @click=" count++ ">{{ title }}被点击了{{ count }}次</button>'
})
// 使用
<myButton title="按钮"></myButton>
HTML 中的 attribute
是不区分大小写的,因为浏览器会把所有的大写字符解释为小写,所以当我们通过驼峰命名法命名的 prop 在使用时,需要使用短横线分割命名:
// 注册组件
Vue.component('myButton', {
props: ['postTitle'],
data() {
return {
count: 0
}
},
template: '<button @click=" count++ ">{{ postTitle }}被点击了{{ count }}次</button>'
})
// 使用
<myButton post-title="按钮"></myButton>
通常我们会使用字符串数组的形式罗列出 props
:
props: ['title', 'likes', 'author']
除此之外,我们还可以对 prop 进行验证
props: {
// 验证数据类型
title: String,
// 多个类型可能
likes: [Number, Boolean, Function],
// 必填字符串
author: {
type: String,
required: true
},
// 带有默认数字
commentIds: {
type: Number,
deafult: 10
},
// 带有默认对象
isPublished: {
type: Object,
// 对象或数组的默认值,必须从一个工厂函数中获取
default() {
return { message: 'Hello' }
}
},
// 自定义验证函数
callback: {
validator(value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
},
contactsPromise: Promise // or any other constructor
}
在进行父子传值时,值得注意的是:父向子传值是一个单向数据流,即子组件中不能直接修改 prop 的值,这样做的目的是,防止修改值后会影响父组件的其他子组件的同名 prop 值。因为,每当父组件中的值发生变化时,子组件中的所有 prop 都会刷新为最新的值。
如果需要更改 prop 的值,推荐使用以下两种方法:
// 方法一:赋值为自己的属性
props: ['postTitle'],
data() {
return {
title: this.postTitle
}
}
// 方法二:使用计算属性
props: ['postTitle'],
computed: {
title() {
return this.postTitle.trim()
}
}
如果父组件传入了一个自定义 attribute
,但是子组件中的 props
中并没有声明,那么这个自定义的 attribute
和值将会自动放到子组件的根标签上。对于绝大多数的 HTML 定义的 attribute
来说,外部传入的值会替换掉子组件中原有的值,但是 class
和 style
除外,他们会合子组件上的同名 attribute
进行合并。
子向父传值
通过 $emit()
在子组件中触发父组件中在子组件身上注册的事件,并传入数据
注意:$emit()
中的事件名不能含有大写字母,需要使用小写字母(直接引用 vue.js 方式使用 Vue 会有此问题,使用脚手架没事)
<!-- 父组件 -->
<template>
<div>
<p>父组件城市:{{ city }}</p>
<button @click="changeCity">父组件按钮,点击切换至上海</button>
<sonComponent @clickSon="clickSonBtn" :city="city"></sonComponent>
</div>
</template>
<script>
import sonComponent from "@/components/son.vue";
export default {
data() {
return {
city: "北京"
};
},
methods: {
changeCity() {
this.city = "上海";
},
clickSonBtn(value) {
console.log(value);
this.city = value;
}
},
components: {
sonComponent
}
};
</script>
<!-- 子组件 -->
<template>
<div>
<p>子组件城市:{{ city }}</p>
<button @click="changeCity">子组件按钮,点击切换到广州</button>
</div>
</template>
<script>
export default {
props: ["city"],
methods: {
changeCity() {
this.$emit("clickSon", "广州");
}
}
};
</script>
插槽
Vue 允许在组件的标签中间带入其他元素,但前提是要在组件中要定义 slot
插槽,如果组件中的 template
中没有一个 slot
,那么该组件的起始标签和闭合标签中间的内容都会被舍弃。
<!-- 组件 my-link 的 template -->
<template>
<a :href="url" class="nav-link">
<slot></slot>
</a>
</template>
<!-- 使用组件 -->
<my-link url="xxx">插槽内容</my-link>
编译时,【插槽内容】将会替换掉 slot
标签,此外,插槽内容和模板的其他地方一样,可以访问组件外部的 property
,但不能访问组件内部的作用域。因为,父级模板里的所有内容都是在父级作用域里编译的,子集模板里的内容都在子级作用域中编译。但是我们可以将组件内部的内容通过绑定 attribute
的方式传到父级,绑定到 slot
上的 attribute
被称为插槽 prop,在父级作用域中,可以通过带值的 v-slot 来定义包含所有 prop 的对象的名字。
<!-- 子组件 -->
<template>
<div>
<slot :sonName="name"></slot>
</div>
</template>
<script>
export default {
data() {
name: '张三'
}
}
</script>
<!-- 父组件 -->
<template>
<div>
<son-info>
<template v-slot="sonInfo"> {{ sonInfo.sonName }} </template> // 张三
</son-info>
<son-info> {{ name }} </son-info> // 李四
</div>
</template>
<script>
export default {
data() {
name: '李四'
}
}
</script>
插槽中可以设置后备内容,即默认内容,例如:<slot> 默认内容 </slot>
,当组件起始标签和闭合标签之前没有值的时候,会展示后备内容
有时我们需要多个插槽,例如:
<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
</footer>
</div>
对于这种情况,我们需要给 slot
标签新增一个属性 name
,用来标记插槽的名字,没有 name
属性的 slot
有一个默认的名字 default
,在使用插槽时,需要通过 v-slot
来指定要替换的插槽,缩写为 #
<!-- 子组件 -->
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
<!-- 父组件 -->
<component>
<template v-slot:header>
<p>我来组成头部</p>
</template>
<p>我是main1</p>
<p>我是main2</p>
<template #footer>
<p>我来组成尾部</p>
</template>
</component>
更多推荐
所有评论(0)