Vue2——组件通信高级

目录

第一章:自定义事件 

第二章:v-model深入

第三章:sync修饰符

第四章:$attrs 和 $listeners

第五章:$children与$parent

第六章:作用域插槽


第一种:props

适用于的场景:父子组件通信

注意事项:

  如果父组件给子组件传递数据(函数):本质其实是子组件给父组件传递数据

  如果父组件给子组件传递的数据(非函数):本质就是父组件给子组件传递数据

  书写方式:三种

  ['todos'], {type: Array}, {type: Array, default: []}

  小提示:路由的props

  书写形式:布尔值,对象,函数形式

第二种:自定义事件

适用于场景:子组件给父组件传递数据

$on和$emit

第三种:全局事件总线$bus

适用于场景:万能

Vue.prototype.$bus = this

第四种:pubsub-js, 在React框架中使用比较多(发布和订阅)

适用于场景:万能

第五种:Vuex

适用于场景:万能

第六种:插槽

适用于场景:父子组件通信(一般结构)

默认插槽

具名插槽

作用域插槽

第一章:自定义事件 

1. 给button绑定原生DOMclick事件,触发handler函数

2. Event1是组件,非原生DOM节点,而绑定的click事件并不是原生DOM事件,而是自定义事件,要把它变成原生的DOM事件,要在click后面加上.native,实质上是给Event1组件的根节点绑定了点击事件,利用了事件委派

3. 给原生DOM绑定自定义事件,是没有任何意义的,因为无法触发$emit

4. 给组件Event2分别绑定了自定义事件click和xxx,在组件里面点击按钮触发自定义事件handler3,并把参数传过去

// Event.vue
<template>
  <div>
    <h1>event组件</h1>
    <!-- 原生DOM绑定系统事件 -->
    <button @click="handler">原生DOM绑定原生事件</button>
    <!-- Event1组件:Event1非原生DOM节点,而绑定的click事件并非原生DOM事件,而是自定义事件
        @click.native,可以把自定义事件变为原生DOM事件
        当前原生DOMclick事件,其实是给子组件的根节点绑定了点击事件————利用了事件委派
    -->
    <Event1 @click.native="handler1"></Event1>
    <!-- 下面的写法是给原生DOM绑定自定义事件
          给原生DOM绑定自定义事件没有任何意义,因为没有办法触发$emit -->
    <button @xxx="handler2">原生DOM绑定自定义事件</button>
    <!-- 组件标签 -->
    <Event2 @click="handler3" @xxx="handler3"></Event2>
  </div>
</template>

<script>
import Event1 from '@/views/Event/event1.vue'
import Event2 from '@/views/Event/event2.vue'
export default {
  name: 'MyEvent',
  components:{
    Event1,
    Event2
  },
  methods:{
    handler(event){
      // 原生DOMbutton事件回调
      console.log('原生DOM绑定原生事件', event);
    },
    handler1(){
      console.log('event1事件回调');
    },
    handler2(){

    },
    // event自定义事件回调
    handler3(params){
      console.log('event自定义事件回调', params);
    }
  }
}
</script>

<style>

</style>
// event1.vue
<template>
  <div>
    <h2>Event1组件</h2>
    <span>其它内容</span>
  </div>
</template>

<script>
export default {
  name: 'MyEvent1'
}
</script>

<style scoped>
  div{
    width: 100%;
    height: 100px;
    background-color: pink;
  }
</style>
// event2.vue
<template>
  <div>
    <h2>Event2组件</h2>
    <button @click="$emit('click', '自定义事件回调')">分发自定义click事件</button>
    <button @click="$emit('xxx', '自定义事件xxx')">分发自定义xxx事件</button>
  </div>
</template>

<script>
export default {
  name: 'MyEvent2'
}
</script>

<style scoped>
  div{
    width: 100%;
    height: 100px;
    background-color: pink;
  }
</style>

第二章:v-model深入

