Vue組件傳值(父子傳值、兄弟傳值、爺孫傳值)整理


  Vue組件之間傳值無論面試還是正常開發中,都很常用,這里主要整理一下父子傳值、兄弟傳值、爺孫傳值的幾種方法。(我盡量整理的全一點,各位大哥們多指教指教,別踩別踩!!!哎嘛。或者您們有什么新的方式,歡迎評論,我給加上,盡量盡善盡美嘛!!!)

  先補充下簡寫:’ : ‘ 是 v-bind的簡寫,

                            ’ @ ‘是v-on的簡寫

一、父子傳值

  父子組件的關系可以總結為 prop 向下傳遞,事件 向上傳遞。父組件通過prop給子組件下發數據,子組件通過 事件給父組件發送信息。

                                                                             

1.1 使用 props 傳值【父傳子】

  props可以是數組或對象,用來接收父組件傳來的數據,在接收的數據中也可以配置一些屬性(type、default、required、validator【驗證器】函數)

<!-- 父組件 --> 
<first-child :fatherMethod="fatherMethod" :sendNumber="number" :sendObj="obj"></first-child>

<!-- 子組件 -->
props: {
    /* 函數 */
    fatherMethod: {
      type: Function, //String、Object、Array等原生構造函數中的一種 、 自定義構造函數 、 這些組成數組
      default: null, //默認值,如果 prop 沒有被傳入,則換做用這個值
      required: false //定義該 prop 是否是必填項(如果是true,但沒有傳,控制台會報警告)
    },
    /* 數字 */
    sendNumber: {
      type: Number,
      default: 0
    },
      /* 字符串 */
    sendObj: {
      type: Object,
      default: {}
    }
},

1.2 使用 this.$emit(eventName,[...args]) 傳值【子傳父】

  eventName:事件名稱,子組件傳遞給父組件的事件名,上面說的 @ ( v-on )【官網對其用法描述:綁定事件的監聽器】

【...args】:附加參數,它會傳給監聽器回調

<!-- 子組件 -->
this.$emit('isUseFatherMet', true)

<!-- 父組件 -->
<first-child @isUseFatherMet="isUseFatherMet"></first-child>

//子組件調用父組件的方法
isUseFatherMet(val) {
  console.log('子組件傳值過來了?', val)
},

1.3 調用子組件中的方法(使用this.$refs.子組件注冊名.子組件方法)

  官方文檔對 vm.$refs 的定義為:一個對象,持有注冊過 refattribute 的所有 DOM 元素和組件實例。我們將引用信息注冊在父組件的 $refs 對象上,對象就會指向子組件實例,以此我們可訪問子組件的方法,進行傳值調用。

<!-- 父組件 -->
<first-child ref="child1"></first-child>

//調用子組件里的方法
runChildMet() {
  this.$refs.child1.showIsUse()
}

<!-- 子組件 -->
//父組件會調用這個方法
showIsUse() {
  alert(true)
}

 1.4 使用 vm.$on 和 vm.$emit【也可應用於兄弟組件傳值】

 首先就官方文檔描述:

  vm.$on(event,callback) :監聽當前實例上的自定義事件。事件可以由  vm.$emit 觸發。回調函數會接收所有傳入事件觸發函數的額外參數。

  vm.$emit(eventName,[...args]):觸發當前實例上的事件。附加參數都會傳給監聽器回調。

  也就是說,子組件通過 vm.$emit() 把事件名和參數傳遞給父組件,父組件使用 vm.$on() 對這個自定義事件監聽,回調函數接收到值,進行下一步操作。

  用法:我們可以新建一個Bus.js文件,文件暴露一個Vue實例,之后分別在父子組件中導入進來,然后調用。

// Bus.js (把它想象成運送數據的交通工具)
// 沒有源碼,在別的項目里找的
import Vue from 'vue'
export default new Vue()
<!-- 父組件 -->
import Bus from'../utils/Bus'
Bus.$on('backDmDetail', val => {
  console.log(val);
})

