亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

vue組件間如何進行通信

發布時間:2021-12-07 08:59:49 來源:億速云 閱讀:123 作者:iii 欄目:編程語言

本篇內容介紹了“vue組件間如何進行通信”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

vue是數據驅動視圖更新的框架, 我們平時開發,都會把頁面不同模塊拆分成一個一個vue組件, 所以對于vue來說組件間的數據通信非常重要,那么組件之間如何進行數據通信的呢?

首先我們需要知道在vue中組件之間存在什么樣的關系, 才更容易理解他們的通信方式。

一般我們分為如下關系:

  • 父子組件之間通信

  • 非父子組件之間通信(兄弟組件、隔代關系組件等)

props / $emit

父組件通過props的方式向子組件傳遞數據,而通過$emit 子組件可以向父組件通信。

  1. 父組件向子組件傳值

<!-- 父組件 -->
<template>
  <div class="section">
    <child :msg="articleList"></child>
  </div>
</template>

<script>
import child from './child.vue'
export default {
  name: 'HelloWorld',
  components: { comArticle },
  data() {
    return {
      msg: '阿離王'
    }
  }
}
</script>
<!-- 子組件 child.vue -->
<template>
  <div>
    {{ msg }}
  </div>
</template>

<script>
export default {
  props: {
      msg: String
  }
}
</script>

注意:

prop 只可以從上一級組件傳遞到下一級組件(父子組件),即所謂的單向數據流。而且 prop 只讀,不可被修改,所有修改都會失效并警告。

  • 第一,不應該在一個子組件內部改變 prop,這樣會破壞單向的數據綁定,導致數據流難以理解。如果有這樣的需要,可以通過 data 屬性接收或使用 computed 屬性進行轉換。

  • 第二,如果 props 傳遞的是引用類型(對象或者數組),在子組件中改變這個對象或數組,父組件的狀態會也會做相應的更新,利用這一點就能夠實現父子組件數據的“雙向綁定”,雖然這樣實現能夠節省代碼,但會犧牲數據流向的簡潔性,令人難以理解,最好不要這樣去做。

  • 想要實現父子組件的數據“雙向綁定”,可以使用 v-model.sync

  1. 子組件向父組件傳值

使用 $emit 向父組件傳數據,原理這樣的: 父組件在子組件通過v-on監聽函數并接收參數,vue框架就在子組件監聽了你v-on="fn"的fn事件函數,在子組件使用$emit就能觸發了,下面寫個例子。

<!-- 父組件 -->
<template>
  <div class="section">
    <child :msg="articleList" @changMsg="changMsg"></child>
  </div>
</template>

<script>
import child from './child.vue'
export default {
  name: 'HelloWorld',
  components: { comArticle },
  data() {
    return {
      msg: '阿離王'
    }
  },
  methods:{
      changMsg(msg) {
          this.msg = msg
      }
  }
}
</script>
<!-- 子組件 child.vue -->
<template>
  <div>
    {{ msg }}
    <button @click="change">改變字符串</button>
  </div>
</template>

<script>
export default {
  props: {
      msg: String
  },
  methods: {
      change(){
          this.$emit('changMsg', '阿離王帶你學習前端')
      }
  }
}
</script>

v-model 指令

v-model 是用來在表單控件或者組件上創建雙向綁定的,他的本質是 v-bindv-on語法糖,在一個組件上使用 v-model,默認會為組件綁定名為 value 的 prop 和名為 input 的事件。

當我們組件中的某一個 prop 需要實現上面所說的”雙向綁定“時,v-model 就能大顯身手了。有了它,就不需要自己手動在組件上綁定監聽當前實例上的自定義事件,會使代碼更簡潔

下面以一個 input 組件實現的核心代碼,介紹下 v-model 的應用。

<!--父組件-->
<template>
    <base-input v-model="inputValue"></base-input>
</template>
<script>
    export default {
        data() {
            return {
                input: ''
            }
        },
    }
</script>
<!--子組件-->
<template>
    <input type="text" :value="currentValue"  @input="handleInput">
</template>
<script>
    export default {
        data() {
            return {
                currentValue: this.value === undefined || this.value === null ? ''
            }
        },
        props: {
            value: [String, Number], // 關鍵1
        },
        methods: {
            handleInput(event) {
                const value = event.target.value;
                this.$emit('input', value); // 關鍵2
            },
        },
}
</script>

