1、插槽
(1)插槽:slot
//设计组件
<div class="content1">
<slot></slot>
<h2>{{contentData.title}}</h2>
</div>
//使用组件
<Content1 :contentData="arr[0]">
<p>我是插槽插进去的数据</p>
</Content1>
(2)具名插槽:v-slot:插槽名 或 #插槽名
slot,slot-scope过时了 2.6.0使用v-slot
没有指定插槽名就是默认插入到插槽,不给插槽插入数据的话,就会使用组件的slot中的数据 插槽名不用使用引号引起来,直接写变量名 插入的内容必须是template标签或者组件 不能是原生的元素
//设计组件
<div class="content2">
<slot></slot> <!-- 不写插槽名默认插入位置 -->
<slot name="slot1"></slot>
<h3>{{contentData.title}}</h3>
<slot name="slot3">你不给我数据到3号插槽中,我就会默认显示出来</slot><!--不写数据,默认显示数据-->
<h3>{{contentData.text}}</h3>
<slot name="slot2"></slot>
</div>
//使用组件
<Content2 :contentData="arr[0]">
<template v-slot:slot1>
<img src="../assets/girl.png" />
</template>
<template v-slot:slot2>
<p>我是具名插槽插进去的数据</p>
</template>
<p>不写插槽名默认插入位置</p>
</Content2>
2、组件的data 设计成function的用义(重点!!!)
组件和挂载到界面的vm对象的区别,vm挂载到页面上时,触发了钩子函数的,data生成了,页面上使用的数据就是data容器中渲染上去的,所有的vm生成时data得有。
组件是引入和注册以后不一定使用的,比如for循环0次就是组件对象生成了的,但是使用0次,所以组件对象并没有使用自己的data容器去渲染数据,造成资源浪费,解决方案就是懒加载:当使用data时去调用,才生成data对象
组件可以多次使用,每使用一次,函数被调用一次则创建出不同的数据对象,实现同名组件的数据可以相互独立
3、组件传值(重点!!!)
(1)父向子传值props
//父组件
<Prop :mydata="msg"/>
//子组件
<div>{{mydata}}</div>
<script>
export default {
props: ["mydata"]
}
</script>
(2)子向父传值$emit
通过在父组件上定义自定义事件,在子组件中通过$emit 来触发事件;子组件的事件被触发并传参,事件处理函数可以接收到子组件的数据;事件绑定的事件处理函数在父节点上,故可在事件处理函数中用到子组件的数据值来修改父节点的数据。
//给父组件绑定自定义事件myclick
<SearchBtn @myclick="change" />
<script>
export default {
methods:{
change(arg){
console.log(arg);
}
}
}
</script>
//子组件
<input type="search" v-model="keywords" /><button @click="send">发表</button>
<script>
export default {
methods:{
send(){
//子组件用过$emit触发事件传参给父组件
this.$emit("myclick",{text:this.keywords,time:new Date().toLocaleString()});
}
}
}
</script>
计算属性如果数组里面某个值变换是不能监听到的,只有整个数组变换才能监听到,所以必须加上下面这句:
this.goods = [...this.goods];
在自定义组件中,如果想要绑定原生事件,如下所示:
<SearchBtn @click.native="change" />
(3)访问根组件的值$root
访问根组件vm对象,所有的子组件都可以将这个实例作为一个全局 store 来访问或使用,现在有更好的技术vuex代替。
//设置
this.$root.count = this.count;
//访问
this.msg= this.$root.count;
(4)$parent 和$children
$parent 访问父组件对象,直接操作父组件的data数据,不需要再使用属性传值,但是容易出现渲染混乱之后只渲染一个的情况。
this.title = this.$parent.title;
$children 访问子组件对象数组,不能保证顺序,也不是响应式的。
this.sondata = this.$children[1].sondata;
(5)$refs
如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子组件上,引用就指向组件实例,可以使用组件的所有方法
<Son :mydata="msg" ref="son" />
<h1 ref="myhe">我是测试数据</h1>
<button @click="getel">点击访问组件或者节点</button>
methods: {
getel() {
console.log(this.$refs.son, this.$refs.son.msg); //Son实例对象
console.log(this.$refs.myhe); //dom元素
this.$refs.son.fn(); //执行Son组件的fn方法
this.$refs.myhe.style.color = "red"; //dom元素字体变红
}
}
(6)中央事件总线bus
通过创建一个新的vm对象,专门统一注册事件,供所有组件共同操作,达到所有组件随意隔代传值的效果
//bus.js文件
const install = function(Vue) {
const Bus = new Vue({
methods: {
emit(event, ...args) {
this.$emit(event, ...args);
},
on(event, callback) {
this.$on(event, callback);
},
off(event, callback) {
this.$off(event, callback);
}
}
});
//由于这个新的vm放在与界面绑定的那个vm的原型上,因此页面上的所有组件都能通过this.$bus访问这个新vm对象
Vue.prototype.$bus = Bus;
};
export default install;
//组件文件中:
//任意业务中都可以通过调用来绑定事件,触发事件并传值,和销毁事件
this.$bus.on(event,callback) //绑定事件
this.$bus.off(event,callback) //销毁事件
this.$bus.emit(event, ...args) //触发事件
//示例
//组件1绑定事件:
created() {
this.$bus.on("myevent", (arg) => {
console.log(arg)
this.msg = arg;
})
}
//组件2触发事件
methods: {
send(){
this.$bus.emit("myevent","中央控制总线传过来的值")
}
}
(7)多层组件传值$attrs/$listeners
$attrs、$listeners可以实现跨级组件通信。$listeners进行事件传递,$attrs进行属性传递。
//爷爷组件
<p>{{msg}}</p>
<One v-bind:title="title" v-bind:title2="title2" v-on:myclick="fn" />
methods: {
fn(arg) {
this.msg = arg;
}
},
//父级组件
<Two v-bind="$attrs" v-on="$listeners" />
//子级组件
<p>{{$attrs.title}}</p>
<p>{{$attrs.title2}}</p>
<input v-model="v1" type="text" /><button @click="send">点击传给爷爷使用</button>
methods: {
send() {
this.$emit("myclick", this.v1)
}
}