beforeDestroy(){
    //最后不要忘了刪除傳輸事件
    Bus.$off("backDmDetail")
}

<!-- 子組件 -->
import Bus from'../utils/Bus'
Bus.$emit('backDmDetail',"dsdd")

1.5 使用 $parent 和 $children 方法

  $parent :在子組件中直接訪問該組件的父實例或組件,

  $children:當前實例的子組件,需要注意 $children 並不保證順序,也不是響應式的。考慮使用 v-for 來生成子組件

    

/* 使用$parent */

<!-- 子組件 -->
useParent() {
  this.$parent.isUseFatherMet('是的,傳過來了,而且我用了$parent')
}

<!-- 父組件 -->
//子組件調用父組件的方法
isUseFatherMet(val) {
  console.log('子組件傳值過來了?', val)
},

 


二、兄弟傳值

  2.1 使用 vm.$on 和 vm.$emit

   就如 標題1.4 所說,導入一個公共的 Bus.js 文件,暴露一個 Vue 實例,在兄弟組件傳遞時使用 Bus.$on  和 Bus.$emit

// Bus.js (把它想象成運送數據的交通工具)
// 沒有源碼,在別的項目里找的
import Vue from 'vue'
export default new Vue()
<!-- 父組件 -->
import Bus from'../utils/Bus'
Bus.$on('backDmDetail', val => {
  this.investigationIndex = this.flag
})

<!-- 子組件 -->
import Bus from'../utils/Bus'
Bus.$emit('backDmDetail',"dsdd")

  注意:暴露一個 Vue 實例,在Vue2.x版本是可以這么使用,但是在Vue3.x中,沒有全局的vue,會報警告:"export 'default' (imported as 'Vue') was not found in 'vue'"

  強勢大哥:呵呵,說了這么多,什么什么跟上面一樣,有啥用,那我就想用Vue3.x,不想降版本,那咋整?

  卑微小弟:別急,哥,有下面的方法:使用第三方庫Mitt

npm install -S mitt //先引入依賴

//新建一個Bus.js(這車有空調,手動滑稽)

<!-- 兄弟組件1 -->
import Bus from '@/utils/Bus.js'

//給兄弟組件傳值
sendToBrother() {
  Bus.emit('toBrotherVal', '我是傳向兄弟組件的值')
}


<!-- 兄弟組件2 -->
import Bus from '@/utils/Bus.js'
Bus.on('toBrotherVal', (val) => {
  alert(val)
})

//使用完別忘了移除(重復發車不回收,性能耗不起啊。。。)
unmounted() {
  //移除toBrotherVal
  Bus.off('toBrotherVal')
}

  2.2 子傳父,父再傳子

     一種比較常規的使用方法,通常都是this.$emit、props等方式一起使用,不做贅述。

 


三、爺孫傳值

  3.1 使用 provide 和 inject

     provide/inject: 這對選項要一起使用, 以允許一個祖先組件向其子孫后台注入一個依賴,不管組件層次有多深,並在其上下有關系成立的時間里始終生效(Vue官方文檔)。

     也就是說他們允許父子組件傳值、爺孫組件傳值。

// 爺爺組件通過provide將自己的數據以對象形式傳出去
provide() {
  return {
    parentValue: '我是兒他爹,也是孫他爺'
  }
},
  
//兒子組件接收
inject: {
  // 使用一個默認值使其變成可選項
  parentValue: {
    // 健名
    from: 'parentValue', // 來源
      default: '000' // 默認值( vue2.5.0++ )
  }
},

//孫子組件接收
inject: {
  // 使用一個默認值使其變成可選項
  parentValue: {
    // 健名
    from: 'parentValue', // 來源
      default: '000' // 默認值( vue2.5.0++ )
  }
},

源代碼(ps:框架是Vue3.x/vue-cli4.54):

父組件:

點擊查看代碼

