vue 聽說你很會傳值?


前置

大小 vue 項目都離不開組件通訊, 在這里總結一下vue組件通訊方式並列出, 都是簡單的例子. 適合像我這樣的小白。如有錯誤,歡迎指正。

溫馨提示: 下文沒有列出 vuex, vuex 也是重要的組件通訊方式。

props

  • 最常用的組件通訊方式
  • 值可以是數組或對象,使用對象時可以配置高級選項,如類型檢測、自定義驗證和設置默認值
  • 方向:父 -> 子

Son.vue

export default {
  props: {
    text: {
      type: String,
      required: true,
    },
  },

  mounted() {
    console.log(this.text) // 我是父組件提供給子組件的值
  },
}

App.vue

<template>
  <Son text='我是父組件提供給子組件的值'/>
</template>

<script>
import Son from './components/dispatch/Son'
export default {
  name: 'app',
  components: {
    Son,
  }
},
</script>

$refs

  • 常用的方式
  • 返回注冊過 ref 特性的所有 DOM 元素和組件實例
  • 可以用來操作 DOM
  • 可以用來傳值
  • 方向:子 -> 父

Son.vue

export default {
  methods: {
    sonFunc() {
      console.log('我是子組件的值')
    },
  },
}

App.vue

<template>
  <Son ref="sonref"/>
</template>

<script>
import Son from './components/dispatch/Son'
export default {
  name: 'app',
  components: {
        Son,
  },
  mounted() {
    this.$refs.sonref.sonFunc()
  },
}
</script>

控制台打印: 我是子組件的值

$emit

  • $emit 用來觸發當前實例上的事件
  • 方向:子 -> 父
  • 參數一:來觸發的當前實例上的事件函數
  • 參數二:附加參數,傳給監聽器回調

Son.vue

export default {
  mounted() {
    this.$emit('customFunc', '我是子組件傳給父組件的值')
  },
}

App.vue

<template>
  <Son v-on:customFunc="fatherFunc" />
</template>

<script>
import Son from './components/dispatch/Son'
export default {
  name: 'app',
  components: {
    Son,
  },

  methods: {
    fatherFunc(value) {
      console.log(value) // 我是子組件傳給父組件的值
    },
  },
}
</script>

@update

  • 需要配合 .sync 使用
  • 與上面的 $emit 寫法類似
  • 不同之處在於$emit 的第一個參數不在是當前實例上的事件函數
  • 方向:子 -> 父

Son.vue

export default {
  mounted() {
    this.$emit("update:text", '我是子組件傳給父組件的值')
  }
}

App.vue

<template>
  <Son :text.sync='text'/>
</template>

<script>
import Son from "./components/dispatch/Son"
export default {
  data() {
    return {
      text: ''
    }
  },
  mounted() {
    console.log(this.text); // 我是子組件傳給父組件的值
  }
}
</script>

接下來看下面的寫法,上面這種寫法是對如下方式的簡寫, 或者稱之為語法糖。可以不借助 .sync

Son.vue

export default {
    mounted () {
        this.$emit('update:text','我是子組件傳給父組件的值')
    }
}

App.vue

 <Son @update:text="v => (this.value = v)" />

 import Son from "./components/dispatch/Son"
 export default {
  mounted() {
    console.log(this.value) // 我是子組件傳給父組件的值
  }
}

v-model

  • v-model 常用來給 input 實現雙向數據綁定
  • v-model 也可以用來傳值
  • 有局限性,只能傳 input value
<input v-model="text">

等價於:

<input
  v-bind:value="text"
  v-on:input="text = $event.target.value"
>

接下來看如何通過 v-model 傳值。

Son.vue

<template>
  <input
    v-bind:value="value"
    v-on:input="$emit('input', $event.target.text)"
  />
</template>

<script>
export default {
    data() {
        return {
            value: '我是子組件傳給父組件的值',
        }
    }
}
</script>

App.vue

<template>
  <Son v-model="text" />
</template>

<script>
import Son from './components/dispatch/Son'
export default {
  name: 'app',
  components: {
    Son,
  }
}
</script>

$parent $childred

  • $parent: 父實例,如果當前實例有的話
  • $children: 當前實例的直接子組件
  • $parent $childred 通過封裝可以實現不同方向的傳值

$children 並不保證順序,也不是響應式的。可以使用一個數組配合 v-for 來生成子組件,使用 Array 作為真正的來源。

App.vue

