Vue中组件通信方式 || V-model || Vue进阶

props

1、适用于的场景

  • 父子组件之间的通信

2、注意事项

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

3、书写方式(3种)

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

小提示

  • 路由的props书写形式也有三种:布尔值、对象、函数形式

自定义事件

1、适用场景

  • 子组件给父组件传递数据
  • 使用$emit和 $on【简写@】

2、事件分类

  • 原生DOM事件【click|mouseenter…】
  • 自定义事件【子给父传递数据】,自定义事件也可以使用click等,不过要加上一些修饰关键字 native等

举例:

<Event1 @click="handler1"></Event1>
  • 组件绑定原生DOM事件,并非原生DOM事件,而是所谓的自定义事
  • 如果你想把自定义事件变为原生DOM事件,需要加上修饰符.native修饰
  • 这个修饰符,可以把自定义事件【名字:原生DOM类型的】变为原生DOM事件,

**而且在父节点设置了单击事件,父节点里面的每个子节点都可以使用点击该单击事件 ** - 因为用到了事件的委派

**注意:**给原生的DOM绑定自定义事件是没有任何意义的,因为没有办法触发$emit函数

全局事件总线 $bus

1、适用场景

  • 万能的

2、使用方式

  • 组件实例的原型的原型指向的Vue.prototype
Vue.prototype.$bus = this

pubsub-js【发布消息与订阅】

  • 在vue中很少使用,在react中使用较多

Vuex【仓库】

1、适用场景

  • 万能的【里面存储的数据是非持久化的】

5个核心概念

核心概念:5
state 
mutations
actions 
getters 
modules

插槽 (slot)

1、适用场景

  • 适用于父子组件之间的通信
  • 【父子通信的结构】

2、三大插槽

默认插槽
具名插槽
作用域插槽:子组件的数据来源于父组件,但是子组件的自己的结构有父亲决定。

v-model

  • 原生DOM当中是有oninput事件的,它经常结合表单元素一起使用,当表单元素文本内容发生变化的时候,就会发出一次回调
  • 区别于@change事件,@change需要在外部在进行一次点击事件的操作

Vue2中 可以通过value与input事件来实现 v-model功能

<input type='text' :value='msg' @input="msg = $event.target.value" />
<span>{{msg}}</span> 

**深入学习 v-model ** 实现父子组件数据同步(即实现父子原件之间通信)

// 父组件 ModelTest.vue
// :value 到底是什么?这可是props,父子组件通信
// @input是什么?并非原生DOM的input事件 属于自定义事件
<CustomInput :value='msg' @input=' msg= $event' />
// 上面写法可以简化为 
<CustomInput v-model='msg' />

// 子组件 CustomInput 
// :value 到底是什么?动态属性
// @input是什么?给原生DOM绑定原生DOM事件
<input type='text' :value='value' @input="$emit('input',$event.target.value)">

v-model实现原理

  • value与input事件实现的,而且还需要注意的是可以通过v-model实现父子组件数据同步的操作

属性修饰符 sync【组件通信方式的一种】

  • 可以实现父子组件数据同步
  • :money.sync 代表父组件给字符串传递props[money] 给当前子组件绑定一个自定义事件(update:money)

父组件

<template>
  <div>
    <h1>小明的爸爸现在有 {{ money }}</h1>
    <!-- 
      :money 是父组件给子组件传递的props
      @update:money 是子组件绑定的自定义事件
      目前这种操作其实和v-model和相似
      可以实现父子组件数据同步操作
     -->
    <Child :money="money" @updateMoney="updateMoney" />

    <hr />
    <!--   
      :money.sync
      - ① 父组件给字符串传递props参数 money
      - ② 给当前子组件绑定了一个自定义事件,并且事件名称为update:money 
      - 在子组件触发的时候,一定是触发的函数名称要是update:money 
    -->
    <Child2 :money.sync="money" />
  </div>
</template>

