vue组件化开发当中,经常需要涉及组件之间的通讯,常见的组件通讯,有以下几种:
1). `props` ★★(使用的频率最高,一般常用在简单的数据传递的场景)
2). `$emit/$on ` ★★ 事件总线
3). `vuex` ★★★
4).`$parent/$children`
5). `$attrs/$listeners`
6). `provide/inject` ★★★
常见的使用场景可以分为以下三大类:
props方法如下:
父组件给子组件传值方法;
父组件:
<template>
<div id="app">
<Child v-bind:child="users"></Child> //前者自定义名称便于子组件调用,后者要传递数据名
</div>
</template>
<script>
import Child from "./components/Child" //子组件
export default {
name: 'App',
data(){
return{
users:["Eric","Andy","Sai"],
transfername:"any data"
}
},
components:{
"Child":Child
}
}
</script>
在父组件使用的时候v-blind:后面的参数名称需要对应子组件中的props中的值,后面=号跟着的变量值,可以是当前父组件data中的任意一个值:
例如,上述中=等号后面的可以是user也可以是transfername,根据需要可以传递父组件中的任意一个值到子组件,:后续的参数只能是child,这里和子组件中的props中的属性一一对应。
子组件:
<template>
<div class="hello">
<ul>
<li v-for="item in child">{{ item }}</li> //遍历传递过来的值渲染页面
</ul>
</div>
</template>
<script>
export default {
name: 'Hello World',
props:{
child:{ //这个就是父组件中子标签自定义名字
type:Array, //对传递过来的值进行校验
required:true //必添
}
}
}
</script>
该方法实现的是简单的将父组件的数据传递给子组件,通过v-bind-传递的需要接受的属性名称,child,和父组件中的bind后面的参数一一对应,
当前传值方法可以传递简单的数值,也可以传递数组,对象等等,详细的传递格式参考官网地址:https://cn.vuejs.org/v2/guide/components-props.html
传入一个对象的所有 property
如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind
(取代 v-bind:prop-name
)。例如,对于一个给定的对象 post
:
post: {
id: 1,
title: 'My Journey with Vue'
}
下面的模板:
<blog-post v-bind="post"></blog-post>
等价于:
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
子组件给父组件传值方法,通过该方法,子组件可以主动给父组件传值:
子组件通过events给父组件发送消息,实际上就是子组件把自己的数据发送到父组件,vue`实例 作为事件总线(事件中心)用来触发事件和监听事件,可以通过此种方式进行组件间通信包括:父子组件、兄弟组件、跨级组件
创建bus文件
```
import Vue from 'vue'
export defult new Vue()
```
```
// gg组件
<template id="a">
<div>
<h3>gg组件</h3>
<button @click="sendMsg">将数据发送给dd组件</button>
</div>
</template>
<script>
import bus from './bus'
export default {
methods: {
sendMsg(){
bus.$emit('sendTitle','传递的值')
}
}
}
</script>
当前组件通过on方法,接受传递过来的参数,传递的动作发生在bus组件当中。
<template>
<div>
接收gg传递过来的值:{{msg}}
</div>
</template>
<script>
import bus from './bus'
export default {
data(){
return {
mag: ''
}
}
mounted(){
bus.$on('sendTitle',(val)=>{
this.mag = val
})
}
}
方法三,如果业务比较复杂,或者涉及到多层组件之间的互相传值,比较推荐的方法是vuex
vuex介绍如下:
`Vuex`实现了一个单向数据流,在全局拥有一个`State`存放数据,当组件要更改`State`中的数据时,必须通过`Mutation`提交修改信息,`Mutation`同时提供了订阅者模式供外部插件调用获取`State`数据的更新。 #### 而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走`Action`,但`Action`也是无法直接修改`State`的,还是需要通过`Mutation`来修改State的数据。最后,根据`State`的变化,渲染到视图上。
vuex中核心概念
* `state`:`vuex`的唯一数据源,如果获取多个`state`,可以使用`...mapState`。
```
export const store = new Vuex.Store({
// 注意Store的S大写
<!-- 状态储存 -->
state: {
productList: [
{
name: 'goods 1',
price: 100
}
]
}
})
getter`: 可以将`getter`理解为计算属性,`getter`的返回值根据他的依赖缓存起来,依赖发生变化才会被重新计算。
```
import Vue from 'vue'
import Vuex from 'vuex';
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
productList: [
{
name: 'goods 1',
price: 100
},
]
},
// 辅助对象 mapGetter
getters: {
getSaledPrice: (state) => {
let saleProduct = state.productList.map((item) => {
return {
name: '**' + item.name + '**',
price: item.price / 2
}
})
return saleProduct;
}
}
})
// 获取getter计算后的值
export default {
data () {
return {
productList : this.$store.getters.getSaledPrice
}
}
}
mutation`:更改`vuex`的`state`中唯一的方是提交`mutation`都有一个字符串和一个回调函数。回调函数就是使劲进行状态修改的地方。并且会接收`state`作为第一个参数`payload`为第二个参数,`payload`为自定义函数,`mutation`必须是同步函数。
```
// 辅助对象 mapMutations
mutations: {
<!-- payload 为自定义函数名-->
reducePrice: (state, payload) => {
return state.productList.forEach((product) => {
product.price -= payload;
})
}
}
页面当中如果需要使用的时候:
reducePrice(){
this.$store.commit('reducePrice', 4)
}
}
该模块的介绍,后续还会继续补充.....
除了上述属性可以方便的用来传值之外,还可以使用localStorage,进行存储和读取。
方法四,vuex中数据存储 localStorage
`vuex` 是 `vue` 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在`vuex`里数据改变的时候把数据拷贝一份保存到`localStorage`里面,刷新之后,如果`localStorage`里有保存的数据,取出来再替换`store`里的`state`。
let defaultCity = "上海"
try {
// 用户关闭了本地存储功能,此时在外层加个try...catch
if (!defaultCity){
// f复制一份
defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))
}
}catch(e){
console.log(e)
}
export default new Vuex.Store({
state: {
city: defaultCity
},
mutations: {
changeCity(state, city) {
state.city = city
try {
window.localStorage.setItem('defaultCity', JSON.stringify(state.city));
// 数据改变的时候把数据拷贝一份保存到localStorage里面
} catch (e) {}
}
}
})
方法五:`$attr/$listeners`
### 1-1 简介
#### 多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此Vue2.4 版本提供了另一种方法----`$attrs/$listeners`
* `$attrs`:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。
* `$listeners`:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
### 例:
```
// index.vue
<template>
<div>
<h2>王者峡谷</h2>
<child-com1 :foo="foo" :boo="boo" :coo="coo" :doo="doo" title="前端工匠"></child-com1>
</div>
</template>
<script>
const childCom1 = () => import("./childCom1.vue");
export default {
components: { childCom1 },
data() {
return {
foo: "Javascript",
boo: "Html",
coo: "CSS",
doo: "Vue"
};
}
};
</script>
```
```
//childCom1.vue
<template class="border">
<div>
<p>foo: {{ foo }}</p>
<p>childCom1的$attrs: {{ $attrs }}</p>
<child-com2 v-bind="$attrs"></child-com2>
</div>
</template>
<script>
const childCom2 = () => import("./childCom2.vue");
export default {
components: {
childCom2
},
inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
props: {
foo: String // foo作为props属性绑定
},
created() {
console.log(this.$attrs);
// { "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "前端工匠" }
}
};
</script>
```
```
// childCom2.vue
<template>
<div class="border">
<p>boo: {{ boo }}</p>
<p>childCom2: {{ $attrs }}</p>
<child-com3 v-bind="$attrs"></child-com3>
</div>
</template>
<script>
const childCom3 = () => import("./childCom3.vue");
export default {
components: {
childCom3
},
inheritAttrs: false,
props: {
boo: String
},
created() {
console.log(this.$attrs);
// {"coo": "CSS", "doo": "Vue", "title": "前端工匠" }
}
};
</script>
```
```
// childCom3.vue
<template>
<div class="border">
<p>childCom3: {{ $attrs }}</p>
</div>
</template>
<script>
export default {
props: {
coo: String,
title: String
}
};
</script>
方法六,`provide/inject` 对当前模块的介绍后续会继续补充。