<template>
  <div class="Father">
    <h5>這里是父組件</h5>
    <el-button @click="runChildMet">使用$refs</el-button>
    <el-button @click="useChildren">使用$children</el-button>
    <!-- 子組件1 -->
    <first-child
      :fatherMethod="fatherMethod"
      :sendNumber="number"
      :sendObj="obj"
      @isUseFatherMet="isUseFatherMet"
      ref="child1"
    ></first-child>
    <!-- 子組件2 -->
    <second-child></second-child>
  </div>
</template>
<script>
import child1 from './Child1'
import child2 from './Child2'
export default {
  name: 'Father',
  props: {},
  components: {
    'first-child': child1,
    'second-child': child2
  },
  // 父組件通過provide將自己的數據以對象形式傳出去
  provide() {
    return {
      parentValue: '我是兒他爹,也是孫他爺'
    }
  },
  data() {
    //這里存放數據
    return {
      number: 12,
      obj: { name: '小明' }
    }
  },
  methods: {
    fatherMethod() {
      alert('這里是父組件')
    },
    //子組件調用父組件的方法
    isUseFatherMet(val) {
      alert('子組件傳值過來了?' + val)
    },
    //調用子組件里的方法
    runChildMet() {
      this.$refs.child1.showIsUse('Father')
    },
    useChildren() {
      console.log('dsdsd', this.$children)
      //返回的是一個組件集合,如果你能清楚的知道子組件的順序,你也可以使用下標來操作
      for (let i = 0; i < this.$children.length; i++) {
        this.$children[i].showIsUse()
      }
    }
  }
}
</script>
<style lang="less">
.Father {
  font-size: 18px;
  .el-divider--horizontal {
    margin: 10px 0;
  }
}
</style>

兩個兒子組件:

點擊查看代碼

// Child1
<template>
  <div class="Child1">
    <el-divider></el-divider>
    <h5>這里是Child1</h5>

    <el-button @click="runProps">使用Props</el-button>
    <el-button @click="runFather">使用this.$emit方法</el-button>

    <div style="margin:12px 0 ">
      <span>Props傳的值:</span>
      <div v-if="showProps">
        <span v-html="sendObj.name"></span>
        <span v-html="sendNumber"></span>
      </div>
    </div>
    <el-button @click="useParent">使用 $parent </el-button>
    <el-button @click="sendToBrother">使用Bus給兄弟組件傳值</el-button>
    <h5 v-html="'我是Child1,我爹傳來的值:' + parentValue"></h5>
  </div>
</template>
<script>
import Bus from '@/utils/Bus.js'
export default {
  name: 'Child1',
  props: {
    /* 函數 */
    fatherMethod: {
      type: Function, //String、Object、Array等原生構造函數中的一種 、 自定義構造函數 、 這些組成數組
      default: null, //默認值,如果 prop 沒有被傳入,則換做用這個值
      required: false //定義該 prop 是否是必填項(如果是true,但沒有傳,控制台會報警告)
    },
    /* 數字 */
    sendNumber: {
      type: Number,
      default: 0
    },
    /* 字符串 */
    sendObj: {
      type: Object,
      default: {}
    }
  },
  components: {},
  provide() {
    return {
      brotherVal: '我是Child1'
    }
  },

  inject: {
    // 使用一個默認值使其變成可選項
    parentValue: {
      // 健名
      from: 'parentValue', // 來源
      default: '000' // 默認值( vue2.5.0++ )
    }
  },
  data() {
    //這里存放數據
    return {
      showProps: false //是否顯示父組件傳的值
    }
  },
  methods: {
    //使用props傳值
    runProps() {
      this.showProps = true
      this.fatherMethod()
    },

    //運行父組件的方法
    runFather() {
      this.$emit('isUseFatherMet', true)
    },

    //父組件會調用這個方法
    showIsUse(val) {
      alert('這里是Child1,是' + val + '調用的這個方法')
    },
    useParent() {
      this.$parent.isUseFatherMet('是的,傳過來了,而且我用了$parent')
    },

    //給兄弟組件傳值
    sendToBrother() {
      Bus.emit('toBrotherVal', '我是傳向兄弟組件的值')
    }
  }
}
</script>