上面例子看到,v-model="inputValue" 他的本質就是 v-bind 和 v-on 的語法糖,默認為父組件綁定名為 :value="inputValue"的屬性,和@input="(v) => { this.inputValue = v }"事件,子組件通過 this.$emit('input', value) 通知父組件

所以他原理也是利用了我們上面講的父子組件傳參 props / $emit 方式來實現雙向綁定

有時,在某些特定的控件中名為 value 的屬性會有特殊的含義,這時可以通過 v-model 選項來回避這種沖突。

.sync 修飾符

  • .sync 修飾符在 vue 1.x 的版本中就已經提供,1.x 版本中,當子組件改變了一個帶有 .syncprop 的值時,會將這個值同步到父組件中的值。這樣使用起來十分方便,但問題也十分明顯,這樣破壞了單向數據流,當應用復雜時,debug 的成本會非常高。

  • 于是乎,在vue 2.0中移除了 .sync。但是在實際的應用中,.sync 是有它的應用場景的,所以在 vue 2.3 版本中,又迎來了全新的 .sync

  • 新的 .sync 修飾符所實現的已經不再是真正的雙向綁定,它的本質和 v-model 類似,只是一種縮寫。

正常封裝組件例子:

<text-document v-bind:title="doc.title" v-on:update:title="doc.title = $event" />

上面的代碼,使用 .sync 就可以寫成

<text-document v-bind:title.sync="doc.title" />

這樣,在子組件中,就可以通過下面代碼來實現對這個 prop 重新賦值了。

this.$emit('update:title', newTitle)

看到這里,是不是發現 .sync 修飾符 和 v-model 很相似,也是語法糖, v-bind:title.sync 也就是 等效于 v-bind:title="doc.title" v-on:update:title="doc.title = $event"

v-model 和 .sync 對比

.sync 從功能上看和 v-model 十分相似,都是為了實現數據的“雙向綁定”,本質上,也都不是真正的雙向綁定,而是語法糖

相比較之下,.sync 更加靈活,它可以給多個 prop 使用,而 v-model 在一個組件中只能有一個。

從語義上來看,v-model 綁定的值是指這個組件的綁定值,比如 input 組件select 組件日期時間選擇組件顏色選擇器組件,這些組件所綁定的值使用 v-model 比較合適。其他情況,沒有這種語義,個人認為使用 .sync 更好。

parent/parent /children

通過$parent$children就可以訪問組件的實例,拿到實例代表什么?代表可以訪問此組件的所有方法data。列子如下:

<!-- 父組件 -->
<template>
  <div class="hello_world">
    <div>{{msg}}</div>
    <com-a></com-a>
    <button @click="changeA">點擊改變子組件值</button>
  </div>
</template>

<script>
import ComA from './test/comA.vue'
export default {
  name: 'HelloWorld',
  components: { ComA },
  data() {
    return {
      msg: 'Welcome'
    }
  },

  methods: {
    changeA() {
      // 獲取到子組件A
      this.$children[0].messageA = 'this is new value'
    }
  }
}
</script>
<!-- 子組件 -->
<template>
  <div class="com_a">
    <span>{{messageA}}</span>
    <p>獲取父組件的值為:  {{parentVal}}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      messageA: 'this is old'
    }
  },
  computed:{
    parentVal(){
      return this.$parent.msg;
    }
  }
}
</script>

要注意邊界情況,如在#app上拿$parent得到的是new Vue()的實例,在這實例上再拿$parent得到的是undefined,而在最底層的子組件拿$children是個空數組。也要注意得到parentparent和children的值不一樣,$children 的值是數組,而$parent是個對象

props $emit$parent $children兩種方式用于父子組件之間的通信, 而使用props進行父子組件通信更加普遍,二者皆不能用于非父子組件之間的通信。

provide / inject

provide / inject 是vue2.2.0新增的api, 簡單來說就是父組件中通過provide來提供變量, 然后再子組件中通過inject來注入變量

官方描述: 這對選項需要一起使用,以允許一個祖先組件向其所有子孫后代注入一個依賴,不論組件層次有多深,并在其上下游關系成立的時間里始終生效

