文章目录
Vue
三、Vue组件
01.组件注册
(1)组件基本概念
- 基本方法:
Vue.component("组件名称",{})
- 第一个参数是标签名称,第二个参数是一个选项对象
- 组件注意事项:
- 组件参数的data值必须是函数,同时这个函数要返回一个对象
- 组件模板必须是单个根元素
- 组件模板的内容可以是模板字符串
- 如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,在普通的标签模板中,必须使用短横线的方式使用组件
(2)全局组件注册
<div id="app">
<!--
组件可以重复使用多次,因为data中返回的是一个对象所以每个组件中的数据是私有的
即每个实例可以维护一份被返回对象的独立的拷贝
-->
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
<!-- 驼峰命名方式注册的组件,在普通标签模板中,必须使用短横线的方式使用组件,因为HTML不区分大小写 -->
<hello-world></hello-world>
</div>
<script>
// 如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,
// 但是在普通的标签模板中,必须使用短横线的方式使用组件
Vue.component('HelloWorld', {
// data值必须是函数,同时这个函数要求返回一个对象
data: function(){
return {
msg: 'HelloWorld'
}
},
// 必须是单个根元素
template: '<div>{{ msg }}</div>'
});
Vue.component('button-counter', {
data: function(){
return {
count: 0
}
},
// 组件模板的内容可以是模板字符串,在字符串模板中可以使用驼峰命名的方式使用组件
template: `
<div>
<button @click="handle">点击了{{count}}次</button>
<HelloWorld></HelloWorld>
</div>
`,
methods: {
handle: function(){
this.count += 2;
}
}
})
let vm = new Vue({
el: '#app',
data: {}
});
</script>
(3)局部组件注册
<div id="app">
<my-component></my-component>
</div>
<script>
// 定义组件的模板
var Child = {
template: '<div>A custom component!</div>'
}
let vm = new Vue({
el: "#app",
// 注册局部组件
components: {
// <my-component> 将只能在父模板可用,一定要在实例上注册了才能在html文件中使用
'my-component': Child
}
})
</script>
02.组件传参
(1)父组件向子组件传值
-
组件内部通过
props
接收传递过来的值 -
父组件通过属性将值传递给子组件
-
父组件发送的形式是以属性的形式绑定值到子组件身上
-
然后在子组件中用属性
props
接收如果在
props
中使用驼峰形式接收参数,那么在模板中需要使用短横线的形式,字符串模板中没有这个限制
<div id="app"> <div>{{pmsg}}</div> <!-- 1、menu-item 在#app中嵌套,故 menu-item 为子组件 --> <!-- 给子组件传入一个静态的值然后再子组件的注册中,用props属性接受这个值 --> <menu-item title='来自父组件的值'></menu-item> <!-- 2、可以动态绑定属性,此时ptitle是来自父组件data中的数据,传的值可以是数字、对象、数组等等 --> <menu-item :title='ptitle' content='hello' menu-title='短横线'></menu-item> </div> <script> Vue.component('menu-item', { // 3、 子组件用属性props接收父组件传递过来的数据 props: ['title', 'content', 'menuTitle'], data: function() { return { msg: '子组件本身的数据' } }, template: '<div>{{msg + "----" + title + "-----" + content + menuTitle}}</div>' }); let vm = new Vue({ el: '#app', data: { pmsg: '父组件中内容', ptitle: '动态绑定属性' } }); </script>
-
(2)子组件向父组件传值
-
props
传递数据原则:单向数据流 -
子组件通过自定义事件向父组件传递消息
- 子组件用
$emit("自定义事件名称",传递的数据)
触发事件
- 子组件用
-
父组件监听子组件的事件
- 父组件用
v-on
监听子组件的事件
<div id="app"> <div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div> <!-- 在父组件上用v-on 监听子组件的事件 这里 enlarge-text 是子组件中的 $emit 自定义事件中的第一个参数 对应 handle 为父组件对应的事件处理函数,即Vue实例中的事件处理函数 --> <!-- enlarge-text 表示子组件中自定义事件,$event 表示子组件传递过来数据的值 --> <menu-item @enlarge-text='handle($event)'></menu-item> </div> <script> // 子组件向父组件传值-携带参数 Vue.component('menu-item', { // 子组件用$emit()触发事件 // 第一个参数为自定义的事件名称,第二个参数为需要传递的数据 template: ` <div> <button @click='$emit("enlarge-text", 5)'>扩大父组件中字体大小</button> <button @click='$emit("enlarge-text", 10)'>扩大父组件中字体大小</button> </div> ` }); var vm = new Vue({ el: '#app', data: { pmsg: '父组件中内容', fontSize: 10 }, methods: { handle: function(val){ this.fontSize += val; } } }); </script>
- 父组件用
(3)非父子组件间的传值
-
需要通过单独的事件中心管理组件间的通信:
let eventHub = new Vue()
-
监听事件与销毁事件:
$on()
、$off()
-
触发事件:
$emit()
<div id="app"> <div>父组件</div> <div> <button @click='handle'>销毁事件</button> </div> <test-A></test-A> <test-B></test-B> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> //1、 提供事件中心,一个空Vue实例 var hub = new Vue(); Vue.component('test-A', { data: function(){ return { num: 0 } }, template: ` <div> <div>A:{{num}}</div> <div> <button @click='handle'>点击</button> </div> </div> `, methods: { handle: function(){ //2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据) 触发兄弟组件的事件 hub.$emit('B-event', 2); } }, mounted: function() { // 3、接收数据方,通过mounted(){} 钩子中 触发hub.$on(方法名 hub.$on('A-event', (val) => { this.num += val; }); } }); Vue.component('test-B', { data: function(){ return { num: 0 } }, template: ` <div> <div>B:{{num}}</div> <div> <button @click='handle'>点击</button> </div> </div> `, methods: { handle: function(){ //2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据) 触发兄弟组件的事件 hub.$emit('A-event', 1); } }, mounted: function() { // 3、接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名 hub.$on('B-event', (val) => { this.num += val; }); } }); var vm = new Vue({ el: '#app', data: { }, methods: { handle: function(){ //4、销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据 hub.$off('A-event'); hub.$off('B-event'); } } }); </script>
- 传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)
- 接收数据方,通过mounted(){} 钩子中,触发hub.$on()方法名
- 销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
(4)ref
-
ref
放在标签上,可以获得原生节点 -
ref
放在组件上,可以获得组件对象<div id="app"> <div ref="mytext"> </div> <my-component ref="myComponent"></my-component> <button @click="handle">点击</button> </div> <script> Vue.component("my-component",{ template: "", methods: { add: function(){ } } }) let vm = new Vue({ methods: { handle: function(){ console.log(this.$refs.mytext); // 可以直接访问子组件中的方法 console.log(this.$refs.myComponent.add()); } } }); </script>
03.动态组件
-
Vue
提供了一个内置组件<component>
,可以通过is
属性动态绑定组件 -
通过
<keep-alive>
保持活性,即保留页面内容,避免再次渲染<keep-alive> <component :is="who"></component> </keep-alive> <!-- 通过改变who值——组件名,来切换组件显示 -->
04.组件插槽
(1)作用
- 在HTML的父组件标签中,向子组件传递内容
(2)基本用法
-
插槽位置:使用
<slot>
标签来预留位置- 【注意】:如果没有
<slot>
标签,那么插槽内容无效
- 【注意】:如果没有
-
插槽内容
<div>
<!-- alert-box是父组件,插槽内容是插入父组件内部定义的模板中 -->
<alert-box>这个就是插槽内容</alert-box>
</div>
<script>
Vue.component('alert-box', {
// 当组件渲染的时候,这个 <slot> 元素将会被替换为 “组件标签中嵌套的内容”
// 但如果没有这个 <slot> 元素,那么组件标签内部嵌套的内容无效
template: `
<div>
<slot>默认内容</slot>
</div>
`
});
</script>
(3)具名插槽
- 含义:即具有名字的插槽,需要使用
<slot>
中的name
属性来绑定元素,从而在指定了名字的插槽中插入内容- 而没有name属性的
<slot>
标签具有隐含的name属性,即name=default
,同时没有绑定名字的内容都会被传入默认插槽中 - 在向具名插槽传入内容的时候,需要在
<template>
元素上使用v-slot
指令 v-slot
可以简写为#
,但后面必须有参数,如:#header
、#default
- 而没有name属性的
<div id="app">
<base-layout>
<!--
通过v-slot指令来匹配具名插槽, 这个v-slot的值必须和下面slot组件的name值对应上
如果没有匹配到,则放到匿名的插槽中,即没有name属性的插槽
template临时的包裹标签最终不会渲染到页面上
-->
<template v-slot:header>
<p>标题信息1</p>
<p>标题信息2</p>
</template>
<!-- 这个标签没有指定v-slot指令,那么这里的内容会被插入匿名插槽中,即没有指定name属性的插槽 -->
<p>主要内容1</p>
<p>主要内容2</p>
<!-- 或者v-slot指令也可以用slot属性来指定,即 slot="...",已废弃 -->
<template slot='footer'>
<p>底部信息信息1</p>
<p>底部信息信息2</p>
</template>
</base-layout>
</div>
<script>
// 具名插槽
Vue.component('base-layout', {
// 使用 <slot> 中的 "name" 属性,指定当前插槽的名字
// 如果 <slot> 元素没有 name 属性,则隐含带有 name=default
// 具名插槽的渲染顺序,完全取决于【模板】的顺序,而不是取决于父组件中元素的顺序
template: `
<div>
<header>
<slot name='header'></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name='footer'></slot>
</footer>
</div>
`
});
</script>
(4)作用域插槽
- 父组件对子组件加工处理,让插槽内容访问子组件的数据
- 既可以复用子组件的slot,又可以使slot内容不一致
<div id="app">
<current-user>
<!--
父组件中,在<template>元素上,使用v-slot指令传递数据,v-slot:default="slotProps",
slotProps在这里只是临时变量,以接收子组件在插槽上传递过来的数据
这里是子组件上的 info 数据传入父组件中,存储在slotProps对象中
-->
<template v-slot:default='slotProps'>
{{slotProps.info.name}}
</template>
</current-user>
</div>
<script>
// 作用域插槽
Vue.component('current-user', {
props: ['list'],
// 在子组件模板中,<slot>元素上有一个类似props传递数据给组件的写法 :msg="xxx",
// 这样,在使用组件时的HTML页面内,即父组件中,也可以访问到这个item数据
// 插槽可以提供一个默认内容,如果如果父组件没有为这个插槽提供内容,会显示默认的内容。
// 如果父组件为这个插槽提供了内容,则默认的内容会被替换掉
template: `
<div>
<slot :info='item'>
{{item.name}}
</slot>
</div>
`
});
</script>
(未完待续~~~)