1. v-model实现原理:value与input事件实现的,而且还需要注意可以通过v-model实现父子组件数据同步

2. 通过input输入框的input事件,可以获取输入框中的文本内容,使用v-bind将data中的数据绑定到输入框中,再通过input事件每当文本框内容发生变化,就把输入框中的内容赋值给msg

3. 通过上面这个特征,可以让让父子组件通信。

        1)通过 :value 将msg中的数据传给子组件

        2)给组件定义自定义事件input,每当触发这个事件,就把传输过来的值赋给msg

        3)CustomInput组件props接收传过来的数据,v-bind绑定到输入框中,再通过input事件触发自定义事件,将文本框中的内容传给父组件

4. v-model不仅可以使用在表单元素中,也可以利用上面这个特性来实现父子组件通信,所有在组件标签中直接使用v-model也是可以的

// Model.vue
<template>
  <div>
    <h2>深入v-model</h2>
    <input type="text" v-model="msg">
    <span>{{msg}}</span>
    <hr>
    <h2>v-model实现原理(vue2)</h2>
    <!-- 原生DOM当中是有oninput事件,它经常结合表单元素一起使用,当表单元素文本内容发生变化的时候,就会触发一次回调
          Vue2:可以通过value与input事件结合实现v-model功能-->
    <input type="text" :value="msg" @input="msg = $event.target.value">
    <span>{{msg}}</span>
    <hr>
    <!-- :value: 是props,父传子  @input:并非原生的DOM的input事件,属于自定义事件 -->
    <CustomInput :value="msg" @input="msg = $event"></CustomInput>
    <CustomInput v-model="msg"></CustomInput>
  </div>
</template>

<script>
import CustomInput from '@/views/Model/CustomInput.vue'
export default {
  name: 'MyModel',
  components: {CustomInput},
  data(){
    return{
      msg: 'abcdefg'
    }
  }
}
</script>
// CustomInput.vue
<template>
  <div style="background: pink; height: 100px">
    <h2>input包装组件</h2>
    <!-- :value 动态属性 @input 给原生DOM绑定原生DOM事件 -->
    <input type="text" :value="value" @input="$emit('input', $event.target.value)">
  </div>
</template>

<script>
export default {
  props: ['value']
}
</script>

第三章:sync修饰符

1. props给子组件传递money,并且给子组件定义“update:money”自定义事件,子组件点击按钮后就会触发这个自定义事件,将变化的数据传递给父组件,更新数据,其实和v-model很相似,都是可以实现父子数据同步 ———— Child1组件

2. 在通过props传递数据的时候,在后面加上.sync,有两个作用,第一个是传递数据,第二个是绑定自定义事件,比如下面给Child2绑定的自定义事件叫做“update:money”,与上面是同样的效果,也实现了父子数据同步 ———— Child2组件

// Sync.vue
<template>
  <div>
    <h1>小明的爸爸现在有{{money}}元</h1>

    <h2>不使用sync修饰符</h2>
    <!-- :money 父组件给子组件传递props
    @update:money 给子组件绑定的自定义事件只不过名字叫做update:money
    目前这种操作,和v-model很相似,可以实现父子数据同步 -->
    <Child1 :money="money" @update:money="money = $event"></Child1>
    <h2>使用sync修饰符</h2>
    <!-- :money.sync 第一:父组件给子组件传递props money, 第二:给当前子组件绑定了一个自定义事件,而且事件名称即为update:money -->
    <Child2 :money.sync="money"></Child2>
  </div>
</template>

<script>
import Child1 from '@/views/Sync/Child1.vue'
import Child2 from '@/views/Sync/Child2.vue'
export default {
  name: 'MySync',
  components:{
    Child1,
    Child2
  },
  data(){
    return{
      money: 10000
    }
  }
}
</script>

<style>

