iT邦幫忙

2021 iThome 鐵人賽

DAY 10
0
Modern Web

我的Vue學習筆記系列 第 10

Day10-元件溝通傳遞(part2)

沒有props還可以傳資料嗎

v-bindv-on在沒有props的情況下一樣可以得到父層的資料。

<div id="app">
   <my-component :class="className" @click="greeting"></my-component>
</div>
<!--渲染後<div class="card block">Vue</div>--->
const app = Vue.createApp({
    data() {
        return {
            className: 'block'
        }
    },
		methods: {
        greeting() {
            alert('Hi')
        }
    }
});
app.component('my-component', {
    template: `
    <div class="card"></div>
    `,
})
app.mount('#app');

補充:

  1. v-bind="$sttrs"

在多個節點的情況下,Vue找不到DOM就會出現錯誤,加上v-bind="$sttrs"可以告訴他DOM在哪

app.component('my-component', {
    template: `
        <header>...</header>
        <body v-bind="$sttrs">...</body>
        <footer>...</footer>
    `
})
  1. inheritAttrs: false

不想要子元素繼承資料可以加這個。

app.component('my-component', {
		inheritAttrs: false,
    template: `.....`
})

遞迴元件Recursive Component

子元件中包子元件,常看到的樣子就是像樹狀圖那樣的結構,如: 側選單含有次選單。

遞迴一定要有name屬性,不然元件就不能呼叫,呼叫時最好也有:key做為唯一的標籤。

Untitled

<div id="app">
        <menu-component :title="menuData.name" :child="menuData.childNodes"></menu-component>
    </div>
const menuData = {
    name: 'uniqlo',
    childNodes: [{
        name: 'women',
        url: null,
        childNodes: [{
            name: 'top',
            url: 'https://www.uniqlo.com/tw/special/outerlineup/women/'
        },
        {
            name: 'inner',
            url: 'https://www.uniqlo.com/tw/store/feature/women/inner/wireless-bra/'
        }]
    },
    {
        name: 'men',
        url: null,
        childNodes: [{
            name: 'top',
            url: 'https://www.uniqlo.com/tw/special/topscollection/men/'
        }, {
            name: 'bottom',
            url: 'https://www.uniqlo.com/tw/special/bottomscollection/men/'
        },
        {
            name: 'shirts',
            url: 'https://www.uniqlo.com/tw/store/feature/men/tops/dress-shirts-long-and-short/'
        }]
    }, {
        name: 'child',
        url: null,
    }
    ]
}
const app = Vue.createApp({
    data() {
        return {
            menuData
        }
    }
});
app.component('menu-component', {
    name: `menu-component`,
    props: {
        title: String,
        url: String,
        child: {
            type: Array,
            default: []
        }
    },
    data() {
        return {
            isOpen: false
        }
    },
    template: `
    <ul>
    <li>
        <template v-if="child.length > 0">
            <h2 class="has-child" :class="{'isOpen':isOpen}" @click="isOpen=!isOpen">{{title}}</h2>
            <menu-component v-show="isOpen" v-for="c in child" :title=c.name :child="c.childNodes" :url="c.url">
            </menu-component>
        </template>
        <a :href="url" v-else>{{title}}</a>
    </li>
</ul>
    `
})
app.mount('#app');

自定義事件

父子元件之間的溝通方式有個流傳已久的口訣:「Props in, Event out」。—重新認識 Vue.js

當子元件要將處理過的資料送回外層父元件時,可以透過this.$emit觸發@update="updateInfo"來更新外層資料,用下圖可以清楚的知道資料的流動。

Untitled

deep
watch裡面的參數。我們都知道對象 {} 都指向一個內存地址。當其屬性改變的時候,對象的內存地址沒發生改變。故當watch監聽一個對象的時候,對象屬性發生改變,watch監聽不到。為了發現對象內部值的變化,可以在選項參數中指定 deep: true 。——Vue.js中的methods,computed,watch。

v-model元件雙向綁定

講到v-model雙向綁定元件前要先回顧一下之前的表單綁定,先前在綁定input時會使用v-bind,可以將其拆解為下

<input v-model="msg">
<!-- 拆解 -->
<input v-bind:value="msg" v-on:input="msg=$event.target.value">

使用在元件雙向綁定也是類似的概念,component 接收一個 modelValue 的值,然後透過觸發 update:modelValue 事件來更新值。

<my-component v-model="msg"></my-component>
const app = Vue.createApp({
    data() {
        return {
            msg: 'Hello Vue!!'
        }
    }
});
app.component('my-component', {
    props: ['modelValue'],
    template: `
    <input type="text" :modelValue="modelValue" @input="$emit('update:modelValue',$event.target.value)">
    `
})

自定義事件上使用v-model,屬性及事件的名稱改為

  • prop:value ⇒ modelValue
  • event:input ⇒ update:modelValue

跨層級傳遞

上下跨層

當父層元件要傳遞給最底部的元件時,可以透過provideinject機制。

  • provide : 定義要傳遞出去的資料(輸出)
  • inject : 取得頂層元件資料 (取回)

Untitled

<input type="text" v-model="msg">
<list-component></list-component>
const app = Vue.createApp({
    data() {
        return {
            msg: 'Hello Vue!!'
        }
    },
    provide() {
        return {
            provideMsg: this.msg,
            provideMsg2: Vue.computed(() => this.msg)
        }
    }
});

若要子層inject取回的資料與上層連動,需透過Vue.computed()包裝,包裝後的物件要加.value才可以運作。

app.component('list-component', {
    template: `
    <ul>
        <li v-for="i in 3">
            {{i}}
            <list-item></list-item>
        </li>
    </ul>
    `,
    components: {
        'list-item': {
            inject: ['provideMsg', 'provideMsg2'],
            template: `
            <div>{{provideMsg}}</div>
            <div>{{provideMsg2.value}}</div>`
        }
    }
})

左右跨層

底層互相傳遞資料可以透過mitt處理。

  1. 先建立mitt()實體指定一個變數bus,並在vue實體上建立created,在bus身上註冊reset事件
  2. <button-counter>元件身上新增created以對應reset事件
  3. <reset-button>元件身上放上reset方法

Untitled

Untitled

Vuex

Vue.js 3.x已不建議使用上述方式,改為Vuex管理

後面某一章節有這部分的內容,等到那天在來深入研究這一部分!今日的篇幅真夠長的?

參考資料

JavaScript - 原始型別概述
https://ithelp.ithome.com.tw/articles/10192598
data為Object或Array時的淺拷貝特性
https://yuugou727.github.io/blog/2017/07/23/vue-data-is-object-or-array/
Vue.js中的methods,computed,watch。
https://codertw.com/程式語言/695853/#outline__3
Vue 3 到底有什么不同:v-model 升级了
https://segmentfault.com/a/1190000023462922


上一篇
Day9-元件溝通傳遞(part1)
下一篇
Day11-動態元件
系列文
我的Vue學習筆記30

尚未有邦友留言

立即登入留言