provide 選項應該是

  • 一個對象或返回一個對象的函數。該對象包含可注入其子孫的屬性。在該對象中你可以使用 ES2015 Symbols 作為 key,但是只在原生支持 Symbol 和 Reflect.ownKeys 的環境下可工作。

inject 選項應該是:

  • 一個字符串數組

  • 一個對象(詳情點擊這里)

基本用法

// 祖先組件 提供foo
//第一種
export default {
  name: "father",
  provide() {
    return {
      foo: 'hello'
    }
  },
}
//第二種
export default {
  name: "father",
  provide: {
    foo:'hello~~~~'
  },
}
//后代組件 注入foo, 直接當做this.foo來用
export default {
  inject:['foo'],
}

上面的兩種用法有什么區別嗎?

  • 如果你只是傳一個字符串,像上面的hello,那么是沒有區別的,后代都能讀到。

  • 如果你需要this對象屬性的值(如下所示代碼),那么第二種是傳不了的,后代組件拿不到數據。所以建議只寫第一種

//當你傳遞對象給后代時
provide() {
    return {
        test: this.msg
    }
},

注意:一旦注入了某個數據,比如上面示例中的 foo,那這個組件中就不能再聲明 foo 這個數據了,因為它已經被父級占有。

provide 和 inject 綁定并不是可響應的。

這是刻意為之的。然而,如果你傳入了一個可監聽的對象,那么其對象的屬性還是可響應的。因為對象是引用類型。

先來個值類型的數據(也就是字符串)例子,不會響應

provide(){
  return{
    test:this.msg
  }
},
data() {
  return {
    msg: "Welcome to Your Vue.js App",
  }
}
mounted(){
  setTimeout(()=>{
    this.msg = "halo world";
    console.log(this._provided.msg)
    //log:Welcome to Your Vue.js App
  },3000)
},

如上所示,這樣做是不行的,打印出來的 _provided 中的數據并沒有改,子組件取得值也沒變。

你甚至可以直接給 this._provided.msg 賦值,但是即使是_provided.msg 里面的值改變了,子組件的取值,依然沒有變。

當你的參數是對象的時候,就可以響應了,如下:

provide(){
  return{
    test:this.activeData
  }
},
data() {
  return {
    activeData:{name:'halo'},
  }
}
mounted(){
  setTimeout(()=>{
    this.activeData.name = 'world';
  },3000)
}

這就是vue官方中寫道的對象的屬性是可以響應的

provide/inject 實現全局變量

provide/inject不是只能從祖先傳遞給后代嗎?是的,但是,如果我們綁定到最頂層的組件app.vue,是不是所有后代都接收到了,就是當做全局變量來用了。

//app.vue
export default {
  name: 'App',
  provide(){
    return{
      app:this
    }
  },
  data(){
    return{
      text:"it's hard to tell the night time from the day"
    }
  },
  methods:{
    say(){
      console.log("Desperado, why don't you come to your senses?")
    }
  }
}
//其他所有子組件,需要全局變量的,只需要按需注入app即可
export default {
  inject:['foo','app'],
  mounted(){
    console.log(this.app.text); // 獲取app中的變量
    this.app.say(); // 可以執行app中的方法,變身為全局方法!
  }
}

provide/inject 實現頁面刷新,不閃爍

  1. vue-router重新路由到當前頁面,頁面是不進行刷新的

  2. 采用window.reload(),或者router.go(0)刷新時,整個瀏覽器進行了重新加載,閃爍,體驗不好

那我們怎么做呢?

跟上面的原理差不多,我們只在控制路由的組件中寫一個函數(使用v-if控制router-view的顯示隱藏,這里的原理不作贅述),然后把這個函數傳遞給后代,然后在后代組件中調用這個方法即可刷新路由啦。

//app.vue
<router-view v-if="isShowRouter"/>

export default {
  name: 'App',
  provide() {
    return {
      reload: this.reload
    }
  },
  data() {
    return {
      isShowRouter: true,
    }
  },
  methods:{
    reload() {
      this.isShowRouter = false;
      this.$nextTick(() => { 
        this.isShowRouter = true;
      })
    }
  }
}
//后代組件
export default {
  inject: ['reload'],  
}

這里 provide 使用了函數傳遞給后代,然后后代調用這個函數,這種思路,也是可以做子后代向父組件傳參通訊的思路了。這里的原理,和 event 事件訂閱發布就很像了

