前言
一篇小总结,组件间的通信问题,把v-model、作用域插槽也归入了。
通信场景
- 父传子
- 子传父
- 父子同步
- 非父子
父传子
父传子也可以分为两种情况
- 传数据:props
- 传html:slot
传数据
这个是最基本的一般就是父组件用v-bind动态赋值,props在子组件内相当于在子组件内新建了一个data,props可以为字符串数组,也可以是对象,为对象时则可以设置类型检查和默认值,如果想在子组件更改传进来的数据可以通过data引用或计算属性来转换。
传html
也就是插槽,基础的html标签中是可以写html或字符的,而在如果自定义组件标签内写,就会--什么都不会发生。因为在子组件内没有定义插槽,如果在子组件内有slot,则传入的内容就会显示在插槽处。传的多的话就要有name来区分
例子
-----子组件---------
<template>
<div class="child">
<p>这是子组件</p>
<slot name='first'>我是默认内容</slot>
<slot name='second'>我是默认内容</slot>
<slot name='third'>我是默认内容</slot>
</div>
</template>
---------父组件-----------
<template>
<div class="parent">
<my-child>
<p slot='first'>我在父组件写但是在子组件显示</p>
<template slot="second">
<h1>我是父组件写的标题</h1>
<p>我是父组件写的文章</p>
</template>
<h1 slot='third'>标题是{{title}}</h1>
</my-child>
<p>这是父组件</p>
</div>
</template>
渲染结果
总之就是花样传进去html,更复杂的作用域插槽在子传父写
子传父
这个都是传数据,方法有以下几种
- 通过事件向父组件发送消息
- 作用域插槽
事件传信
在父组件的子组件标签上监听事件,就像监听一个click一样,监听一个自定义事件,而这个事件的触发却在子组件内,例子,子组件的按钮会发出事件信号add,在父组件中的子组件标签上注册监听,监听到则触发。
----------------子组件--------
<p>这是子组件</p>
<button @click="$emit('add')">子组件</button>
----------------父组件-------------
<my-child v-on:add ='count += 1'></my-child >
<p>{{count}}</p>
<p>这是父组件</p>
根据上面的例子进一步,
$emit
的第二个参数可以通过事件传值,在父组件中通过访问$event
接收 例子这样就是点一下加3了。如果事件是函数那值会称为函数的第一个参数,无需$event
----------------子组件--------
<p>这是子组件</p>
<button @click="$emit('add',3)">子组件</button>
----------------父组件-------------
<my-child v-on:add ='count += $event'></my-child >
<p>{{count}}</p>
<p>这是父组件</p>
作用域插槽
这个作用域插槽是勉强可以放到子向父传数据,虽然还是要还给子组件,上面插槽可知,子组件内部的slot在父组件未向其传内容时是不显示或显示默认内容的,但是如果父组件指向向子组件传递html和css但是内容却由子组件自己提供呢?在父组件内不管{{}}写到哪,它能访问的都是父组件的作用域,现在我们需要在父组件的子标签内访问子标签的作用域
---------------------子组件-------------------------
<template>
<div class="child">
<p>这是子组件</p>
<slot :data='data'> </slot>
</div>
</template>
<script>
export default {
data() {
return {
data: ['子数据1','子数据2']
}
},
}
</script>
----------------------父组件----------------------
<template>
<div class="parent">
<my-child >
<template slot-scope='scope'> // scope就是个名字代表着子组件作用域
{{scope.data}}
</template>
</my-child >
<p>这是父组件</p>
</div>
</template>
子组件内slot通过bind绑定data,在父组件内给它取个名叫scope,slot-scope='scope'就可以访问了。懒得起名就用解构赋值slot-scope={data}
父子同步
也就是prop的双向绑定,单向数据流不可能双向绑定的,但是通过父通过props传子,子通过$emit传父,父在传回来可以实现同步
-----------------子组件-------------------------
<div class="child">
<p>这是子组件</p>
<button @click="$emit('update:title',childTitle)">更新标题</button>
<p>{{title}}</p>
</div>
-----------------父组件---------------------
<div class="parent">
<my-child :title="title" v-on:update:title='title = $event'></my-child >
<p>这是父组件</p>
</div>
简写
<my-child :title.sync="title"></my-child >
v-model
v-model也是一个意思,一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件。默认传递value进去又默认监听input事件,且监听后将传过来的值赋给value。
<input
type="text"
v-model="something">
<!--等价于-->
<input
type="text"
v-bind:value="something"
v-on:input="something = $event.target.value">
非父子
也就是同级组件或孙子组件之类的,一种方法是用一个空的vue实例当作中央事件总线,当作中介。组件通过on监听事件来接受,很好理解。
一种就是使用vuex。虽然官方推荐项目比较大时才使用,但是vuex语法简单,可以塞异步方法进去,配合调试工具数据流清晰。不管大小直接就用。