前言
数据传递是框架的核心要素之一,也是在业务开发中极其重要的技术.熟练掌握所有的通信方法,是开发者必须具备的技能。
这篇文章我将会把vue2所有的通信的方法都形成简单易懂的demo。
在我的分类中,通信方法有两种大类型:
1.vue框架提供的通信方法
2.插件及其他通信方法
一.vue框架提供的通信方法:
1.props通信
props通信方法可以实现父组件向子组件传递参数,也可以实现子组件给父组件传递参数.
父传子:传递常规变量
子传父:传递函数
父组件
<template>
  <div>
    <p>父组件数字值1:{{ numValue1 }} <el-button @click="addNum">增加</el-button></p>
    <p>获取子组件的值:{{ numValue2 }}</el-button></p>
    <hr />
    <PropsChild :numValue1="numValue1" :numValue2="getChildValue"></PropsChild>
  </div>
</template>
<script>
import PropsChild from './child.vue'
export default {
  name: 'propsTest',
  components: {
    PropsChild
  },
  data() {
    return {
      numValue1: 0,
      numValue2: '父组件默认的值'
    }
  },
  methods: {
    addNum() {
      this.numValue1++
    },
    getChildValue(childValue) {
      console.log(childValue, '???子组件的数值')
      this.numValue2 = childValue
    },
    getChildValueFun() {
    }
  }
}
</script>
<style lang="less"></style> 
子组件
<template>
    <div>
      子组件
      <el-input v-model="value" @input="changeValue"></el-input>
    </div>
  </template>
  <script>
    export default{
      name:'emitChild',
      data(){
        return{
          value:'子组件默认的值'
        }
      },
      methods:{
        changeValue(){
          this.$emit('getChildValue',this.value)
        }
      }
    }
  </script>
  <style lang="less">
  
  </style> 
效果:

2.emit通信(自定义事件)
父组件
<template>
  <div>
    父组件
    <el-input v-model="value"></el-input>
    <hr>
    <EmitChild @getChildValue="getChildValue"></EmitChild>
  </div>
</template>
<script>
import EmitChild from './child.vue'
  export default{
    name:'emitTest',
    data(){
      return{
        value:'父组件默认的值'
      }
    },
    components:{
        EmitChild
    },
    methods:{
      getChildValue(childValue){
         this.value = childValue
      }
    }
  }
</script>
<style lang="less">
</style> 
子组件
<template>
    <div>
      子组件
      <el-input v-model="value" @input="changeValue"></el-input>
    </div>
  </template>
  <script>
    export default{
      name:'emitChild',
      data(){
        return{
          value:'子组件默认的值'
        }
      },
      methods:{
        changeValue(){
          this.$emit('getChildValue',this.value)
        }
      }
    }
  </script>
  <style lang="less">
  
  </style> 
效果

3.evenBus(全局事件总线)
main.js安装事件总线
new Vue({
  router,
  store,
  axios,
  //配置事件总线
  beforeCreate(){
    Vue.prototype.$bus = this
  },
  render: h => h(App),
}).$mount('#app') 
任意组件1:
<template>
    <div>
        <p>子组件1</p>
        <p>子组件1的值:{{ value1 }} <el-button @click="addNum">增加</el-button></p>
        <p>来自子组件2的值:{{ value2 }}</p>
    </div>
</template>    
<script>
 export default{
    name:'child1',
    data(){
        return{
          value1:0,
          value2:100
        }
    },
    created(){
        this.$eventBus.$on('bus_deleteNum',this.changeValue2)
    },
    methods:{
        addNum(){
            this.value1++
            this.$eventBus.$emit('bus_addNum',this.value1)
        },
        changeValue2(val){
            this.value2 = val
        }
    },
    destroyed(){
        this.$eventBus.$off('bus_deleteNum')
    }
 }
</script> 
任意组件2
<template>
    <div>
        子组件2
        <p>来自子组件1的值:{{ value1 }}</p>
        <p>子组件2的值:{{ value2 }} <el-button @click="deleteNum">减少</el-button> </p>
    </div>
</template>    
<script>
 export default{
    name:'child2',
    data(){
        return{
            value1:0,
            value2:100
        }
    },
    created(){
        this.$eventBus.$on('bus_addNum',this.changeValue1)
    },
    methods:{
        changeValue1(value){
           this.value1 = value
        },
        deleteNum(){
            this.value2--
            this.$eventBus.$emit('bus_deleteNum',this.value2)
        }
    },
    destroyed(){
        this.$eventBus.$off('bus_addNum')
    }
 }
