VUE——组件间通信

组件间通信的基本原则

  • 不要在子组件中直接修改父组件的状态数据
  • 数据在哪, 更新数据的行为(函数)就应该定义在哪

数据定义在哪个组件?
若多个组件公共一份数据,那么这个数据就定义在他们共同的父组件上

更新数据的函数方法定义在哪个组件?
数据在哪个组件, 那更新数据的行为(函数/方法)就定义,
后面可能需要将这些函数传递给需要调用的后代组件
后代组件不要直接更新父组件传递的数据, 而应该调用函数来更新

props : 父 ——> 子

非函数属性(对象/数组/基本类型)

子组件不要直接修改接收到的属性(props), 只是让你子组件去读取的
如果子组件想要更新父组件的数据 ==> 父组件需要向子组件传递更新数据的函数, 子组件调用(请看下节——函数属性)

使用步骤:

1). 通过标签属性
	props:['comments']
	
2). 父组件中给子组件标签指定属性
	App中 <List :comments="comments"/>
	
3). 子组件中声明接收标签属性:
 接收的所有标签属性都会成为子组件对象的属性, 在模板中可以直接在子组件中使用
	{{comments}}

子组件声明接收属性的方式:

方式一:

props: ['comments'], // 指定接收属性的属性名

方式二:

props: {
	      comments: Array  // 指定接收属性的属性名和属性值类型
	    }

方式三:

props: {
			sex: { // 指定属性的属性名/属性值的类型 / 默认属性值 / 是否必须的
		        type: Number,
		        default: 1, // 一般在不是必须时使用
		        required: true,  // 即使指定default值, 也必须传递标签属性
		    }
		}

函数属性

函数属性是从父组件传递给子组件,子组件调用了父组件传递的函数属性, 但能显示的数据是从子组件传递给了父组件,最终更新了父组件的数据

父组件:App.vue

<template>
  <div>
    <header class="site-header jumbotron">
      <div class="container">
        <div class="row">
          <div class="col-xs-12">
            <h1>请发表对Vue的评论</h1>
          </div>
        </div>
      </div>
    </header>
    <div class="container">
      <Add  :addComment="addComment"/>
      <List :comments = "comments"  :deleteComment="deleteComment"/>
    </div>
  </div>
</template>d
<script type="text/ecmascript-6">
  import Add from '@components/Add'
  import List from '@components/List'

  export default {
    data(){
      return{
        comments:[
          {id:1, username : 'A' , content : 'hello'},
          {id:2, username : 'B' , content : 'hi'},
          {id:3, username : 'C' , content : '你好'},
          {id:4, username : 'D' , content : '嗨'}
        ]
      }
    },
    methods:{
      addComment(comment){
        this.comments.unshift(comment)
      },
      deleteComment(index){
        this.comments.splice(index,1)
      }
    },
    components:{
      Add,
      List
    }
  }
</script>
子组件:item.vue

<template>
  <div>
    <li class="list-group-item">
        <div class="handle">
        //在子组件中定义一个删除函数
          <a href="javascript:;" @click = "delected">删除</a>
        </div>
        <p class="user"><span >{{comment.username}}</span><span>:</span></p>
        <p class="centence">{{comment.content}}</p>
      </li>
  </div>
</template>

<script type="text/ecmascript-6">
  export default {
  //通过 props 传基本属性 以及 删除属性
    props:['comment',"deleteComment","index"],
    data(){
      return{

      }
    },
    methods: {
      delected(){
        if(window.confirm("你确定删除这条评论吗?")){
        //在子组件中定义一个删除函数,并调用传过来的函数属性
        this.deleteComment(this.index)
        }
      }
    },
  }
</script>


自定义事件($emit) : 子 ——> 父

第一步:触发事件(子组件) : this.$emit

子组件Header.vue:

<template>
  <div class="todo-header">
  //在子组件中定义一个函数increase,触发事件
    <input type="text" placeholder="请输入你的任务名称,按回车键确认" @keyup.enter="increase" v-model = "content"/>
  </div>
</template>

<script type="text/ecmascript-6">
  export default {
    data(){
      return{
        content : ''
      }
    },
    methods:{
      increase(){
        let content = this.content.trim();
        if(!content || content === ''){
          return
        }
        let todo = {
          id : Date.now(),
          content,
          completed : false
        }
        // 分发事件,将 todo 数据传给父组件
        this.$emit('addTodo' , todo)
        this.content = ''
      }
    }
  }
</script>

第二步:绑定事件监听(父组件)

上面子组件this.$emit 的第一个参数要跟这个组件@后面的名称一致

父组件App.vue:

//将子组件传递给来的addTodo函数,绑定到父组件定义的add函数
<Header @addTodo="add"/>
... ...
methods:{
//todo参数是从子组件接收过来的
      add(todo){
        this.todos.unshift(todo)
      },
}

