美文网首页
vue的父子组件通信

vue的父子组件通信

作者: 路耀广的前端微博 | 来源:发表于2018-04-26 21:42 被阅读0次

    我们知道vue的精华就在于组件话开发,第一次看vue的时候懵懵懂懂,只是大概了解了怎么去用。今天闲来无事第二次去看发现了很多精华的东西,现在写出来与大家分享下。
    vue的组件间通信一般分为父子组件通信和兄弟组件通信。父子组件通信是一层一层向下传递prop然后一层层通过事件向上传递。而兄弟组件之间通信一般通过vuex进行,在本文中就不多赘述。

    单向数据流

    这是父子组件的核心概念,prop是单向绑定的。当父组件的属性发生变化的时候,会传导到子组件。但是反之,为了防止子组件无意间修改来父组件的状态,从下往上的数据流是不允许的。
    下面我们举个栗子:

      parent
     <template>
       <div id="app">
         parent:<input type="text" v-model="txt" />
         <child :txt='txt' />
       </div>
    </template>
    <script>
    import child from "@/components/child";
    export default {
      name: "App",
      data() {
        return {
          txt: "test"
        };
      },
      components: { child }
    };
    </script>
    
      child
    <template>
      <div>
        child:<input type="text" v-model="txt">
      </div>
    </template>
    <script>
    export default {
      name: "child",
      data() {
        return {};
      },
      props: ["txt"]
    };
    </script>
    

    当父组件属性改变时,会传导进子组件。而子组件的属性改变时,会报错。
    那么如果是不是局的props有点鸡肋了,只能初始化组件的时候用,在子组件内并不能进行操作。
    其实还是用两种办法去操作props的:
    1.定义一个局部变量,并用props初始化它,以后操作这个局部变量。

      child
    <template>
      <div>
           <button @click="child_txt++">click!</button>
           <p>{{child_txt}}</p>
      </div>
    </template>
    <script>
    export default {
      name: "child",
      data() {
        return {
          child_txt:this.txt
        };
      },
      props: ["txt"]
    };
    </script>
    

    当我们将0传入子组件时,然后点击按钮看一下变化。



    2.定义一个计算属性,处理prop的值并返回。

    <template>
      <div>
        <h1>{{formatter}}</h1>
      </div>
    </template>
    <script>
    export default {
      name: "child",
      data() {
        return {
          child_txt: this.txt
        };
      },
      props: ["num"],
      computed:{
        formatter(){
          return parseInt(this.num)+'.00';
        }
      }
    };
    </script>
    

    自定义事件

    了解了组件对单向数据流,我们知道了组件从上至下是通过prop传递进行通信的。那从下往上怎么办呢?vue规定子组件通过触发事件来与父组件进行通信。即父组件在调用子组件时,定义事件,子组件通过触发这个事件来与父组件进行通信。
    父组件通过v-on:eventName="parentEventName"来设置监听,子组件通过$.emit('eventName')来触发事件。

    parent
    <template>
      <div id="app">
        <h1>{{total}}</h1>
        <child :total='total' @trigger="myClick" />
        <child :total='total' @trigger="myClick" />
      </div>
    </template>
    <script>
    import child from "@/components/child";
    export default {
      name: "App",
      data() {
        return {
          total: 0
        };
      },
      components: { child },
      methods:{
        myClick(){
          this.total++;
        }
      }
    };
    </script>
    
    <template>
        <button @click="add">{{count}}</button>
    </template>
    <script>
    export default {
      name: "child",
      data() {
        return {
          count: this.total
        };
      },
      props: ["total"],
      methods:{
        add(){
          this.count++;
          this.$emit('trigger');
        }
      }
    };
    </script>
    

    我们给子组件绑定来trigger事件,事件源是父组件的myClick事件,并调用来child子组件两次。而子组件child中的按钮被点击时,子组件add事件触发,将自身的count++,且触发letrigger事件将父组件内的total++。



    而且更奇妙的是,子组件触发事件时可以使用载荷(payload)。让我们来修改下demo

      parent
    <template>
      <div id="app">
       <ul>
         <li v-for="item in list" :key="item.id">{{item}}</li>
       </ul>
       <child @add="add"></child>
      </div>
    </template>
    <script>
    import child from "@/components/child";
    export default {
      name: "App",
      data() {
        return {
          list:[]
        };
      },
      components: { child },
      methods:{
        add(value){
          this.list.push(value);
        }
      }
    };
    </script>
    
      child
    <template>
      <div>
        <input type="text" v-model="msg">
        <button @click="add">add</button>
      </div>
    </template>
    <script>
    export default {
      name: "child",
      data() {
        return {
          msg: ""
        };
      },
      props: ["total"],
      methods: {
        add() {
          let value = this.msg;
          this.$emit("add", value);
          this.msg = "";
        }
      }
    };
    </script>
    

    我们通过子组件触发父组件的事件,将子组件input的内容发送给父组件。



    当然,有时候我们想给子组件的根元素绑定原生事件,此时可以使用修饰符.native来进行操作。

      parent
    <template>
      <div id="app">
       <child @click.native="print"></child>
      </div>
    </template>
    <script>
    import child from "@/components/child";
    export default {
      name: "App",
      data() {
        return {
        };
      },
      components: { child },
      methods:{
        print(e){
          console.log(e.offsetX,e.offsetY);
        }
      }
    };
    </script>
    
      child
    <template>
      <div class="content">
      </div>
    </template>
    <script>
    export default {
      name: "child",
      data() {
        return {
        };
      },
    };
    </script>
    <style scoped>
      .content{
        width:200px;
        height:200px;
        background:lightcyan;
      }
    </style>
    

    此时我们给子元素绑定原生点击事件,来监测鼠标的点击位置。


    .sync修饰符

    单向数据绑定可以防止子组件无意间去窜改父组件的状态,双向数据绑定在一定的场景下也是有一定用处的。比如在开发可复用的组件库时,双向数据绑定显得十分有用。
    .sync修饰符帮我们做到来这一点,在vue2.0中.sync被移除了,在vue2.3.0又被重新引入,只是做了很大的修改。.sync现在作为一个语法糖来使用,他会被扩展为一个自动更新父组件属性的v-on监听器。
    总结一下就是.sync只是在底层自动为子组件绑定来一个监听事件,子组件只需要在需要修改父组件状态时去手动触发他就OK了。
    例如:

      <child :foo.sync="bar"></child>
    

    会被扩展为:

      <child :foo="bar" @update:foo="val=>bar=val"></child>
    

    是不是很眼熟,它只是将bar的值绑定在了child上,同时给child绑定了一个update:foo的监听事件。相对应的我们只需要在子组件定义foo的prop,然后手动$.emit('update:foo')进行触发就可以了。
    多说无益,上个demo

      parent
    <template>
      <div id="app">
        <h1>{{msg}}</h1>
       <child :msg.sync="msg"></child>
      </div>
    </template>
    <script>
    import child from "@/components/child";
    export default {
      name: "App",
      data() {
        return {
          msg:'.sync'
        };
      },
      components: { child }
    };
    </script>
    
      child
    <template>
      <input type="text" v-model="my_msg"/>
    </template>
    <script>
    export default {
      name: "child",
      data() {
        return {
          my_msg:this.msg
        };
      },
      props:['msg'],
      watch:{
        my_msg(newVal){
          this.$emit('update:msg',newVal);
        }
      }
    };
    </script>
    

    这里我们通过watch变量my_msg来触发事件,更新父组件。


    相关文章

      网友评论

          本文标题:vue的父子组件通信

          本文链接:https://www.haomeiwen.com/subject/rvxvlftx.html