</script> 
效果

4.ref通信
父组件
<template>
    <div>
      <p>父组件的值:{{ value }} <el-button @click="addNum">增加</el-button> </p>
      <hr>
      <Child ref="child"></Child>
    </div>
</template>
<script>
import Child from './child.vue'
  export default{
    name:'refTest',
    components:{
        Child
    },
    data(){
        return{
          value:0
        }
    },
    methods:{
      addNum(){
        this.value++
        this.$refs.child.getValue(this.value)
    }
    }
  }
</script> 
子组件
<template>
    <div>
       子组件的值{{ value }}
    </div>
</template>
<script>
  export default{
    name:'refTest',
    data(){
        return{
           value:'子组件初始化的值'
        }
    },
    methods:{
        getValue(val){
            this.value = val
        }
    }
  }
</script> 
效果

5.组件v-model通信
v-model不仅可以在输入类型标签上实现双向数据绑定,也可以在组件上实现父子组件数据通信
父组件
<template>
  <div>
    <el-input v-model="value"></el-input>
    <hr />
    <Child v-model="value" @changeValue="changeValue"></Child>
  </div>
</template>
<script>
import Child from "./child.vue";
export default {
  name: "vModel",
  components: {
    Child,
  },
  data() {
    return {
      value: "父组件默认的值",
    };
  },
  methods: {
    changeValue(data) {
      this.value = data;
    },
  },
};
</script> 
子组件
<template>
  <div>
    子组件
    <el-input v-model="inputvalue" @input="changeValue"></el-input>
  </div>
</template>
<script>
export default {
  name: "child",
  props: ["value"],
  model: {
    prop: "value",
    event: "changeValue",
  },
  data() {
    return {
      inputvalue: "子组件默认的值",
    };
  },
  create() {
    //子组件初始化同步,注释掉则为默认值
    // this.inputvalue = this.value;
  },
  methods: {
    changeValue(val) {
      this.$emit("changeValue", val);
    },
  },
  watch: {
    value: function (newVal) {
      this.inputvalue = newVal;
    },
  },
};
</script> 
效果
父子组件数据同步

6.sync修饰符通信
sync修饰符也是一种语法糖,也可以实现父子通信
父组件
<template>
  <div>
    <div>sync模块</div>
    <el-input v-model="value"></el-input>
    <hr />
    <Child :value.sync="value" @changeVal="changeVal"></Child>
  </div>
</template>
<script>
import Child from "./child.vue";
export default {
  name: "sync",
  components: {
    Child,
  },
  data() {
    return {
      value: "父组件默认的值",
    };
  },
  methods: {
    changeVal(val) {
      this.value = val;
    },
  },
};
</script>
<style lang="less" scoped>
</style>
 
子组件
<template>
  <div>
    <p>子组件</p>
    <el-input v-model="childValue" @input="changeVal"></el-input>
  </div>
</template>
<script>
export default {
  name: "child",
  props: ["value"],
  data() {
    return {
      childValue: "子组件默认的值",
    };
  },
  methods: {
    changeVal() {
      this.$emit("changeVal", this.childValue);
    },
  },
  watch: {
    value: function (newVal) {
      this.childValue = newVal;
    },
  },
};
</script>
<style lang="less" scoped>
</style>
 
效果

7.$attr 和 $listeners
父组件
<template>
  <div>
    <p>$attrs与$listeners</p>
    <p>title的值:<el-input class="inputBox" v-model="title"></el-input></p>
    <p>数值1:<el-input class="inputBox" v-model="value1"></el-input></p>
    <p>数值2:<el-input class="inputBox" v-model="value2.value"></el-input></p>
    <p>数字值:{{ num }}</p>
    <hr />
    <Child
      :title="title"
      :value1="value1"
      :value2="value2"
      v-on="$listeners"
      @changeNum="changeNum"
    ></Child>
  </div>
</template>
<script>
import Child from "./child.vue";
export default {
  name: "AttrListen",
  components: {
    Child,
  },
  data() {
    return {
      title: "标题",
      value1: "数值1",
      value2: {
        value: "对象字段2",
      },
      num: 0,
    };
  },
  mounted() {},
  methods: {
    changeNum() {
      this.num++;
    },
  },
};
</script>
<style lang="less" scoped>
.inputBox {
  width: 300px;
}
</style>
 
