默认状况下数据不能在组件之间共享,可是能够经过如下几种方法实现组件之间的数据传递:vue
props
父子组件之间的数据传递:vuex
父传子数组
父绑定数据,子接收数据缓存
详细的格式:安全
Props:{
数据项名字:{
type:类型。指明从父组件中传递过来的数据必须是什么类型。它的取值是:Object,Array,String,Number,Boolean 都是构造器。不要写成字符串
default://默认值。当父组件没有传数据时,就用这个值
required:true/false 。是否必须必定要传递过来
}
}
复制代码
子传父bash
this.$emit
发出这个事件,发出事件的同时能够携带数据 (this.$emit("事件名",附加的数据))父传子传孙,只能一级一级的传,不能跨级传app
示例:dom
<body>
<!-- ******************************************************************** -->
<!-- 父传子 -->
<!-- <div id="app">
<h1>父组件 ->数据:{{num}}</h1>
<hr>
<son :sonnum="num" :sonname="name"></son>
</div>
<template id="son">
<div>
子组件 -> 数据:{{mysum}} -> {{sonname}}
<button @click="sonnum=200">修改数据为200</button>
<h2>{{mysum}}</h2>
<button @click="mysum=200">修改数据为200</button>
</div>
</template> -->
<!-- ******************************************************************** -->
<!-- 子传父 -->
<div id="app">
<h1>父组件</h1>
<son @submitmsg="addmsg"></son>
<h2>{{a}}</h2>
</div>
<template id="son">
<div>
<h3>子组件</h3>
<button @click="fashe">发射</button>
</div>
</template>
<script>
// 子传父************************************************************************
let Son = {
template: "#son",
data() {
return {
num:111,
}
},
methods: {
fashe() {
this.$emit("submitmsg", this.num)
}
}
}
let vm = new Vue({
el: "#app",
data: {
a: 0,
},
methods: {
addmsg(info) {
this.a = info
}
},
components: {
Son,
}
})
// 父传子*************************************************************************
// let Son = {
// template: "#son",
// data() {
// return {
// mysum: this.sonnum,
// }
// },
// props: {
// sonnum: Number,
// sonname: {
// type: String,
// // required:true,
// default: "jun",
// }
// },
// methods: {
// }
// }
// let vm = new Vue({
// el: "#app",
// data: {
// num: 100,
// name: "fan"
// },
// methods: {
// },
// components: {
// Son,
// }
// })
//********************************************************************************
</script>
</body>
复制代码
这么多东西,相信你也懒得看,你能够本身建一个文件,复制到里面测试一下异步
$attrs
若是想要把父组件的数据传递给子子组件,若是使用props
绑定来进行信息的传递,显然是比较繁琐的
为了解决该需求,引入了 $attrside
$attrs
能够收集父组件中的全部传过来的属性除了那些在组件中没有经过 props 定义的。
示例:
首先有三个组件A-B-C,而后想A中的属性传入C中,基本的作法是这样的,一层一层经过 props 往下传递
<template>
<div id="app">
A{{msg}}
<component-b :msg="msg"></component-b>
</div>
</template>
<script>
let vm = new Vue({
el: "#app",
data: {
msg: "100"
},
components: {
ComponentB: {
props: ["msg"],
template: `<div>B<component-c :msg="msg"></component-c></div>`,
components: {
ComponentC: {
props: ["msg"],
template: "<div>C{{msg}}</div>"
}
}
}
}
});
</script>
复制代码
ComponentB 组件中并无使用到父组件传递过来的属性 msg,可是这样写就是想把属性再传递给ComponentC,那么除了这种写法还能够给ComponentC绑定$attrs
属性。
<script>
let vm = new Vue({
el: "#app",
data: {
msg: "100"
},
components: {
ComponentB: {
inheritAttrs: false,
template: `<div>B<component-c v-bind="$attrs"></component-c></div>`,
components: {
ComponentC: {
props: ["msg"],
template: "<div>C{{msg}}</div>"
}
}
}
}
});
</script>
复制代码
这样就能够很方便的作到数据传递,使用起来也比较简单,避免多写 props
的痛苦
通常在使用 $attrs
时,都会加上 inheritAttrs:false
它的做用就是没有用到的数据,就不会显示在DOM结构上
$listeners
说完了 $attrs
,知道了怎么把数据从A传递给C,那么此时咱们又想到了一个问题,怎么把C组件的信息同步到A组件呢? 这时就用到了 $listeners
当组件的根元素不具有一些DOM事件,可是根元素内部元素具有相对应的DOM事件,那么能够使用 $listeners
获取父组件传递进来的全部事件函数,再经过v-on="xxx"绑定到相对应的内部元素上便可。 简单的来讲,就是父组件向子组件传递的全部方法都存在在$listeners
中
有时候咱们会使用 .native
修饰符把原生事件绑定到组件上,可是这样存在弊端,若是组件的根元素不能使用 某事件时,这个绑定就会失效,并且还不容易控制它的事件范围,因此咱们通常不用这个修饰符
示例:
//父组件
<template>
<div>
ParentPage
<button @click="handleClick">ParentClick</button>
<Child @customClick="handleClick" />
</div>
</template>
<script>
import Child from "./Child";
export default {
name: "ParentPage",
components: {
Child
},
methods: {
handleClick() {
alert("hello");
}
}
};
</script>
复制代码
//子组件
<template>
<div>
ChildPage
<!-- <button @click="$emit('customClick')">ChildClick</button> -->
<button @click="$listeners.customClick">ChildClick</button>
</div>
</template>
<script>
import SubChild from "./SubChild.vue";
export default {
name: "ChildPage",
components: {
SubChild
},
data() {
return {};
}
};
</script>
复制代码
当多层组件引用时,子组件传递父组件方法 v-on="$listeners"
至子子组件
// 子组件
<template>
<div>
ChildPage
<!-- <button @click="$emit('customClick')">ChildClick</button> -->
<button @click="$listeners.customClick">ChildClick</button>
<SubChild v-on="$listeners" />
</div>
</template>
<script>
import SubChild from "./SubChild.vue";
export default {
name: "ChildPage",
components: {
SubChild
},
data() {
return {};
}
};
</script>
复制代码
// 子子组件
<template>
<div>
SubChildPage
<button @click="$listeners.customClick">SubChildClick</button>
</div>
</template>
<script>
export default {
name: "SubChildPage",
data() {
return {};
}
};
</script>
复制代码
相信若是好好看了以上的代码,你就会理解的
$emit
1.父组件可使用props
把数据传给子组件
2.子组件可使用 $emit
触发父组件的自定义事件
vm.$emit( event, arg ) //触发当前实例上的事件
vm.$on( event, fn );//监听event事件后运行 fn;
复制代码
示例:
//父组件
<template>
<div>
<div>$emit子组件调用父组件的方法并传递数据</div>
<h1>父组件数据:{{msg}}</h1>
<emit-ch @updateInfo="updateInfo" :sendData="msg"></emit-ch>
</div>
</template>
<script>
import emitCh from "./$emitCh";
export default {
name: "emitFa",
components: { emitCh },
data() {
return {
msg: "北京"
};
},
methods: {
updateInfo(data) {
// 点击子组件按钮时触发事件
console.log(data);
this.msg = data.city; // 改变了父组件的值
}
}
};
</script>
复制代码
<template>
<div class="train-city">
<h3>父组件传给子组件的数据:{{sendData}}</h3>
<br />
<button @click="select()">点击子组件</button>
</div>
</template>
<script>
export default {
name: "emitCh", // 至关于一个全局 ID,能够不写,写了能够提供更好的调试信息
props: ["sendData"], // 用来接收父组件传给子组件的数据
data() {
return {};
},
computed: {},
methods: {
select() {
let data = {
city: "杭州"
};
this.$emit("updateInfo", data); // select事件触发后,自动触发updateInfo事件
}
}
};
</script>
复制代码
整体来讲,就是用子组件触发父组件里的方法,子组件里面this.$emit
里的this
,就指的是父组件
$refs
$refs
的使用方法就是在元素或组件标签上添加ref
属性指定一个引用信息,引用信息将会注册在父组件的$refs
对象上,在js中使用$refs
来指向DOM元素或组件实例;
首先给你的子组件作标记:
<firstchild ref="test"></firstchild>
而后在父组件中,经过 this.$refs.test
就能够访问这个子组件,包括访问子组件里的 data 里的数据,而且还能够调用它的函数
$parent
与$children
说了上面的 $refs
接下来讲说 $parent
与$children
this.$parent
能够查找当前组件的父组件。this.$children
能够查找当前组件的直接子组件,能够遍历所有子组件, 须要注意 $children
并不保证顺序,也不是响应式的。固然你也可使用 this.$root
来查找根组件,并能够配合$children
遍历所有组件。
注:这两个都是不限制距离的,就是说能够直接查找到最外层数据或者最内层数据,固然,若是你能很清楚的知道子组件的顺序,你也能够用下标来操做
示例:
//父组件
<template>
<div class="game">
<h2>{{ msg }}</h2>
<LOL ref="lol"></LOL>
<DNF ref="dnf"></DNF>
</div>
</template>
<script>
import LOL from "@/components/game/LOL";
import DNF from "@/components/game/DNF";
export default {
name: "game",
components: {
LOL,
DNF
},
data() {
return {
msg: "Game",
lolMsg: "Game->LOL",
dnfMsg: "Game->DNF"
};
},
methods: {},
mounted() {
//注意 mounted
//读取子组件数据,注意$children子组件的排序是不安全的
console.log(this.$children[0].gameMsg); //LOL->Game
//读取命名子组件数据
console.log(this.$refs.dnf.gameMsg); //DNF->Game
//从根组件查找组件数据
console.log(this.$root.$children[0].msg); //APP
console.log(this.$root.$children[0].$children[0].msg); //Game
console.log(this.$root.$children[0].$children[0].$children[0].msg); //Game->LOL
console.log(this.$root.$children[0].$children[0].$children[1].msg); //Game->DNF
}
};
</script>
复制代码
//子组件LOL
<template>
<div class="lol">
<h2>{{ msg }}</h2>
</div>
</template>
<script>
export default {
name: "LOL",
data() {
return {
msg: "LOL",
gameMsg: "LOL->Game"
};
},
methods: {},
created() {
//直接读取父组件数据
this.msg = this.$parent.lolMsg;
}
};
</script>
复制代码
//子组件DNF
<template>
<div class="dnf">
<h2>{{ msg }}</h2>
</div>
</template>
<script>
import Bus from "../../utils/bus.js";
export default {
name: "DNF",
data() {
return {
msg: "DNF",
gameMsg: "DNF->Game"
};
},
methods: {},
created() {
//从根组件向下查找父组件数据
this.msg = this.$root.$children[0].$children[0].dnfMsg;
//this.msg = this.$children.dnfMsg;
}
};
</script>
复制代码
上面的有些是使用下标的,固然也能够不使用下标,直接 $parent
获取父组件的实例 $children
获取全部的子组件
provide
与inject
provide
和inject
使用场景也是组件传值,尤为是祖父组件--子子组件等有跨度的组件间传值,单向传值(由provide
的组件传递给inject
的组件)。 不推荐使用
示例:
//父组件
<template>
<div>
<father-dom>
</father-dom>
</div>
</template>
<script>
import sonDom from "./sonDom.vue";
export default {
provide: {
fooNew: "bar"
},
data() {
return {};
},
components: { sonDom },
methods: {}
};
</script>
复制代码
//子组件
<template>
<div>
<child-dom></child-dom>
</div>
</template>
<script>
import childDom from "./childDom.vue";
export default {
name: "son-dom",
components: { childDom }
};
</script>
复制代码
//子子组件
<template>
<div>
<p>fooNew:{{fooNew}}</p>
</div>
</template>
<script>
export default {
name: "childDom",
inject: ["fooNew"],
methods: {}
};
</script>
复制代码
Vuex
接下来讲一下咱们的压轴好戏,最 6 的一种数据传递方式:Vuex
当咱们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏。
对于问题一,传参的方法对于多层嵌套的组件将会很是繁琐,而且对于兄弟组件间的状态传递无能为力。对于问题二,咱们常常会采用父子组件直接引用或者经过事件来变动和同步状态的多份拷贝。以上的这些模式很是脆弱,一般会致使没法维护的代码。
所以,咱们为何不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,咱们的组件树构成了一个巨大的“视图”,无论在树的哪一个位置,任何组件都能获取状态或者触发行为!
另外,经过定义和隔离状态管理中的各类概念并强制遵照必定的规则,咱们的代码将会变得更结构化且易维护。
Vuex
//获取state
this.$store.state.count
//vuex的辅助方法
import { mapState } from 'vuex'
computed:mapState([
'count'
])
复制代码
//直接使用
this.$store.getters.doneTodosCount
//使用辅助方法
import { mapGetters } from 'vuex'
computed:mapGetters({
doneCount: 'doneTodosCount'
})
复制代码
//触发mutations
this.$store.commit('xxx')
//辅助函数
import { mapMutations } from 'vuex'
methods:mapMutations(['increment' ])
复制代码
我的理解以下:
若是有异步的复杂逻辑而且能够重复调用就使用Action。
//触发action
store.dispatch('increment')
//辅助函数
import { mapActions } from 'vuex'
methods:mapActions(['increment' ])
复制代码
为了解决以上问题,Vuex 容许咱们将 store 分割成模块(module)。每一个模块拥有本身的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行一样方式的分割。
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
复制代码
相信看了本篇文章,你确定会对组件之间的数据传递和通讯有了更深的理解