</style>
// Child1.vue
<template>
  <div style="background: #ccc; height: 50px">
    <span>小明每次花100元</span>
    <button @click="$emit('update:money', money - 100)">花钱</button>
    爸爸还剩{{money}}元
  </div>
</template>

<script>
export default {
  name: 'MyChild1',
  props: ['money']
}
</script>
// Child2.vue
<template>
  <div style="background: #ccc; height: 50px">
    <span>小明每次花100元</span>
    <button @click="$emit('update:money', money - 100)">花钱</button>
    爸爸还剩{{money}}元
  </div>
</template>

<script>
export default {
  name: 'MyChild2',
  props: ['money']
}
</script>

 第四章:$attrs 和 $listeners

1. $listeners与$attrs(组件通信方式一种), 它们两者都是组件实例的属性,可以获取到父组件给子组件传递props与自定义事件

2. $attrs属于组件的一个属性,可以获取到父组件传递过来的props数据

3. 对于子组件而言,父组件给的数据可以利用props接收,但是如果子组件通过props接收的数据,在$attrs属性中获取不到

4. $listeners,他也是组件实例自身的一个属性,它可以获取父组件给子组件传递的自定义事件

5. 在子组件中,如果要使用$attrs,则必须用v-bind,不能使用: ,如果要使用$listeners,必须使用v-on,不能使用@

// AttrsListeners.vue
<template>
  <div>
    <h2>自定义带hover提示的按钮</h2>
    <!-- 当用户使用封装的按钮的时候, 需要向HintButton组件传递相应的参数, -->
    <!-- @click, 表示自定义事件 -->
    <HintButtonVue type="primary" icon="el-icon-edit" size="mini" title="按钮" @click="handler"></HintButtonVue>
  </div>
</template>

<script>
import HintButtonVue from '@/views/HintButton/HintButton.vue'
export default {
  name: 'AttrsListeners',
  components:{ HintButtonVue },
  methods:{
    handler(){
      console.log('触发handler函数');
    }
  }
}
</script>

<style>

</style>
// HintButton.vue
<template>
  <div>
    <a :title="title"></a>
    <!-- v-bind不能用: , v-on 不能用 @ -->
    <el-button v-bind="$attrs" v-on="$listeners"></el-button>
  </div>
</template>

<script>
export default {
  props: ['title'],
  mounted(){
    // $attrs属于组件的一个属性,可以获取到父组件传递过来的props数据
    // 对于子组件而言,父组件给的数据可以利用props接收,但是如果子组件通过props接收的数据,在$attrs属性中获取不到
    console.log(this.$attrs);
    // $listeners,他也是组件实例自身的一个属性,它可以获取父组件给子组件传递的自定义事件
    console.log(this.$listeners);
  }
}
</script>

<style>

</style>

第五章:$children与$parent

1. ref可以获取到某一个组件,子组件给父组件传递数据

2. $children组件实例的属性,可以获取当前组件的全部子组件[数组]

3. $parent组件实例的属性,可以获取当前子组件的父组件,进而可以操作父组件的数据与方法

4. 下面这个案例,BABA找小明和小红借钱,可以通过$children获取它们的组件实例对象,进而操作数据

5. 小明和小红给BABA前,可以通过$parent获取父组件实例对象,操作数据

6. 通过这两个方法也可以实现父子组件之间的通信

注意:获取子组件实例对象时不要使用 this.$children[0] 这个样式,因为如果组件多的话顺序不一定

// ChildrenParent.vue
<template>
  <div>
    <h2>BABA有存款:{{money}}</h2>
    <button @click="borrowMoneyFromXM(100)">找小明借钱100</button>
    <button @click="borrowMoneyFromXH(150)">找小红借钱150</button>
    <button @click="borrowMoneyFromAll(200)">找所有孩子借钱200</button>
    <br>
    <Son ref="xm"></Son>
    <br>
    <Daughter ref="xh"></Daughter>
  </div>
</template>