<script>
import Child from "./components/Child.vue";
import Child2 from "./components/Child2.vue";
export default {
  name: "App",
  data() {
    return {
      money: 10000,
    };
  },
  components: { Child, Child2 },
  methods: {
    updateMoney($event) {
      this.money = $event;
      return money;
    },
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

子组件child,一般形式下

<template>
  <div>
    <span>小明每次花100</span>
    <button @click="$emit('updateMoney', money-100)">花钱</button>
    爸爸还剩下{{money}}</div>
</template>

<script>
export default {
  name: "Child",
  props: ["money"],
};
</script>

<style>
</style>

子组件Child2 使用了sync修饰符

<template>
  <div>
    <span>小明每次花100</span>
    <button @click="$emit('update:money', money-100)">花钱</button>
    爸爸还剩下{{money}}</div>
</template>

<script>
export default {
  name: "Child",
  props: ["money"],
};
</script>

<style>
</style>

Element-Ui(2点要求)

在使用element-ui局部引入的操作的时候,应该现在控制台安装这个

npm i babel-plugin-component -D

在babel.config.js中配置如下内容

"plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]

$attrs 与 $listeners (父子组件通信)

  • $attrs:组件实例的属性,可以获取到父亲传递的props数据(前提子组件没有通过props接受)
  • $listeners:组件实例的属性,可以获取到父亲传递自定义事件(对象形式呈现)

例子(对el-button进行二次封装)

父组件

<template>
  <div>
    <h2>自定义带Hover提示的按钮</h2>
    <!-- 
      当用户在使用我们封装的按钮的时候
      需要向HintButton组件传递相应的参数(el-button进行二次封装)
    -->
    <HintButton
      type="success"
      icon="el-icon-delete"
      size="mini"
      title="提示按钮"
      @click="handler"
    ></HintButton>
  </div>
</template>

<script>
import HintButton from "./components/HintButton.vue";
export default {
  name: "App",
  data() {
    return {};
  },
  components: { HintButton },
  methods: {
    // 点击事件的回调
    handler(){
      alert(666)
    }
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

子组件

<template>
  <div>
    <!-- 可以巧妙的利用a标签实现按钮带有提示功能 -->
    <a :title="title">
      <!-- <el-button :type="$attrs.type">添加</el-button> -->
      <!-- 下面这种写法必须使用v-bind,不能使用其简写形式 v-on也不能用@符进行替换 -->
      <el-button v-bind="$attrs" v-on="$listeners">添加</el-button>
    </a>
  </div>
</template>

<script>
export default {
  name: "HintButton",
  props: ["title"],
  mounted() {
    /* 
      $attrs 属于组件的一个属性
      可以获取父组件传递过来的props数据 是一个对象
      - 对于子组件而言,父组件给的数据可以利用props接收
      - 但是需要注意:如果子组件通过props接收的属性,在$attrs属性当中是获取不到的

      $listeners 
      - 也是组件实例自身的一个属性,它可以获取到父组件给子组件传递自定义事件
    */
    console.log(this.$attrs);
    console.log(this.$listeners)
  },
};
</script>

<style>
</style>

相应的解释,写在了上述代码当中

$children 与 $parent (可以实现父子组件通信)

  • ref:可以在父组件内部获取子组件—实现父子通信
ref:获取节点(组件标签)

注意:ref可以获取真实DOM节点,当然也可以获取子组件标签(操作子组件的数据与方法)
在这里插入图片描述

  • $children:可以在父组件内部获取全部的子组件【返回数组】
    如果子组件过多,第0项可能不是小明,尽可能不要使用 中括号+下标的形式来书写
this.$children[0].money -= 200 // 不用
// 推荐使用forEach
  • $parent:可以在子组件内部获取唯一的父组件【返回组件实例】

混入mixin

  • 如果项目当中出现很多结构类似功能,想到组件复用
  • 如果项目当中很多组件JS业务逻辑相似,想到mixin【可以把多个组件JS部分重复、相似地方】

插槽

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

默认插槽
具名插槽
作用域插槽:子组件的数据来源于父组件,但是子组件是决定不了自身结构与外观

解构对象的时候,我们必须要想到的就是kv必须要一致才行,这一点很关键 - 使用插槽的时候,子组件决定不了它的结构,它的结构是由父组件决定的

作用域插槽的实例

父组件

<template>
  <div>
    <h2>效果一:显示TODO列表时,已完成的TODO为绿色</h2>
    <!-- 首先子组件的数据来源于父组件 -->
    <List :todos="todos">
      <!-- 子组件决定不了自己的结构与样式,需要通过父组件决定 -->
      <!-- todo是子组件返回回来的对象 -->
      <template slot-scope="todo">
        <span :style="{ color: todo.todo.icComplete ? 'green' : 'red' }">{{
          todo.todo.text
        }}</span>
      </template>
    </List>

    <hr />
    <List1 :todos="todos"> 
      <!-- 子给父传的是两个,可以用一个对象来接收进行解构 -->
      <template slot-scope="{todo, $index}">
        <span>{{$index}} --- {{todo.text}}</span>
      </template>
    </List1>
  </div>
</template>

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

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

子组件

<template>
  <div>
    <ul>
        <li v-for="(item, index) in todos" :key="index">
            <!-- 作用域插槽 :不是props,其将每一个数据回传 -->
            <slot :todo="item" :$index="index"></slot>
        </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "List",
  props: {
    todos: Array,
  },
};
</script>

<style>
</style>

子组件占了一个坑,父组件来填坑的感觉

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值