export default {
  data() {
    return {
      value: '我是父組件的值',
    }
  },

Son.vue

export default {
  mounted: {
      console.log(this.$parent.value) // 我是父組件的值
      this.$parent.value = 666
      console.log(this.$parent.value) // 666
  },
}

簡單封裝一下即可實現$parent 配合 $emit 實現跨級向上傳值。

main.js

Vue.prototype.$dispatch = function(event, value) {
  let parent = this.$parent
  while (parent) {
    parent.$emit(event, value)
    parent = parent.$parent
  }
}

這樣使用: this.$dispatch('event',value)

簡單封裝一下即可實現$children 配合 $emit 實現向下傳值。

Vue.prototype.$broadcast = function(event, value) {
  const broadcast = children => {
    children.forEach(child => {
      child.$emit(event, value)
      if (child.$children) {
        broadcast(child.$children)
      }
    })
  }
  broadcast(this.$children)
}

這樣使用: this.$broadcast('event',value)

$attrs

  • 獲取父組件通過 v-bind 傳過去的所有值
  • class 和 style 除外
  • 可以通過 v-bind="$attrs" 傳入內部組件
  • 只能在 <template> 中使用
  • 方向:父 -> 子

App.vue

<template>
  <Son :value1="123" :value2="456" />
</template>

import Son from './components/dispatch/Son'
export default {
  name: 'app',
  components: {
        Son,
  },
}

Son.vue

<template>
  <div>{{$attrs}}</div>
</template>

<script>
export default {
  inheritAttrs: false,
}
</script>

$listener

  • 獲取父作用域中的 () v-on 事件監聽器。
  • 不含 .native 修飾器修飾的時間監聽器。
  • 可以通過 v-on="$listeners" 傳入內部組件(孫子組件)。
  • 方向:父 -> 子

App.vue

<template>
  <Son @customFunc="fatherFunc"/>
</template>

<script>
import Son from './components/dispatch/Son'
export default {
  name: 'app',
  components: {
        Son,
  },
  methods: {
    fatherFunc() {
      console.log('666')
    },
  },
}
</script>

Son.vue

<template>
  <button @click="$listeners.customFunc()">看</button>
</template>

provide inject

  • provideinject 不推薦直接用於應用程序代碼中
  • 與 React 的上下文特性很相似。這對選項需要一起使用,以允許一個祖先組件向其所有子孫后代注入一個依賴,不論組件層次有多深,並在起上下游關系成立的時間里始終生效
  • provide 選項應該是一個對象或返回一個對象的函數。該對象包含可注入其子孫的屬性。在該對象中你可以使用 ES2015 Symbols 作為 key,但是只在原生支持 Symbol 和 Reflect.ownKeys 的環境下可工作
  • provideinject 綁定並不是可響應的。這是 vue 刻意為之
  • 如果你傳入了一個可監聽的對象,那么其對象的屬性還是可響應的

這里有一個簡單的示例:

App.vue

<template>
  <Son />
</template>

<script>
import Son from './components/dispatch/Son'
export default {
  name: 'app',
  components: {
        Son,
  },
  provide() {
    return {
      text: '我是父組件的值',
    }
  },
}
</script>

Son.vue

export default {
  inject: ['text'],
  mounted() {
    console.log(this.text) // 我是父組件的值
  },
}

事件總線

  • EventBus 又稱為事件總線
  • 不是一個具體的 API,EventBus 代表一種思路
  • 可以看作 vuex 的究極壓縮版

App.vue

<template>
  <div>
    <Son />
  </div>
</template>

<script>
import Son from './components/dispatch/Son'
export default {
  name: 'app',
  components: {
        Son,
  },
  mounted() {
    this.$EventBus.$emit('event', 'app.vue')
  },
}
</script>

Son.vue

export default {
  mounted() {
    this.$EventBus.$on('event', function(v) {
      console.log(v)
    })
  },
}

Observable

  • observable 可以讓一個對象可響應
  • vue 內部會用它來處理 data 函數返回的對象
  • 返回的對象可以直接用於渲染函數和計算屬性內,並且會在發生改變時觸發相應的更新
  • 可以作為最小化的跨組件狀態存儲器,用於簡單的場景

store.js

import Vue from 'vue'

export const store = Vue.observable({ text: '我是store里的' })
export const mutations = {
  setText(text) {
    store.text = text
  },
}

App.vue

import { store, mutations } from '../store'
export default {
    mounted() {
      console.log(store.text) //我是store里的
      mutations.setText('我在App.vue中將你改變')
      console.log(store.text) //我在App.vue將你改變
  },
}

composition-api

  • composition-api 包含 vue3 的新特性
  • provideinject 可以實現嵌套組件之間的數據傳遞
  • 這兩個函數只能在 setup 函數中使用
  • 父級組件中使用 provide 函數向下傳遞數據
  • 子級組件中使用 inject 獲取上層傳遞過來的數據
  • 不限層級。

App.vue

<template>
  <provideAndInject />
</template>

<script>
import { provide } from "@vue/composition-api"
import provideAndInject from "./components/provideAndInject"

export default {
  name: "app",
  components: {
    provideAndInject
  },
  setup() {
    // provide('數據名稱', 要傳遞的數據)
    provide("customVal", "我是父組件向子組件傳遞的值");
  }
};
</script>

Son.vue

<template>
  <h3>{{ customVal }}</h3>
</template>

<script>
import { inject } from "@vue/composition-api";

export default {
  setup() {
    //調用 inject 函數,通過指定的數據名稱,獲取到父級共享的數據
    const customVal = inject("customVal");

    return {
      customVal
    };
  }
};
</script>

父組件可以通過 ref 創建響應式數據通過 provide 共享給子組件。


免責聲明!

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



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