vue 聽說你很會傳值?
- 2020 年 4 月 5 日
- 筆記
前置
大小 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
provide
和inject
不推薦直接用於應用程式程式碼中- 與 React 的上下文特性很相似。這對選項需要一起使用,以允許一個祖先組件向其所有子孫後代注入一個依賴,不論組件層次有多深,並在起上下游關係成立的時間裡始終生效
provide
選項應該是一個對象或返回一個對象的函數。該對象包含可注入其子孫的屬性。在該對象中你可以使用 ES2015 Symbols 作為 key,但是只在原生支援 Symbol 和 Reflect.ownKeys 的環境下可工作provide
和inject
綁定並不是可響應的。這是 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 的新特性provide
和inject
可以實現嵌套組件之間的數據傳遞- 這兩個函數只能在
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 共享給子組件。