// Child2
<template>
  <div class="Child2">
    <el-divider></el-divider>
    <h5>這里是Child2</h5>
    <h5 v-html="'我是Child2,我爹傳來的值:' + parentValue"></h5>
    <grond-son></grond-son>
    <div>----------------------------</div>
    <h5 v-html="brotherVal"></h5>
    <div style="font-size:15px;padding:5px;margin-bottom:20px">
      很顯然,上面兄弟組件使用 provide/inject
      傳值沒有傳過來,因為官方說的是在組件之間存在上下有關系才會生效
    </div>
  </div>
</template>
<script>
import Bus from '@/utils/Bus.js'
import grondSon from './GrandSon.vue'
export default {
  name: 'Child2',
  props: {},
  components: {
    'grond-son': grondSon
  },
  data() {
    //這里存放數據
    return {}
  },
  inject: {
    // 使用一個默認值使其變成可選項
    parentValue: {
      // 健名
      from: 'parentValue', // 來源
      default: '000' // 默認值( vue2.5.0++ )
    },
    brotherVal: {
      from: 'brotherVal',
      default: 'Child222'
    }
  },
  methods: {},
  mounted() {
    Bus.on('toBrotherVal', (val) => {
      alert('這里是Child2,傳來的值:' + val)
    })
  },

  unmounted() {
    //移除toBrotherVal
    Bus.off('toBrotherVal')
  }
}
</script>

孫子組件:

點擊查看代碼

<!-- 孫子組件 -->
<template>
  <div class="GrandSon">
    <div style="text-align:center">這里是孫子的地界,我在Child2里</div>
    <h5 v-html="'我爺爺傳來的值:' + parentValue"></h5>
  </div>
</template>

<script>
//這里可以導入其他文件(比如:組件,工具js,第三方插件js,json文件,圖片文件等等)
//例如:import 《組件名稱》 from '《組件路徑》';

export default {
  name: 'GrandSon',
  props: {},
  //import引入的組件需要注入到對象中才能使用
  components: {},
  inject: {
    // 使用一個默認值使其變成可選項
    parentValue: {
      // 健名
      from: 'parentValue', // 來源
      default: '000' // 默認值( vue2.5.0++ )
    }
  },
  data() {
    //這里存放數據
    return {}
  },
  //計算屬性
  computed: {},
  //監控data中的數據變化
  watch: {},
  methods: {},
  mounted() {}
}
</script>

四、總結

     組件傳值的方式:

       1、使用 props 傳值

       2、使用 this.emit();

       3、使用 this.$refs

       4、使用 $parent 和 $children 方法

       5、定義一個js文件,暴露一個vue實例,使用Bus.$on() 和 Bus.$emit()【別忘了使用Bus.$off 刪除傳值事件,ps:Vue2.x】

       6、定義一個js文件,引入mitt依賴,然后使用Bus.on() 和 Bus.emit()【別忘了Bus.off 刪除傳值事件,ps:Vue3.x、Vue2.x】

       7、使用 provide 和 inject

       Vue組件傳值的方法有很多很多,我這邊總結的肯定也是不全的,當然無論是使用propsthis.$emit還是別的什么方法,高性能、方便易懂,我認為是最重要的(我就是這么認為的!!耶穌來了也不好使),而且就技術更新迭代速度來說,本文說不定過幾年就不好用了呢,哈哈哈。安啦安啦,有更新的話,會更的......

 

借鑒各位大佬:

vue事件之vm.$on事件和v-on事件:https://blog.csdn.net/jaquechen/article/details/102557718

VUE3前端筆記 Mitt事務總線使用方法:https://blog.csdn.net/fuweipeng2012/article/details/113812794

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM