• 浅谈Vue不同场景下组件间的数据交流


    正文

    浅谈Vue不同场景下组件间的数据“交流”
     
    Vue的官方文档可以说是很详细了。在我看来,它和react等其他框架文档一样,讲述的方式的更多的是“方法论”,而不是“场景论”,这也就导致了:我们在阅读完文档许多遍后,写起代码还是不免感到有许多困惑,因为我们不知道其中一些知识点的运用场景。这就是我写这篇文章的目的,探讨不同场景下组件间的数据“交流”的Vue实现
     

    父子组件间的数据交流

    父子组件间的数据交流可分为两种:
    1.父组件传递数据给子组件
    2.子组件传递数据给父组件

    父组件传递数据给子组件——props

    这是组件数据沟通中最常见的场景:你让父组件掌握了数据源,然后传递给子组件,供子组件使用
     
    许多人会说,这很简单!用props嘛! 对,正因如此,它不是我要讲的主要内容,不过我们还是用代码简单过一遍:
    父组件
    复制代码
    <template>
      <div id="father">
        {{ '我是父组件' }}
        <son :text = "text"></son>
      </div>
    </template>
     
    <script>
    import son from './son.vue'
    export default {
      data: function () {
        return {
          text: '从父组件传来的数据'
        }
      },
      components: {
        son: son
      }
    }
    </script>
     
    <style scoped>
    </style>
    复制代码
    子组件:
    复制代码
    <template>
      <div>
        {{ '我是子组件,我接收了' + text }}
      </div>
    </template>
     
    <script>
    export default {
      props: {
        text: { type: String, default: '' }
      }
    }
    </script>
     
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    </style>
    复制代码
    demo:
     
    在这个demo里面,我们把“从父组件传来的数据”这一个字符串通过props传递给了子组件
     
    如果我们希望在子组件中改变父组件的数据的话,可以在父组件中定义一个能改变父组件数据的函数,然后通过props将该函数传递给子组件,并在子组件在适当时机调用该函数——从而起到在子组件中改变父组件数据的效果

    子组件传递数据给父组件

    子组件传递数据给父组件   方式一:回调传参

    父组件:
    复制代码
    <template>
      <div id="father">
        {{ '我是父组件,名称是' + componentName }}
        <son  :changeComponentName = "changeComponentName"></son>
      </div>
    </template>
     
    <script>
    import son from './son.vue'
    export default {
      data: function () {
        return {
          componentName: '组件A'
        }
      },
     
      methods: {
        changeComponentName: function (newComponentName) {
          this.componentName = newComponentName
        }
      },
     
      components: {
        son: son
      }
    }
    </script>
     
    <style scoped>
      #father div{
        padding: 10px;
        margin: 10px;
        border: 1px solid gray;
      }
    </style>
    复制代码
    子组件:
    复制代码
    <template>
      <div>
        <p>我是子组件:一个button</p>
        <button @click="() => changeComponentName(newComponentName)">
          把父组件的名称修改为:彭湖湾的组件
        </button>
      </div>
    </template>
     
    <script>
    export default {
      data: function () {
        return {
          newComponentName: '彭湖湾的组件'
        }
      },
      props: {
        changeComponentName: {
          type: Function,
          default: () => { }
        }
      }
    }
    </script>
     
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    </style>
    复制代码

    demo:

    点击前:

    点击后:

    图解:

     

    点击子组件(按钮)的时候,将父组件的名称从“A”修改为“彭湖湾的组件”
     
    我们从父组件向子组件传递了一个函数(changeComponentName)。并在子组件调用这个函数的时候,以参数的形式传递了一个子组件内部的数据(newComponentName)给这个函数,这样,在父组件中定义的函数(changeComponentName)就可以取得子组件传来的参数了
     
    【PS】 命名太长不好意思
     

    子组件传递数据给父组件   方式二:自定义事件

    父组件:
    复制代码
    <template>
      <div id="father">
        <div>
          {{ '我是父组件,我的名称是:' + fatherComponentName }}
          <son  v-on:changeComponentName = "changeComponentName"></son>
        </div>
      </div>
    </template>
     
    <script>
    import son from './son.vue'
    export default {
      data: function () {
        return {
          fatherComponentName: 'A组件'
        }
      },
     
      methods: {
        changeComponentName: function (componentName) {
          this.fatherComponentName = componentName
        }
      },
     
      components: {
        son: son
      }
    }
    </script>
     
    <style scoped>
      #father div{
        padding: 10px;
        margin: 10px;
        border:1px solid grey;
      }
    </style>
    复制代码
    子组件:
    复制代码
    <template>
      <div>
        <p>我是子组件:一个按钮</p>
        <button @click="clickCallback">
          修改父组件的名称为:彭湖湾的组件
        </button>
      </div>
    </template>
     
    <script>
    export default {
      data: function () {
        return {
          fatherComponentName: '彭湖湾的组件'
        }
      },
      methods: {
        clickCallback: function () {
          this.$emit('changeComponentName', this.fatherComponentName)
        }
      }
    }
    </script>
     
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    </style>
    复制代码

     demo:

    点击前:

    点击后:

     
     图解:
    通过$emit(event, [...参数]),所有的参数将被传递给监听器回调,也就是我们在父组件中定义的changeComponentName方法,从而实现从子组件中给父组件传参

    兄弟组件间的数据交流(有共同父组件的兄弟组件)

    父组件:
    复制代码
    <template>
      <div id="father">
        <div>
          {{ '我是父组件:father' }}
          <eldest-son :text = "text"></eldest-son>
          <youngest-son :changeText="changeText"></youngest-son>
        </div>
      </div> 
    </template>
     
    <script>
    import eldestSon from './eldestSon.vue'
    import youngestSon from './youngestSon.vue'
     
    export default {
      data: function () {
        return {
          text: '我是一行文本'
        }
      },
     
      methods: {
        changeText: function () {
          this.text = '我是经过改动的一行文本'
        }
      },
     
      components: {
        eldestSon: eldestSon,
        youngestSon: youngestSon
      }
    }
    </script>
     
    <style>
        #father div{
          border: 1px solid grey;
          padding: 10px;
          margin: 10px;
        }
    </style>
    复制代码
    兄弟组件1:
    复制代码
    <template>
      <div>
        <p>我是兄弟组件:eldestSon</p>
        <p>我有一个可变数据text:{{ text }}</p>
      </div>
    </template>
     
    <script>
    export default {
      props: {
        text: {
          type: String,
          default: ''
        }
      }
    }
    </script>
     
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    </style>
    复制代码
    兄弟组件2:
    复制代码
    <template>
      <div>
        <p>我是兄弟组件:youngestSon</p>
        <button @click="changeText">更改eldestSon兄弟组件中的文本</button>
      </div>
    </template>
     
    <script>
    export default {
      props: {
        changeText: {
          type: Function,
          default: () => {}
        }
      }
    }
    </script>
     
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    </style>
    复制代码

     点击前:

    点击后:

     图解:

    如果两个兄弟组件间存在这种数据关系的话,我们可以尝试寻找其共同的父组件,使数据和相关方法“提升”到父组件内部,并向下传给两个子组件
     
    这样,其中一个子组件取得了数据,另外一个子组件取得了改变数据的方法,便可以实现上述的数据沟通
     
    【注意】这种场景存在局限性,它要求两个组件有共同父组件。对于这种场景之外的处理方法,请看下文
     

    全局组件间的数据交流——Vuex

     
    我上述的许多场景里面,都运用到了props或者函数传参的方式去处理组件间的数据沟通。然而在稍大型的应用里面,它们都不约而同地给我们带来了很大的麻烦
    例如:
    1.通过props从父组件向子组件传递数据
    对于直接的父子关系的组件,数据流显得很简洁明确,但在大型应用里面,我们上下嵌套许多个组件的时候,这就会导致我们的代码非常地繁琐,并难以维护
     
    2.对于没有共同的父组件的兄弟组件,函数传参的数据传递方式也无能为力了,Vue文档里介绍到,你可以通过以$emit和$on函数为基础的“事件总线”沟通数据,但它无法应对更加大型的应用
     
    这个时候Vuex就成为了实现全局组件间数据交流的最佳方案了
    Vuex拥有一个包含全部顶层状态的单一数据源(state)
    1.所有的组件都可以使用这个单一数据源里面的数据
    2.所有的组件都可以通过派发 动作(actions)修改这个单一数据源里的数据
     
    原本要“走很多弯路”才能实现沟通的数据流,一下子就找到了最短的捷径
     

    实现View层的数据和model层的解耦

     
    在1,2小节中处理的数据(Vue)和第三小节中处理的数据(Vuex),在很多时候是两种不同类型的数据,前者是属于View层,仅负责单纯的UI展示,而model层的大多是从后端取得后注入的数据。
     
    一点建议:
    1.Vue部分的代码负责构建View层
    2.Vuex部分的代码负责构建model层
    (上述的Vue指的是Vuex之外的框架体系)
    以上述两点为基础,决定某部分的代码到底要写进Vue里面还是写进Vuex里面,并尽量将两者分开,从而实现View层和model层的解耦,提高前端代码的可维护性和扩展性
  • 相关阅读:
    BZOJ 1251 序列终结者(Splay)
    ZOJ 2112 Dynamic Rankings(动态区间第 k 大+块状链表)
    POJ 2887 Big String(块状链表)
    BZOJ 1093 [ZJOI2007] 最大半连通子图(强联通缩点+DP)
    Codeforces Beta Round #13 C. Sequence (DP)
    Codeforces Round #184 (Div. 2) E. Playing with String(博弈)
    MemSQL start[c]up Round 2
    Codeforces Round #195 A B C 三题合集 (Div. 2)
    哈密尔顿回路总结
    最大团问题
  • 原文地址:https://www.cnblogs.com/JonaLin/p/11114452.html
Copyright © 2020-2023  润新知