ref / $refs

ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子組件上,引用就指向組件實例,可以通過實例直接調用組件的方法或訪問數據, 我們看一個ref 來訪問組件的例子:

// 子組件 A.vue

export default {
  data () {
    return {
      name: 'Vue.js'
    }
  },
  methods: {
    sayHello () {
      console.log('hello')
    }
  }
}
// 父組件 app.vue

<template>
  <component-a ref="comA"></component-a>
</template>
<script>
  export default {
    mounted () {
      const comA = this.$refs.comA;
      console.log(comA.name);  // Vue.js
      comA.sayHello();  // hello
    }
  }
</script>

ref 這種方式,就是獲取子組件的實例,然后可以直接子組件的方法和訪問操作data的數據,就是父組件控制子組件的一種方式,子組件想向父組件傳參或操作,只能通過其他的方式了

eventBus

eventBus呢,其實原理就是 事件訂閱發布,eventBus 又稱為事件總線,在vue中可以使用它來作為溝通橋梁的概念, 就像是所有組件共用相同的事件中心,可以向該中心注冊發送事件或接收事件, 所以組件都可以通知其他組件。

這里我們可以直接使用 vue 自帶的事件監聽,也就是 emitemiton,我們來簡單封裝下:

  1. 首先需要創建一個事件總線并將其導出, 以便其他模塊可以使用或者監聽它.

新建一個 event-bus.js 文件

// event-bus.js

import Vue from 'vue'
export const EventBus = new Vue()
  1. 發生事件

假設你有兩個組件: additionNum 和 showNum, 這兩個組件可以是兄弟組件也可以是父子組件;這里我們以兄弟組件為例:

<template>
  <div>
    <show-num-com></show-num-com>
    <addition-num-com></addition-num-com>
  </div>
</template>

<script>
import showNumCom from './showNum.vue'
import additionNumCom from './additionNum.vue'
export default {
  components: { showNumCom, additionNumCom }
}
</script>
// addtionNum.vue 中發送事件

<template>
  <div>
    <button @click="additionHandle">+加法器</button>    
  </div>
</template>

<script>
import { EventBus } from './event-bus.js'
console.log(EventBus)
export default {
  data() {
    return {
      num: 1
    }
  },

  methods: {
    additionHandle() {
      EventBus.$emit('addition', {
        num: this.num++
      })
    }
  }
}
</script>
  1. 接收事件

// showNum.vue 中接收事件

<template>
  <div>計算和: {{count}}</div>
</template>

<script>
import { EventBus } from './event-bus.js'
export default {
  data() {
    return {
      count: 0
    }
  },

  mounted() {
    EventBus.$on('addition', param => {
      this.count = this.count + param.num;
    })
  }
}
</script>

這樣就實現了在組件addtionNum.vue中點擊相加按鈕, 在showNum.vue中利用傳遞來的 num 展示求和的結果.

  1. 移除事件監聽者

如果想移除事件的監聽, 可以像下面這樣操作:

import { eventBus } from 'event-bus.js'
EventBus.$off('addition')

自己封裝一套 eventBus

這里使用自己封裝一套eventBus也行,方便自己想干啥就干啥, 下面貼封裝好的一套給大家

/* eslint-disable no-console */
// 事件映射表
let eventMap = {}

/**
 * 監聽事件
 * @param {string}    eventName 事件名
 * @param {function}  listener 回調函數
 * @param {object}    instance 注冊事件的實例
 */
function on(eventName, listener, instance) {
  eventMap[eventName] = eventMap[eventName] || []
  eventMap[eventName].push({
    listener,
    instance,
  })
}

// 監聽事件,只執行一次
function once(eventName, listener, instance) {
  eventMap[eventName] = eventMap[eventName] || []
  eventMap[eventName].push({
    listener,
    instance,
    once: true,
  })
}

// 解除事件監聽
function off(eventName, listener) {
  // 解除所有事件監聽
  if (!eventName) {
    eventMap = {}
    return
  }

  // 沒有對應事件
  if (!eventMap[eventName]) {
    return
  }

  // 解除某事件監聽
  eventMap[eventName].forEach((currentEvent, index) => {
    if (currentEvent.listener === listener) {
      eventMap[eventName].splice(index, 1)
    }
  })
}