或者:

<Header ref="header"/>
... ...
mounted () {
	this.$refs.header.$on('addTodo', this.add)---正确
	
	this.$refs.header.$on('addTodo', (todo) => {this.add})---正确
	
	this.$refs.header.$on('addTodo', function(todo){this.add})--错误

},
methods:{
//父组件定义的addTodo函数
	add(todo){
	  this.todos.unshift(todo)
	},
}

第三步:解绑事件(父组件)

父组件App.vue:

beforeDestroy ( ) {
	This.$refs.header.$off (‘ addTodo’)
}


全局事件总线—任意组件间通信

Vue.prototype.$Bus = new Vue();
-------------------------------------------------------------------------
new Vue({
  beforeCreate () { // 尽量早的执行挂载全局事件总线对象的操作
    Vue.prototype.$globalEventBus = this
  },
}).$mount('#root')
mounted(){
	this.$globalEventBus.$on('deleteTodo', this.deleteTodo);
},
methods:{
	deleteTodo(name){
		this._name = name
	}
}
methods:{
	deleteTodo(){
		this.$globalEventBus.$emit('deleteTodo', this.name)
	}
}

数据从 $emit 传到 $on

this.$globalEventBus.$off('deleteTodo')

消息订阅与发布—任意组件间通信

npm install -S pubsub-js
import PubSub from 'pubsub-js' // 引入

import Vue from 'vue';
import App from './App'
import PubSub from 'pubsub-js'

Vue.config.productionTip = false;
new Vue({
  beforeCreate() {
    Vue.prototype.$globalEventBus = this;
    Vue.prototype.$PubSub = PubSub;
  },
  el:"#root",
  render : h => h(App)
})

(2)this.token = PubSub.subscribe(‘msgName’,(msgName, data) => { } ) // 订阅消息, 返回token —— mounted()

mounted() {
      this.$refs.header.$on('addTodo' , this.addTodo);
      this.$globalEventBus.$on('deleteTodo',this.deleteTodo)
      this.token = this.PubSub.subscribe('updateTodo', (msgName,{todo, isTrue})=>{
        this.updateTodo(todo, isTrue)
      })
    },

(3)PubSub.publish(‘msgName’, data): 发布消息, 触发订阅的回调函数调用

this.PubSub.publish('updateTodo', {todo:this.todo , isTrue : value})

数据从 publish 传到 subscribe

(4)PubSub.unsubscribe(this.token/msgName): 取消消息的订阅——beforeDestory()

beforeDestroy() {
      this.$PubSub.unsubscribe(this.token)
    },

beforeDestroy() {
      this.$PubSub.unsubscribe("updateTodo")
    },

async

功能与v-model类似, 可实现父子组件间的相互通信, 更准确的是双向数据同步

父组件:

<comp :foo.sync="bar"></comp>

<comp :foo="bar" @update:foo="val => bar = val"></comp>
子组件:

this.$emit('update:foo', newValue)    //将 bar 更新为 newValue

[ 例 ] 弹窗的关闭事件:

<template>
    <div class="details">
        <myComponent :show.sync='valueChild' style="padding: 30px 20px 30px 5px;border:1px solid #ddd;margin-bottom: 10px;"></myComponent>
        <button @click="changeValue">toggle</button>
    </div>
</template>
<script>
import Vue from 'vue'
Vue.component('myComponent', {
      template: `<div v-if="show">
                    <p>默认初始值是{{show}},所以是显示的</p>
                    <button @click.stop="closeDiv">关闭</button>
                 </div>`,
      props:['show'],
      methods: {
        closeDiv() {
          this.$emit('update:show', false); //触发 input 事件,并传入新值
        }
      }
})
export default{
    data(){
        return{
            valueChild:true,
        }
    },
    methods:{
        changeValue(){
            this.valueChild = !this.valueChild
        }
    }
}
</script>

$attrs / $listeners

父组件与任意子孙组件的通信

$ attrs: 排除props声明, class, style的所有组件标签属性组成的对象
$ listeners: 组件标签绑定的所有自定义事件监听的对象
一般: v-bind 与 $ attrs配合使用, v-on与 $ listeners配合使用

<el-button  v-bind = " $ attrs"   v-on = " $ listeners "  ></el-button>

Provider / Inject

父组件与任意子孙组件的通信
由于vue有$parent属性可以让子组件访问父组件。但孙组件想要访问祖先组件就比较困难。通过provide/inject可以轻松实现跨级访问祖先组件的数据

$parent / $children & $refs

父子组件通信
在父组件中通过refs 获取子组件的DOM,然后可以直接在父组件中调用子组件的方法
通过this.$ parent 、 this.$ children 直接获取、使用父组件或子组件的属性和方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值