Vue 组件的几种信息传递

365 阅读2分钟

单向数据流

在vue中,当父组件通过方法改变自己的数据时,如果子组件也用到了这个数据,那么子组件也会跟着更新发生改变;父组件的数据流动到了子组件,子组件更新,这就是vue数据的单向数据流

父子组件的信息交互

  • 父传子:可通过属性传递,子组件先在行内用动态属性接收,然后在组件中用props注册之后才可以使用
<html>
    <body>
    <div id="app">
    <my-child :mydata="name"></my-child>
  </div>
  <template id="myChild">
     <p class="bg">{{mydata}}</p>
  </template>
    </body>
    <script>
    let my-child={
        template:'#myChild',
        //简写:props:['mydata'],
        props:{ //子组件需要props 接收一下才能用
            mydata:{
                type:[Number,String], //数据类型检测
                default:'大妖',  //默认值
                required:true,  //是否为必传
                validator(val){  //自定义规则使用的属性;不管成功与否都会继续渲染页面
                    //val值为传进来的mydata
                    return val.length>2
                }
            }
        }
    }
    let vm=new Vue({
        el:'#app',
        data:{
            name:'小妖',
        },
        components:{  //[kəmˈpəʊnənts]组件,子组件需要在父组件components注册才能使用
            //全写:my-child:my-child,
            my-child
        }
    })
    </script>
</html>

新增父传后代

  • 把要传递的属性也要先在子组件的行内属性接收一下,子组件通过$attrs 获取传递的属性值,通过$listeners 获取传递的方法
  • 孙子组件则需要在行内通过v-bind 接收属性,v-on 接收方法
<html>
    <body>
        <div id="app">
	    <child :val="a" user='大妖' age="18" @hhh='fn'></child>
        </div>
    </body>
    <script>
    let  grandson={
	    template:"<div>{{$attrs.user}}<button @click='$listeners.f'>改一改</button></div>"
    }
    let  child  ={
	        data(){
  	        return {}
        }, 
        components:{
  	        grandson
        },
      // props:["val"],
      // 可以通过v-bind将child的行间的属性以对象的形式传递给grandson组件
      // v-bind :属性 v-on : 事件方法
        template:"<div><grandson v-bind='$attrs' v-on='$listeners'></grandson></div>"
    }
    let  vm = new Vue({
    	el:"#app",
      data:{
      	a:"小妖"
      },
      components:{
      	child
      },
      methods:{
      	fn(){
        	this.a="小妖现世"
        }
    	}
    })
    </script>
</html>

子传父

  • 通过自定义事件发布订阅,子组件通过this.$emit("方法名") 通知父组件方法执行;简写用.sync修饰符::x.sync="xx" ;在子件中修改用,写在方法中 this.$emit("update:x", mm);mm为要修改的值
<html>
    <body>
        <div id="app">
           <!-- 自定义事件:自己起的名字 -->
           {{money}}
           <!-- 订阅 -->
           <son :m="money" @changemoney="fn"></son>
        	//sync修饰符
        	<!--<son v-bind:m="money"  @update:m="money=$event"></son>-->
          <son :m.sync="money"></son>
        </div>
    </body>
    <script>
            let son = {
            data(){
              return {}
            },
            methods:{
               add(){
                 // 发布订阅==>自定义事件
                 // 子组件订阅了父组件的方法;在子组件中,触发了父组件的方法,让父组件改变自己的数据;
                 // 发布让订阅的方法执行
                 // $emit: 是vue实例的一个方法;发布自定义事件的方法
                 this.$emit("changemoney");
                 //update:m 自定义事件的名字
                 //888是最新值,传给了$event,触发事件必须是update:m
                 this.$emit("update:m",888);
               }   
           },
           props:["m"],
           template:"<div>{{m}}<button  @click='add'>多要点</button></div>"
        }
        let vm = new Vue({
          el:"#app",
          data:{
          	money:666
          },
          methods:{
            fn(){
            	this.money=888;
            }
          },
          components:{
          	son
          }
        });
    </script>
</html>

兄弟组件之间的交互

<body>
    <div id="app">
       <bro1></bro1>
       <bro2></bro2>
    </div>
<body>
<script>
let  eventBus = new Vue;// 这个就是个容器,能够把两个兄弟进行关联
let bro1 = {
	data(){
    return {
    	color:"红色"
    }
	},
  methods:{
  	changeBro2(){
    	// 让eventBus中的订阅的changeRed执行
      eventBus.$emit("changeRed");
    }
	},
  template:"<div>{{color}}<button @click='changeBro2'>变红色</button></div>"
};
let bro2= {
  data(){
    return {
     	color:"绿色"
    }
  },
  created(){
  	// created能获取到methods方法
		// $on : 订阅;把bro2的事件放到了eventBus这个实例的事件池中
    eventBus.$on("changeRed",this.changeRed)
  },
  methods:{
  	changeRed(){
    	this.color="红色";
    }
  },
  template:"<div>{{color}}<button>变绿色</button></div>"
}
new Vue({
	el:"#app",
  data:{},
  methods:{},
	components:{
  	bro1,
    bro2
  },
})
</script>

Vuex:专门解决vue项目的公共数据管理的库

  • store 是一个实例,state:公共数据
  • 创建Vuex的实例,并注入vue实例中,可以在子组件的钩子函数中使用
  • 在vuex中规定,要改state的数据,必须通过commit来提交一个mutation
let store=new Vuex.Store({
  state:{
    count:100
  },
  getters:{
    //相当于computed ,根据数据依赖技术,有缓存机制
    str(state){
      //str 执行时,默认会传递进来一个state,第一个参数都是当前store的state
      return state.count%2===0?"偶":"奇"
    }
  },
  mutations:{
    //mutations 是同步事物
    add(state,payload){
      //第一项state 默认是store 中的state
      //payload :调用函数传递的参数
      //vue中唯一改变state的数据方法就是提交mutations
      state.count+=payload;
    }
  },
  actions:{
    //action提交的是mutation, 不直接更改状态
    //action可以异步操作,函数接受一个与 store 实例具有相同方法和属性的 context 对象
    increase(context,option){
      //context === store的实例,当然也可以把{commit}结构出来
      setTimeout(()=>{
        context.commit("add",option);
      },200)
    }
  }
})  
let child={
  data(){
    return{}
  },
  computed:{
    //vuex.mapState 返回值是一个对象
    //组件的计算属性依赖store中的数据变化
    ...Vuex.mapState(["count"]),
    ...Vuex.mapGetters(["str"])
  },
  methods:{
   // mutations中的方法是commit提交
   // addCount(val){
   //     this.$store.commit("add",val);
   // }
   // actions中的方法是dispatch派发
   // addCount(val){
   //通知vuex中add 方法执行
   //     this.$store.dispatch("increase");
   // }
   ...Vuex.mapMutations(["add"])
   /*将store中的add方法映射到自己的methods,
   并且methods这个方法名字叫aaa*/
   // ...Vuex.mapMutations({aaa:"add"})
   ...Vuex.mapActions(["increase"])
  },
  template:"<div>{{count}}<p @click='addCount(2)'>累加</p><div>"
}
// 每个模块独立的state;但是getters  mutations  actions 会注册到全局上;
// 每一个module都可以有自己独立的state getters  mutations  actions 
// 如果私有模块和公有模块存在同样的方法,会把两个方法整合到一起;
let vm=new Vue({
  el:"#app",
  data:{},
  created(){
    //可以在生命周期的任何一个钩子函数中使用vuex
    console.log(this.$store.state.count)
  },
  store //把store注入vm实例中
})