// 發送事件,執行對應響應函數
function emit(eventName, ...args) {
  if (!eventMap[eventName]) {
    return
  }

  eventMap[eventName].forEach((currentEvent, index) => {
    currentEvent.listener(...args)
    if (currentEvent.once) {
      eventMap[eventName].splice(index, 1)
    }
  })
}

// 顯示當前注冊的事件,代碼優化時使用
function showEventMap(targetEventName) {
  if (targetEventName) { // 查看具體某個事件的監聽情況
    eventMap[targetEventName].forEach(eventItem => {
      console.log(targetEventName, eventItem.instance, eventItem.listener)
    })
  } else { // 查看所以事件的監聽情況
    Object.keys(eventMap).forEach(eventName => {
      eventMap[eventName].forEach(eventItem => {
        console.log(eventName, eventItem.instance, eventItem.listener)
      })
    })
  }
}

// 提供 vue mixin 方法,在 beforeDestroy 自動注銷事件監聽
export const mixin = {
  created() {
    // 重載 on 函數,收集本組件監聽的事件,待消除時,銷毀事件監聽
    this.$eventListenerList = []
    this.$event = { off, once, emit, showEventMap }
    this.$event.on = (eventName, listener) => {
      this.$eventListenerList.push({ eventName, listener })
      on(eventName, listener)
    }
  },

  // 消除組件時,自動銷毀事件監聽
  beforeDestroy() {
    this.$eventListenerList.forEach(currentEvent => {
      off(currentEvent.eventName, currentEvent.listener)
    })
  },
}

export default { on, off, once, emit, showEventMap }

如何使用呢,只需在 項目的 main.js, 引入 ,然后 Vue.mixin 即可,如下:

// main.js
import Vue from 'vue'
import { mixin as eventMixin } from '@/event/index'

Vue.mixin(eventMixin)

在vue項目其他文件,就可以直接 this.$event.on this.$event.$emit  如下:

this.$event.on('test', (v) => { console.log(v) })   this.$event.$emit('test', 1)

還順便封裝了個mixin, 好處呢,就是在vue頁面監聽事件后,頁面銷毀后,也自動銷毀了事件監聽

Vuex

Vuex介紹

  • Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件的狀態,并以相應的規則保證狀態以一種可預測的方式發生變化.

  • Vuex 解決了多個視圖依賴于同一狀態和來自不同視圖的行為需要變更同一狀態的問題,將開發者的精力聚焦于數據的更新而不是數據在組件之間的傳遞上

Vuex各個模塊

  • state:用于數據的存儲,是store中的唯一數據源

  • getters:如vue中的計算屬性一樣,基于state數據的二次包裝,常用于數據的篩選和多個數據的相關性計算

  • mutations:類似函數,改變state數據的唯一途徑,且不能用于處理異步事件

  • actions:類似于mutation,用于提交mutation來改變狀態,而不直接變更狀態,可以包含任意異步操作

  • modules:類似于命名空間,用于項目中將各個模塊的狀態分開定義和操作,便于維護

Vuex實例應用

這里我們先新建 store文件夾, 對Vuex進行一些封裝處理

在 store 文件夾下添加 index.js 文件

// index.js

// 自動掛載指定目錄下的store
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

let modules = {}

// @/store/module 目錄下的文件自動掛載為 store 模塊
const subModuleList = require.context('@/store/modules', false, /.js$/)
subModuleList.keys().forEach(subRouter => {
  const moduleName = subRouter.substring(2, subRouter.length - 3)
  modules[moduleName] = subModuleList(subRouter).default
})

export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules
})

在 store 文件夾下添加 module 文件夾,在module文件夾再新建 user.js 文件

// user.js

import user from '@/utils/user.js'
import userApi from '@/apis/user'
import { OPEN_ACCOUNT_STAGE, STAGE_STATUS } from '@/constant'

let getUserPromise = null