<script>
import Son from '@/views/ChildrenParent/Son.vue'
import Daughter from '@/views/ChildrenParent/Daughter.vue'
export default {
  name: 'ChildrenParent',
  components:{
    Son, Daughter
  },
  data(){
    return {
      money: 1000
    }
  },
  methods:{
    borrowMoneyFromXM(money){
      this.money += money
      // ref可以获取真实DOM节点,也可以获取子组件标签(操作子组件的数据与方法)
      this.$refs.xm.money -= money
    },
    borrowMoneyFromXH(money){
      this.money += money
      this.$refs.xh.money -= money
    },
    borrowMoneyFromAll(money){
      this.money += 2 * money
      this.$children.forEach(item => {
        item.money -= money
      })
    }
  }
}
</script>
// Son.vue
<template>
  <div style="background: pink; height: 70px;">
    <h3>儿子小明,有存款{{money}}</h3>
    <button @click="lendMoney(50)">给BABA钱:50</button>
  </div>
</template>

<script>
export default {
  name: 'MySon',
  data(){
    return{
      money: 3000
    }
  },
  methods:{
    lendMoney(money){
      this.money -= money
      // 可以通过$parent属性获取到某一个组件的父组件, 进而操作父组件的数据与方法
      this.$parent.money += money
    }
  }
}
</script>

<style>

</style>
// Daughter.vue
<template>
  <div style="background: pink; height: 70px">
    <h3>女儿小红,有存款{{ money }}</h3>
    <button @click="lendMoney(100)">给BABA钱:100</button>
  </div>
</template>

<script>
export default {
  name: 'MyDaughter',
  data() {
    return {
      money: 2000,
    }
  },
  methods: {
    lendMoney(money) {
      this.money -= money
      this.$parent.money += money
    },
  },
}
</script>

<style></style>

第六章:作用域插槽

1.插槽:可以实现父子组件通信(通信的结构)

2. 作用域插槽:子组件来源于父组件,但是子组件决定不了它的外观和结构

3. 在下面两个案例中,父组件将todos传给子组件,子组件决定不了它的外观,将数组的每一项传给父组件,定义作用域插槽,在父组件中定义它们的样式

//SlotScope.vue
<template>
  <div>
    <h2>效果一:显示TODO列表时,已完成的TODO为绿色</h2>
    <List :todos="todos">
      <template slot-scope="todo">
        <span :style="{color: todo.todo.isComplete ? 'green' : 'red'}">{{todo.todo.text}}</span>
      </template>
    </List>
    <hr>
    <h2>效果二:显示TODO列表时,带序号,TODO的颜色为蓝绿搭配</h2>
    <List1 :todos="todos">
      <template slot-scope="{todo, $index}">
        <span :style="{color: todo.isComplete ? 'pink' : 'purple'}">{{$index}} --- {{todo.text}}</span>
      </template>
    </List1>
  </div>
</template>

<script>
import List from '@/views/ScopeSlot/List.vue'
import List1 from '@/views/ScopeSlot/List1.vue'
export default {
  name: 'ScopeSlot',
  components:{ List, List1 },
  data(){
    return{
      todos: [
        { id: 1, text: 'AAA', isComplete: false },
        { id: 2, text: 'BBB', isComplete: true },
        { id: 3, text: 'CCC', isComplete: false },
        { id: 4, text: 'DDD', isComplete: false },
      ]
    }
  }
}
</script>

<style>

</style>
// List.vue
<template>
  <ul>
    <li v-for="(item, index) in todos" :key="index">
      <slot :todo="item"></slot>
    </li>
  </ul>
</template>

<script>
export default {
  name: 'MyList',
  props:{
    todos: Array
  }
}
</script>
// List1.vue
<template>
  <ul>
    <li v-for="(item, index) in todos" :key="index">
      <slot :todo="item" :$index="index"></slot>
    </li>
  </ul>
</template>

<script>
export default {
  name: 'MyList',
  props:{
    todos: Array
  }
}
</script>

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值