子组件
<template>
  <div>
    <p>子组件</p>
    <p>title的值:{{ $attrs.title }}</p>
    <p>数值1:{{ $attrs.value1 }}</p>
    <p>数值2:{{ $attrs.value2.value }}</p>
    <el-button @click="changeNum">修改值</el-button>
  </div>
</template>
<script>
export default {
  name: "child",
  created() {},
  methods: {
    changeNum() {
      console.log(this.$listeners, "this.$listeners的值子组件");
      //   this.$emit("changeNum");
      this.$listeners.changeNum();
    },
  },
};
</script>
<style lang="less" scoped>
</style>
 
效果

8. provide和inject
可以实现上层组件对于后代组件的值传递,相当于穿透传递
祖先组件(这里使用app.vue)
<template>
  <div id="app">
    <router-view></router-view>  </div>
</template>
<script>
export default {
  name: 'App',
  provide(){
    return{
      penetrateVal:'穿透的值'
    }
  },
  components: {
  },
  created(){
  },
  methods:{
  }
}
</script>
 
任意后代组件
<template>
    <div>
        <p>穿透值:{{ penetrateVal }}</p>
    </div>
</template>
<script>
export default {
    name: 'prodvideAndInject',
    inject: ['penetrateVal'],
    methods: {
    }
}
</script>
<style></style> 
效果

9.作用域插槽
父组件
<template>
    <div>
        父组件
        <hr>
        <Child>
            <template slot-scope="num">
                <span>父组件中使用:{{ num.data }}</span>
            </template>
        </Child>
    </div>
</template>
<script>
import Child from './child.vue'
export default {
    name: 'slotVal',
    components: {
        Child
    },
}
</script>
 
子组件
<template>
    <div>
      <slot :data="num"></slot>
      <el-button @click="add">子组件中点击增加</el-button>
    </div>
   </template>
   <script>
    export default{
       name:'Child',
       data(){
        return{
            num:0
        }
       },
       methods:{
        add(){
            this.num ++
        }
       }
    }
   </script>
    
效果

以上都是vue框架自带的通信方法,但是在开发业务中是不够用的,因为还需要有一些全局的通信方法,去实现业务逻辑。
二.插件和其他的通信方法
这里的一些方法就不写具体的demo和效果图了,只是做一个简单的介绍
1.vue-router
路由传参是实现参数传递的重要途径,想必是个vue开发者肯定都用过吧
传递方
this.$router.push({path:'/demo1',query:{id:1}}) 
接收方
console.log(this.$route.query.id) 
2.vuex
这里也不细说,绝大多数vue2的项目都用vuex来做全局的一个参数存储,例如用户信息,token等等,都离不开vuex
vuex地址:Vuex 是什么? | Vuex
3.Pinia
本质是和vuex是一样的,不过做了优化,在vue3项目中会使用的更多,也可以在vue2中使用
pinia地址:介绍 | Pinia 中文文档
4.Storage
这里是对localStroage和sessionStorage的统称,这两个都是浏览器本身的方法,都具有持久化保存的作用。最具体的就是用户token校验,判断是否登录过的时候,基本都得用Storage。
5.cookie
和Storage差不多的东西,不细说
6.redux
默认是react的配套插件,虽然没见过什么大神在vue中用redux,但是官方介绍确实可以在vue中使用redux。
我是没有操作过,想看的可以参考引用其他人的文档:Redux vue - 老白网络
7.PubSub(万金油)
镇场子的大哥,任何框架,甚至原生都可以使用的一个经典信息传递插件,设计模式的经典之作,跨越语言的存在,别说前端了,你在redis上也都会遇到pubsub.
在使用上和eventbus像,简单好用。我就不费劲写了,直接参考别人的文档吧:一、PubSub 的Vue使用方式_vue pubsub-CSDN博客
结束语
方法有很多,但具体如何使用,看场景,看业务,灵活变通。
其实,不只是props可以实现双向传递参数,$emit和ref都是可以的,你只需要换个思路就可以了,我这里还写了一篇拓展:,使用props,emit和ref实现双向数据通信。
有兴趣的可以去看看:vue2组件通信中的一些拓展(props,emit,ref父子双向传参)-CSDN博客



