export default {
  namespaced: true,
  state() {
    return {
      userInfo: null, // 用戶信息
      isLogined: !!user.getToken(), // 是否已經登錄
    }
  },
  mutations: {
    // 更新用戶信息
    updateUser(state, payload) {
      state.isLogined = !!payload
      state.userInfo = payload
    },
  },
  actions: {
    // 獲取當前用戶信息
    async getUserInfo(context, payload) {
      // forceUpdate 表示是否強制更新
      if (context.state.userInfo && !payload?.forceUpdate) {
        return context.state.userInfo
      }
      if (!getUserPromise || payload?.forceUpdate) {
        getUserPromise = userApi.getUserInfo()
      }
      // 獲取用戶信息
      try {
        const userInfo = await getUserPromise
        context.commit('updateUser', userInfo)
      } finally {
        getUserPromise = null
      }
      return context.state.userInfo
    },

    // 登出
    async logout(context, payload = {}) {
      // 是否手動退出
      const { manual } = payload
      if (manual) {
        await userApi.postLogout()
      }
      user.clearToken()
      context.commit('updateUser', null)
    },
  }
}

然后在項目的 main.js 文件中引入

import Vue from 'vue'
import App from '@/app.vue'
import { router } from '@/router'
import store from '@/store/index'

const vue = new Vue({
  el: '#app',
  name: 'root',
  router,
  store,
  render: h => h(App),
})

封裝的很愉快了,然后就正常操作即可。

this.$store.state.user.isLogined
this.$store.state.user.userInfo
this.$store.commit('user/updateUser', {})
 await this.$store.dispatch('user/logout', { manual: true })

localStorage / sessionStorage

這種通信比較簡單,缺點是數據和狀態比較混亂,不太容易維護。

  • 通過window.localStorage.getItem(key)獲取數據

  • 通過window.localStorage.setItem(key,value)存儲數據

注意用JSON.parse() / JSON.stringify() 做數據格式轉換, localStorage / sessionStorage可以結合vuex, 實現數據的持久保存,同時使用vuex解決數據和狀態混亂問題.

自己實現簡單的 Store 模式

對于小型的項目,通信十分簡單,這時使用 Vuex 反而會顯得冗余和繁瑣,這種情況最好不要使用 Vuex,可以自己在項目中實現簡單的 Store。

// store.js
const store = {
  debug: true,
  state: {
    author: 'yushihu!'
  },
  setAuthorAction (newValue) {
    if (this.debug) console.log('setAuthorAction triggered with', newValue)
    this.state.author = newValue
  },
  deleteAuthorAction () {
    if (this.debug) console.log('deleteAuthorAction triggered')
    this.state.author = ''
  }
}
export default store

上面代碼原理就是,store.js文件暴露出一個對象 store,通過引入 store.js 文件 各個頁面來共同維護這個store對象

和 Vuex 一樣,store 中 state 的改變都由 store 內部的 action 來觸發,并且能夠通過 console.log() 打印觸發的痕跡。這種方式十分適合在不需要使用 Vuex 的小項目中應用。

$root 訪問根實例的方法相比,這種集中式狀態管理的方式能夠在調試過程中,通過 console.log() 記錄來確定當前變化是如何觸發的,更容易定位問題。

通過 $root 訪問根實例

通過 $root,任何組件都可以獲取當前組件樹的根 Vue 實例,通過維護根實例上的 data,就可以實現組件間的數據共享

//main.js 根實例
new Vue({
    el: '#app',
    store,
    router,
    // 根實例的 data 屬性,維護通用的數據
    data: function () {
        return {
            author: ''
        }
    },
    components: { App },
    template: '<App/>',
});


<!--組件A-->
<script>
export default {
    created() {
        this.$root.author = '于是乎'
    }
}
</script>


<!--組件B-->
<template>
    <div><span>本文作者</span>{{ $root.author }}</div>
</template>

注意:通過這種方式,雖然可以實現通信,但在應用的任何部分,任何時間發生的任何數據變化,都不會留下變更的記錄,這對于稍復雜的應用來說,調試是致命的,不建議在實際應用中使用。

attrsattrs與listeners

現在我們來討論一種情況, 我們一開始給出的組件關系圖中A組件與D組件是隔代關系, 那它們之前進行通信有哪些方式呢?

  1. 使用props綁定來進行一級一級的信息傳遞, 如果D組件中狀態改變需要傳遞數據給A, 使用事件系統一級級往上傳遞

  2. 使用eventBus,這種情況下還是比較適合使用, 但是碰到多人合作開發時, 代碼維護性較低, 可讀性也低

  3. 使用Vuex來進行數據管理, 但是如果僅僅是傳遞數據, 而不做中間處理,使用Vuex處理感覺有點大材小用了.

所以就有了 $attrs / $listeners ,通常配合 inheritAttrs 一起使用。

