组件
组件是Vue.js最强大的功能之一,单文件组件、组件库、Vuex等等概念都是建立在组件的基础上的。
一、组件注册的简单知识
1、如何注册子组件
Vue.component('my-component-name', { /* ... */ })
组件名就是my-component-name
2、组件命名
驼峰命名 kebab-case
Vue.component('my-component-name', { /* ... */ })
首字母大写命名 PascalCase
Vue.component('MyComponentName', { /* ... */ })
但是这里我们注意,在js里是不分大小写的,所以直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。
3、全局注册和局部注册
全局注册:
全局组件注册后,任何vue实例都可以用
<div id="example">
<!-- 2、 组件使用 组件名称 是以HTML标签的形式使用 -->
<my-component></my-component>
</div>
<script>
// 注册组件
// 1、 my-component 就是组件中自定义的标签名
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
// 创建根实例
new Vue({
el: '#example'
})
</script>
局部注册:
只能在当前注册的vue实例中使用
<div id="app">
<my-component></my-component>
</div>
<script>
// 定义组件的模板
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
//局部注册组件
components: {
// <my-component> 将只在父模板可用 一定要在实例上注册了才能在html文件中使用
'my-component': Child
}
})
</script>
另:组件注意事项:
- 组件参数的data值必须是函数,同时这个函数要求返回一个对象(
简单解释一下,如果data是一个对象,如果有多个组件的情况,data的属性是共享的,所以data必须是一个函数) ,像这样
data(){
return{
firstName:'cocoa',
lastName:'jia'
}
}
- 组件模板必须是单个根元素
(这里加一个拓展阅读内容,vue为什么要求组件模板只能有一个根元素) - 组件模板template的内容可以是模板字符串
二、组件传值
组件直接传分三块,父传子,子传父,兄弟之间互传。
1、首先定义根组件 app.vue
<div id="app"></div>
var vm = new Vue({
el: "#app",
});
2、父组件以属性的形式绑定值到子组件身上,子组件通过props接收:
<div id="app">
<div>{{pmsg}}</div>
<!--1、menu-item 在 APP中嵌套着 故 menu-item 为 子组件 -->
<!-- 给子组件传入一个静态的值 -->
<menu-item title='来自父组件的值'></menu-item>
<!-- 2、 需要动态的数据的时候 需要属性绑定的形式设置 此时 ptitle 来自父组件data 中的数据 .
传的值可以是数字、对象、数组等等
-->
<menu-item :title='ptitle' content='hello'></menu-item>
</div>
<script type="text/javascript">
Vue.component('menu-item', {
// 3、 子组件用属性props接收父组件传递过来的数据
props: ['title', 'content'],
data: function() {
return {
msg: '子组件本身的数据'
}
},
template: '<div>{{msg + "----" + title + "-----" + content}}</div>'
});
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件中内容',
ptitle: '动态绑定属性'
}
});
3、子传父
子传父emit在vue官网上说得比较复杂,首先子组件通过调用内建的 $emit 方法并传入事件名称来触发一个事件,使用事件抛出一个值(就是参数),在组件上使用 v-model绑定等,总结起来其核心步骤就两步
- 子组件里
@click='$emit("enlarge-text", 5)
去触发事件 - 父组件通过
@enlarge-text="handle($event)
去监听
<div id="app">
<div :style='{fontSize:fontSize + "px"}'>{{pmsg}}</div>
// 2、enlarge-text绑定了handle事件处理函数
<child-component @enlarge-text="handle($event)"></child-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component("child-component", {
props: [],
data: function() {
return {};
},
//1 触发click事件,实际上是用$emit去触发事件enlarge-text
template: `
<button @click='$emit("enlarge-text", 5)'>扩大字号</button>
`,
});
new Vue({
el: "#app",
data: {
pmsg: "父组件的原生内容",
fontSize: 10,
},
methods: {
// 3 调用handle函数
handle: function(val) {
this.fontSize += val;
},
},
});
</script>
4、兄弟传值
我真正觉得麻烦的是兄弟组件之间的传值,需要通过事件中心传递(但是其实后面学了vuex,就感觉就简化了很多)
兄弟传值第一种方式,是利用上面学的父传子 子传父,先把要传的值放到父组件里,再由子组件通过props去取。
第二种方式是建立一个vue事件中心,在全局建立一个空的vue对象,然后将事件绑定到该空对象上,最后通过该空对象来触发$on监听的事件
看起来比较麻烦哈,其实核心就是在method里hub.$emit()
,
在mounted()里接收hub.$on()
var hub = new Vue();
//tom component
Vue.component("test-tom", {
data: function() {
return { num: 0 };
},
template: `
<div>
<div>Tom:{{num}}</div>
<div>
<button @click="handle">点击</button>
</div>
</div>
`,
methods: {
// 传递
handle: function() {
hub.$emit("JERRY-EVENT", 2);
},
},
// 接收
mounted: function() {
hub.$on("TOM-EVENT", (val) => {
this.num += val;
});
},
});
//jerry component
Vue.component("test-jerry", {
data: function() {
return { num: 1 };
},
template: `
<div>
<div>jerry:{{num}}</div>
<div>
<button @click="handle">点击</button>
</div>
</div>
`,
methods: {
//传递
handle: function() {
hub.$emit("TOM-EVENT", 2);
},
},
//接收
mounted: function() {
hub.$on("JERRY-EVENT", (val) => {
this.num += val;
});
},
});
var vm = new Vue({
el: "#app",
data: {},
methods: {
handle: function() {
//hub.$off 销毁
// hub.$off("tom-event");
// hub.$off("jerry-event");
},
},
});
三 、组件插槽 slot
插槽简单解释就是,父组件传内容(区别传值,传值就是上面的props$emit)给子组件。插槽需要借助的就是关键词slot,简单看分三种,匿名插槽,具名插槽,和作用域插槽。
1、匿名插槽
用<slot>默认内容</slot>
确定子组件插入的位置,在父组件里定义要传送的内容,如下:
<div id="app">
<child-component>父组件内容</child-component>
<child-component></child-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component("child-component", {
template: `
<div>
//当组件渲染的时候,这个 <slot> 元素将会被替换为“组件标签中嵌套的内容”,如果父组件没有内容,则会显示slot里面的默认内容
<slot>默认内容</slot>
</div>
`,
});
new Vue({
el: "#app",
});
2、具名组件
子组件的模版里用 <slot name='name'>
绑定元素,来指定当前插槽的名字,父组件里用<template slot="name">
来匹配,如果模版里name的值和父组件里slot的值相等,就渲染父组件的内容。插槽的顺序完全取决于模版,如果name没匹配上,则渲染匿名插槽。
<div id="app">
<child-component>
// 通过slot属性指定,比较slot的值和name里的值
<template slot="context">父组件内容</template>
</child-component>
<!-- <child-component></child-component> -->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component("child-component", {
data: function() {
return {};
},
template: `
<div>
// 在slot里通过属性'name'绑定元素
<slot name='context'>默认内容</slot>
</div>
`,
});
new Vue({
el: "#app",
data: {},
});
</script>
3、作用域插槽
好了,本文最后一个知识点来了,学完我们就结束了组件和插槽的基础知识。之所以叫做”作用域“插槽,是因为模板虽然是在父级作用域中渲染的,却能拿到子组件的数据。
自 2.6.0 起有所更新。使用 slot-scope attribute 的语法已废弃。vue官网上不是很好理解,我找到一篇解释很详细的文章[Vue核心技术-22,作用域插槽],对文章我加了一些自己的理解,老样子,先来个简单的栗子:
<div id="app">
<child-component>
<template scope="props">
<p>作用域插槽传值 : {{ props.msg }}</p>
</template>
</child-component>
</div>
<script type="text/javascript">
Vue.component("child-component", {
template:
'<div class="container">' +
'<slot msg="作用域插槽message!"></slot>' +
"</div>",
});
var vm = new Vue({
el: "#app",
});
</script>
在模版中,有一个scope="props"
写法(props是任意取的名字),通过{{ props.lastname }} 可以取到子组件中传递的latename的值。
再看一个复杂的栗子, 过程可以根据代码的备注来理解。
<div id="app">
// 2、通过模版,将books传递进来
<my-list :books1="books">
<template slot="book" scope="props">
// 5、遍历、展示
<li>书名:{{ props.bookName}}--售价:{{ props.bookPrice }}</li>
</template>
</my-list>
</div>
<script type="text/javascript">
Vue.component('my-list',{
//3、 在子组件里,用props接收
props:['books1'],
template:
'<ul>' +
'<slot name="book" ' +
//4、结合v-for,使用:book-name和:book-price绑定并输出数组对象的属性值 ,通过slot暴露给外部插槽
'v-for="book in books" ' +
':book-name="book.name" ' +
':book-price="book.price">' +
'</slot>' +
'</ul>'
});
var vm = new Vue({
el: '#app',
// 1、在根组件中,data里有个books的数组
data:{
books: [
{name: 'Vue.js', price:50},
{name: 'Javascript', price:30},
{name: 'Css', price:40},
{name: 'Html', price:60}
]
}
})
</script>
总结一下本文的所有内容