tag:
父子组件通信
,data为什么为函数
,事件总线
组件的可选属性
组件不能直接访问顶层Vue实例中的数据:
<script>//✖!
Vue.component('cpn', {
template: "<div>{{number}}</div>"
})
const vm = new Vue({
el: "#app",
data: {
message:"some text"
},
})
组件中也可以有data
属性和methods
等属性:
Vue.component('cpn', {
template: "#cpn-t",
data() {
return {
message: "some text"
}
},
})
const vm = new Vue({
el: "#app",
})
可以发现,子组件中的data
是一个函数并且返回一个对象,并且如果不写成函数的形式而是写成对象的形式时会报错(“data”属性应该是在组件定义中返回一个对象实例的函数。)
![](https://img.haomeiwen.com/i19600599/ce828d506c0b38b7.png)
在每一次使用组件时,vue每次都会对调用data函数并且返回一个对象
解析:
函数返回内部对象时相当于会创建一个新的实例对象并返回该对象(的地址),而在返回外部对象时并不会创建新的对象而是返回外部对象的地址,这就会造成组件中数据污染到外部对象或是其他组件的数据
function f1() {
return{
name: 'Sam',
age: 20
};
}
let a = f1();//创建新的对象并返回
let b = f1();//创建新的对象并返回
let c = f1();//创建新的对象并返回
//-----------------------------------------
const person2 = {
name: "Alex",
age: 21
}
function f2(){
return person2;//这里return的是外部对象的引用(地址)
}
let d = f2();//外部对象的引用
let e = f2();//外部对象的引用
let f = f2();//外部对象的引用
//d,e,f指向同一个对象,修改任意一个其他的对象属性也会变.(当对象为const时属性仍然可修改)
data为函数:
Vue.component('button-counter2', {
data: function () {
return {
count: 0;
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
new Vue({ el: '#components-demo3' })
![](https://img.haomeiwen.com/i19600599/4fe1cc077d2d103b.gif)
var buttonCounter2Data = {
count: 0
}
Vue.component('button-counter2', {
data: function () {
return buttonCounter2Data
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
new Vue({ el: '#components-demo3' })
![](https://img.haomeiwen.com/i19600599/9ab772720652426a.gif)
组件通信
由于Vue父子组件中的数据是互相不可见的,因此有时需要传递数据时就需要组件通信.
-
通过props向子组件传递数据
<div id="app">
<cpn></cpn>
</div>
<template id="cpn-t">
<div>
<h4>title</h4>
<cpn-children :message="message"></cpn-children>
</div>
</template>
在父组件中 使用子组件时,需要绑定prop数组中的值到父组件的数据.由于html标签中不支持大小写,所以props中的变量名应为小写,若使用驼峰标识,在html标签内要转化为短横线命名.
<script>
Vue.component('cpn', {
template: "#cpn-t",
data() {
return {
message: "some text"
}
},
components: {
cpnChildren: {
template: '<div>{{message}}</div>',
//数组形式:props: ['message',/*...*/] ,在Vue风格指南中作为反例
//对象形式:
props: {
message: String,
name: {
type: String, //类型
default: "Smith", //默认值
required:false
}
}
}
}
})
如果props中的数据是对象类型或数组类型,在默认值处应用工厂方法返回一个数组或对象:
props:{
fruit:{
type:Array,
default:function(){
return [];
}
}
}
-
通过自定义事件向父组件发送消息
<body>
<div id="app">
<cpn></cpn>
</div>
</body>
<template id="cpn-t">
<div>
<cpn-children @emit-btn-click="consoleLog"></cpn-children> //监听子组件发射自定义事件并处理事件!该方法不需要参数,默认传入.
</div>
</template>
<script>
Vue.component('cpn', { //定义父组件
template: "#cpn-t",
methods: {
consoleLog(item) { //默认传入自定义事件中的传递的数据,由父组件处理发射事件
console.log("Clicked", item.name);
}
},
components: { //定义子组件
cpnChildren: {
template: `<div>
<button v-for="item in fruit" @click="btnclicked(item)">{{item.name}}</button>
</div>
`,
data() {
return {
fruit: [{
id: 200,
name: "apple",
price: 2
},
{
id: 201,
name: "banana",
price: 2.5
},
{
id: 202,
name: "orange",
price: 0.5
}
]
}
},
methods: {
btnclicked(item) {
this.$emit("emit-btn-click",item) // 子组件向父组件发射自定义事件
},
}
}
}
})
const vm = new Vue({
el: "#app",
methods: {
}
})
</script>
Vue<ROOT>
|-cpn =>监听发射事件,,接收数据,处理发射,
|-cpnChildren =>发射事件,数据
补充:通过事件总线发送消息
当某些组件层次过深或传递复杂时,一方面我们可以用Vuex来进行管理事件,另一方法是在vue原型上添加$bus
属性,通过另一个vue实例来对事件进行发送和监听。
//main.js
import Vue from 'vue'
import App from './App.vue'
//vue原型添加事件总线
Vue.prototype.$bus = new Vue()
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
//CpnSendMsg.vue
//发送消息(事件)的组件
<template>
<div>
<button @click="sendMsg">
send
</button>
</div>
</template>
<script>
export default {pn
name:"Cpn",
methods: {
sendMsg(){
//利用新建的vue实例来发送自定义事件
this.$bus.$emit("cpnSendMsg")
}
}
}
</script>
//CpnReseveMsg.vue
//接受自定义事件的组件
<template>
<div>
。。。
</div>
</template>
<script>
export default {
name:"CpnReseveMsg",
created(){
this.$bus.$on("cpnSendMsg",()=>{
//事件处理
})
}
}
</script>
补充:使用vue原型包装全局组件
使用场景:封装Toast(弹窗)组件,由于该组件需要在不同场景下使用,,十分频繁,因此将该组件挂载到vue原型上。
//toast/Toast.vue
<template>
<div class="toast" v-show="isShow">
{{message}}
</div>
</template>
<script>
export default {
name: 'Toast',
data() {
return {
message:'',
isShow:false
}
},
methods: {
show(message,time){
this.message = message
this.isShow = true
setTimeout(() => {
this.isShow = false
this.message = ''
}, time);
}
},
};
</script>
<style lang='scss' scoped>
.toast{
position: fixed;
top:50%;
left: 50%;
transform: translate(-50%,-50%);
z-index: 1000;
padding: 20px 40px;
color: rgb(110, 110, 110);
background-color: rgba(236, 236, 236, 0.85);
border-radius: 15px;
}
</style>
//toast/index.js
import Toast from "./Toast"
const obj = {
}
obj.install = function (Vue) {
//1.创建组件构造器
const constructor = Vue.extend(Toast)
//2.使用new方法创建组件
const toast = new constructor()
//3.手动挂载组件对象
toast.$mount(document.createElement('div'))
//4.已经挂载的对象具有了el属性
document.body.appendChild(toast.$el)
Vue.prototype.$toast = toast
/********************or***************************/
//1.创建组件
const toast = new Vue(Toast)
//2.手动挂载组件对象
toast.$mount(document.createElement('div'))
//3.已经挂载的对象具有了el属性
document.body.appendChild(toast.$el)
Vue.prototype.$toast = toast
}
export default obj
//main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
//导入toast
import toast from "components/common/toast/index"
//vue原型添加事件总线
Vue.prototype.$bus = new Vue()
Vue.config.productionTip = false
//安装toast
Vue.use(toast)
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
//use
//...
methods:{
someEventAcitve(){
this.$toast.show("MESSAGE",2000)
}
}
网友评论