inheritAttrs

默認情況下父作用域的不被認作 propsattribute 綁定 (attribute bindings) 將會“回退”且作為普通的 HTML attribute 應用在子組件的根元素上。當撰寫包裹一個目標元素或另一個組件的組件時,這可能不會總是符合預期行為。

通過設置 inheritAttrsfalse,這些默認行為將會被去掉。而通過 (同樣是 2.4 新增的) 實例 property $attrs 可以讓這些 attribute 生效,且可以通過 v-bind 顯性的綁定到非根元素上。

注意:這個選項不影響 classstyle 綁定。

上面是官方描述:還是很難懂。

簡單的說就是

  • inheritAttrs:true 時繼承除props之外的所有屬性

  • inheritAttrs:false 只繼承class 和 style屬性

$attrs:包含了父作用域中不被認為 (且不預期為) props 的特性綁定 (class 和 style 除外),并且可以通過 v-bind="$attrs" 傳入內部組件。當一個組件沒有聲明任何 props 時,它包含所有父作用域的綁定 (class 和 style 除外)。

$listeners:包含了父作用域中的 (不含 .native 修飾符) v-on 事件監聽器。它可以通過 v-on="$listeners" 傳入內部組件。它是一個對象,里面包含了作用在這個組件上的所有事件監聽器,相當于子組件繼承了父組件的事件

講了這么多文字概念,我們還是來看代碼例子吧:

新建一個 father.vue 組件

<template>
   <child :name="name" :age="age" :infoObj="infoObj" @updateInfo="updateInfo" @delInfo="delInfo" />
</template>
<script>
    import Child from '../components/child.vue'

    export default {
        name: 'father',
        components: { Child },
        data () {
            return {
                name: '阿離王',
                age: 22,
                infoObj: {
                    from: '廣東',
                    job: 'policeman',
                    hobby: ['reading', 'writing', 'skating']
                }
            }
        },
        methods: {
            updateInfo() {
                console.log('update info');
            },
            delInfo() {
                console.log('delete info');
            }
        }
    }
</script>

child.vue 組件:

<template>
    <!-- 通過 $listeners 將父作用域中的事件,傳入 grandSon 組件,使其可以獲取到 father 中的事件 -->
    <grand-son :height="height" :weight="weight" @addInfo="addInfo" v-bind="$attrs" v-on="$listeners"  />
</template>
<script>
    import GrandSon from '../components/grandSon.vue'
    export default {
        name: 'child',
        components: { GrandSon },
        props: ['name'],
        data() {
          return {
              height: '180cm',
              weight: '70kg'
          };
        },
        created() {
            console.log(this.$attrs); 
       // 結果:age, infoObj, 因為父組件共傳來name, age, infoObj三個值,由于name被 props接收了,所以只有age, infoObj屬性
            console.log(this.$listeners); // updateInfo: f, delInfo: f
        },
        methods: {
            addInfo () {
                console.log('add info')
            }
        }
    }
</script>

grandSon.vue 組件

<template>
    <div>
        {{ $attrs }} --- {{ $listeners }}
    <div>
</template>
<script>
    export default {
        props: ['weight'],
        created() {
            console.log(this.$attrs); // age, infoObj, height 
            console.log(this.$listeners) // updateInfo: f, delInfo: f, addInfo: f
            this.$emit('updateInfo') // 可以觸發 father 組件中的updateInfo函數
        }
    }
</script>

這種方式的傳值雖然說不常用,感覺可讀性不是很好。但其對于組件層級嵌套比較深,使用props會很繁瑣,或者項目比較小,不太適合使用 Vuex 的時候,可以考慮用它

“vue組件間如何進行通信”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

vue
AI

台北市| 赤壁市| 稻城县| 上虞市| 东乡族自治县| 江城| 兴文县| 介休市| 阿坝县| 安泽县| 海阳市| 武鸣县| 新巴尔虎右旗| 彩票| 时尚| 金华市| 德昌县| 扶绥县| 黄平县| 宜宾县| 壤塘县| 津市市| 汝阳县| 宜城市| 句容市| 宁波市| 普洱| 古蔺县| 洛宁县| 通河县| 栾城县| 屏山县| 诏安县| 贵德县| 沁阳市| 彭阳县| 高安市| 改则县| 招远市| 重庆市| 宝丰县|