Vue组件之间传递数据的五种方式_详解
Vue 组件之间数据传递的几种方式:父组件向子组件传递数据,使用props属性;子组件向父组件中传递数据,在子组件中使用$emit派发事件,父组件中使用v-on 监听事件;缺点:组件嵌套层次多的话,传递数据比较麻烦。祖先组件通过依赖注入(inject / provide)的方式,向其所有子孙后代传递数据;缺点:无法监听数据修改的来源,不支持响应式。通过属性$root / $parent /...
Vue 组件之间数据传递的几种方式:
- 父组件向子组件传递数据,使用props属性;子组件向父组件中传递数据,在子组件中使用$emit派发事件,父组件中使用v-on 监听事件;缺点:组件嵌套层次多的话,传递数据比较麻烦。
- 祖先组件通过依赖注入(inject / provide)的方式,向其所有子孙后代传递数据;缺点:无法监听数据修改的来源,不支持响应式。
- 通过属性$root / $parent / $children / ref,访问根组件、父级组件、子组件中的数据;缺点:要求组件之间要有传递性。
- 通过事件总线(event bus)的方式,可以实现任意两个组件间进行数据传递;缺点:不支持响应式,这个概念是vue1.0版本中的,现在已经废弃。
- 通过 VueJs 的状态管理模式 Vuex,实现多个组件进行数据共享,推荐使用这种方式进行项目中各组件间的数据传递。
下面详细介绍数据传递的几种方式:
1. props/$emit
prop 是在组件上注册的一些自定义的 attribute。就像下面的 :sub-num 。
<div :sub-num="num"></div>
1.1 父组件向子组件中传递数据
- 一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。
- 当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个属性(例如下面的 subNum),通过属性名,我们就能够在组件实例中访问这个值。
- 父组件向子组件中传递数据,可以在子组件中通过设置props属性来接收传递过来的数据。
<div id="app">
<div>{{num}}</div>
<!-- 将父组件中的num,传递给子组件中的sub-num -->
<blog-count :sub-num="num" :sub-user="user"></blog-count>
</div>
<script>
const blogCount={
//子组件中通过props属性接收父组件传递过来的数据
props:["subNum","subUser"],
template:`<div>
<p>这是从父组件传进来的数字:{{subNum}}</p>
<p>这是从父组件传进来的对象:{{subUser.name}}-{{subUser.age}}</p>
</div>`,
}
var vm=new Vue({
el:"#app",
data:{
num:2,
user:{
name:"zhangsan",
age:18
}
},
components:{
blogCount
}
})
</script>
1.2 子组件向父组件传递数据
- 子组件向父组件传递数据,通过 $emit派发事件,父组件中通过 v-on 接收该事件,拿到传递的数据。
- 语法:
$emit(eventName,data)
,第一个参数为事件名称,需要跟父组件中 v-on 监听的事件名称一致;第二个参数为要传递的数据。
<div id="app">
<div>{{num}}</div>
<!-- 通过 v-on 监听事件-->
<blog-count @countchange="changeHandle"></blog-count>
</div>
<script>
const blogCount={
template:`<button @click="clickHandle">点击接收子组件传递过来的数据</button>`,
data(){
return {num:6}
},
methods: {
clickHandle(){
//使用 $emit派发事件
this.$emit("countchange",this.num);
}
}
}
var vm=new Vue({
el:"#app",
data:{
num:0
},
methods: {
changeHandle(data){
this.num=data;
}
},
components:{
blogCount
}
})
</script>
1.3 .sync 修饰符
如果使用 update:myPropName 的模式触发事件,上面的代码可以写成下面这样:
<div id="app">
<div>{{num}}</div>
<blog-count :num="num" @update:countchange="num=$event"></blog-count>
<!--最终可以简写为-->
<!-- <blog-count :countchange.sync="num"></blog-count> -->
</div>
<script>
const blogCount={
template:`<button @click="$emit('update:countchange',num)">点击接收子组件传递过来的数据</button>`,
data(){
return {num:6}
}
})
var vm=new Vue({
el:"#app",
data:{num:0}
}
</script>
提取出组件的代码为:
<blog-count :num="num" @update:countchange="num=$event"></blog-count>
为了方便起见,我们为这种模式提供一个缩写,即 .sync 修饰符:
<blog-count :countchange.sync="num"></blog-count>
子组件向父组件传递数据,也可以通过使用 .sync 修饰符来完成。
2. 依赖注入 provide inject
- 这对选项是2.2.0版本新增的。需要一起使用,它允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
- 主要解决了跨级组件间的通信问题。
- 在祖先组件中增加属性
provide
,它的属性值是一个对象或返回一个对象的函数。该对象包含了给子组件要传递的数据。 - 在子组件中增加属性
inject
,用来接收数据,它的选项是一个字符串数组,或一个对象。
<div id="app">
<div>{{num}}</div>
<blog-count></blog-count>
</div>
<script>
const blogCount={
//子组件中使用inject属性来接收数据
inject:["num"],
template:`<div>{{num}}</div>`
}
var vm=new Vue({
el:"#app",
data:{
num:10
},
//父组件中使用provide属性存放要传递的数据
provide:function(){
return {
num:this.num
}
},
components:{
blogCount
}
})
</script>
依赖注入 provide inject,这种方式的缺点:
- 祖先组件不需要知道哪些后代组件使用它提供的属性。
- 后代组件不需要知道被注入的属性来自哪里。
- 会将应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难。
- 所提供的属性是非响应式的。
3. $root / $parent / $children / ref
- 通过
$root
属性访问根实例 new Vue()。 - 通过
$parent
属性访问父组件的实例。 - 通过
$children
属性访问当前实例的直接子组件。需要注意 $children 并不保证顺序,也不是响应式的。 - 通过
$refs
属性访问子组件中的数据,子组件标签上加 ref 的属性。例如在子组件的dom元素上增加属性ref = "abc"
,就可以使用this.$refs.abc
拿到这个子组件的实例。 - ref,如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。
- $refs 只会在组件渲染完成之后生效,并且它们不是响应式的。
<div id="app">
<blog-count></blog-count>
</div>
<script>
const blogItem={
template:`<div>{{$root.num}}-{{$parent.num}}</div>`,
data(){
return {
num:3
}
},
mounted() {
//访问根实例中的数据
console.log("Vue.num:"+this.$root.num);
//访问父级组件中的数据
console.log("blogCount.num:"+this.$parent.num);
//调用父级组件中的方法
this.$parent.start()
},
}
const blogCount={
template:`<div><blogItem ref="refItem"></blogItem></div>`,
data(){
return {num:6}
},
components:{
blogItem
},
methods: {
start(){
console.log("blogCount start...");
console.log(this.$children[0].num)
}
},
mounted() {
//访问子组件的实例
console.log(this.$refs.refItem.num);
},
}
var vm=new Vue({
el:"#app",
data:{
num:0
},
components:{
blogCount
}
})
</script>
4. event bus 事件总线
事件的触发器,vue 1.0版本使用比较多,现在已经不用。
这种方法可以看作是通过一个空的实例 new Vue()
作为事件总线(事件中心),用它来派发和监听事件,可以实现任何组件间的通信,包括父子、兄弟、跨级。缺点:这种传递数据的方式不是响应式。
<div id="app">
<div>{{num}}</div>
<blog-count></blog-count>
<button @click="clickHandle">send</button>
</div>
<script>
//创建一个空的vue实例
const eventbus=new Vue();
//子组件
const blogCount={
data(){
return {num:1}
},
template:`<div>{{num}}</div>`,
mounted() {
//监听事件
eventbus.$on("message",(msg)=>{
this.num=msg;
})
}
}
var vm=new Vue({
el:"#app",
data:{
num:10
},
components:{
blogCount
},
methods: {
clickHandle(){
//派发事件
eventbus.$emit('message',this.num)
}
}
})
</script>
5. vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它的状态存储是响应式的。采用集中式存储管理应用的所有组件的状态,也就是说,对数据的所有操作都要在vuex中进行。
5.1 vuex 原理
vuex 实现了一个单向数据流,在全局拥有一个 State 用来存放数据,当组件用同步的方式更改 State 中的数据时,必须通过 Mutation 进行。当使用异步方式(例如 ajax请求)修改数据,必须先经过 Actions ,再由 Actions 经过 Mutation来操作。
5.2 store
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。
const store = new Vuex.Store({
state: {
//相当于自定义组件中的data
},
getters:{
//相当于自定义组件中的computed
},
mutations: {
//相当于自定义组件中的methods,只能做同步的操作
//对state中的数据进行修改
},
actions: {
//异步操作,例如ajax请求
//使用commit 触发 mutations
}
})
5.3 vuex的核心概念
5.3.1 State
- State相当于自定义组件中的data,用来存放数据,页面中所有的数据都是从该对象中进行读取。
- 在组件中使用
store.state
/this.$store.state
来读取vuex中的数据。
//创建 vuex 实例
const store = new Vuex.Store({
state: {
count:3
}
}
//创建 vue 实例
const app = new Vue({
el: '#app',
store,
components: { Counter },
template: `<div class="app"><counter></counter></div>`
})
//自定义组件
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}
5.3.2 getter
- getter 相当于自定义组件中的computed,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
- Getter 接受 state 作为其第一个参数,也可以接受其他 getter 作为第二个参数。
- 可以使用
store.getters
/this.$store.getters
访问这些值。
const store = new Vuex.Store({
state: {
sourceList:[],
pageNo:1,
pageSize:5,
},
getters: {
dataList:(state)=>{
//计算分页后的数据
let start=(state.pageNo-1)*state.pageSize;
let end=start+state.pageSize;
let result=state.sourceList.slice(start,end);
return result;
},
pages:(state,getters)=>{
//计算页码的范围
return Math.ceil(getters.dataList.length/state.pageSize);
}
}
})
5.3.3 Mutation
- mutation 相当于自定义组件中的methods。
mutation是更改 Vuex 的 State 中数据的唯一方法
。- 通过
store.commit(type,data)
调用 mutation,第一个参数为事件类型,需要和mutation中函数名称一致;第二个参数为要传递的参数。 - mutation中的函数接受 state 作为其第一个参数。
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})
const vm = new Vue(){
methods:{
change(){
store.commit("increment");
}
}
5.3.4 Action
- action 主要用来操作所有的异步请求。
- action 不能直接对State 中的数据进行操作,只能通过commit(type,data) 方法调用 mutation。
- action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
- 通过
store.dispatch(type)
方法触发action,参数为事件类型,需要和action中函数名称一致。
const store = new Vuex.Store({
state: {
dataList:[]
},
mutations: {
render(state,data){
state.dataList=data;
},
},
actions: {
getData({commit}){
fetch("./data.json").then((res)=>res.json()).then((res)=>{
commit("render",res.data);
})
}
}
})
const vm = new Vue(){
created(){
store.dispatch("getData");
}
}
总结:
- 父组件和子组件间通信:
- 父向子传递数据是通过props,子向父传递数据是通过event($emit);
- 通过父链/子链进行数据传递($parent / $children);
- 通过 ref 也可以访问组件实例;
- 依赖注入:provide / inject;
- 兄弟组件间通信:
- event bus
- Vuex
- 跨级组件间通信:
- event bus;
- Vuex;
- 依赖注入:provide / inject;
更多推荐
所有评论(0)