iT邦幫忙

2021 iThome 鐵人賽

DAY 11
0
Modern Web

我的Vue學習筆記系列 第 11

Day11-動態元件

這章節是延伸v-if和v-show管理元件,如何用更簡便的方式做tab頁籤。

v-bind:is

做一個tab按鈕切會下面顯示的內容,直覺的方式是直接用v-if去寫三個元件

<button v-for="tab in tabs" 
				:key="tab" 
				@click="currentTab = tab" 
				:class="{'active': currentTab === tab}">
	{{tab}}
</button>
<tab-a v-if="currentTab === 'A'"></tab-a>
<tab-b v-if="currentTab === 'B'"></tab-b>
<tab-c v-if="currentTab === 'C'"></tab-c>
const app = Vue.createApp({
    data() {
        return {
            currentTab: 'A',
            tabs: ['A', 'B', 'C']
        }
    }
});
app.component('tab-a', {
    template: `
    <h1>aaaaaaaaaaaaaaaaaa</h1>
    `
})
app.component('tab-b', {
    template: `
    <h1>bbbbbbbbbbbbbbbbbb</h1>
    `
})
app.component('tab-c', {
    template: `
    <h1>ccccccccccccccccc</h1>
    `
})

用更快的方式就是使用v-bind:is,新增一個computed,當點擊按鈕時會觸發currentTab = tab,此時新的tab就會傳到:is="currentTabComponent"身上,即顯示出與用v-if一樣的結果。

<button v-for="tab in tabs" 
				:key="tab" 
				@click="currentTab = tab" 
				:class="{'active': currentTab === tab}">
	{{tab}}
</button>
<component :is="currentTabComponent"></component>
const app = Vue.createApp({
    data() {
        return {
            currentTab: 'A',
            tabs: ['A', 'B', 'C']
        }
    },
    computed: {
        currentTabComponent() {
            return `tab-${this.currentTab.toLowerCase()}`;
        }
    }
});
app.component('tab-a', {
    template: `<h1>aaaaaaaaaaaaaaaaaa</h1>`
})

app.component('tab-b', {
    template: `<h1>bbbbbbbbbbbbbbbbbb</h1>`
})
app.component('tab-c', {
    template: `<h1>ccccccccccccccccc</h1>`
})

保留元件狀態

改寫剛剛的tab頁籤,內容物改成v-model綁定的input可以改寫資料。

改寫資料後按下其它標籤會發先先前改寫的資料會被還原成初始值。

app.component('tab-a', {
    template: `<input type="text" v-model="title">`,
    data: () => ({ title: 'A component' })
})

app.component('tab-b', {
    template: `<input type="text" v-model="title">`,
    data: () => ({ title: 'B component' })
})
app.component('tab-c', {
    template: `<input type="text" v-model="title">`,
    data: () => ({ title: 'C component' })
})

若想讓資料在切換時也能保持改寫後的樣子,就可以在外層包上keep-alive

<keep-alive>
  <component :is="currentTabComponent"></component>
</keep-alive>

補充: keep-alive同一時間只會有一個子元件被渲染

搭配的屬性

  1. include / exclude 只保留/排除部分

使用時必須搭配name屬性

<!-- 寫法一:逗點格開 -->
<keep-alive include="A-TAB,B-TAB">
    <component :is="currentTabComponent"></component>
</keep-alive>
<!-- 寫法二:Regular expression -->
<keep-alive :include="/(A|B)-TAB/">
    <component :is="currentTabComponent"></component>
</keep-alive>
<!-- 寫法三:陣列 -->
<keep-alive :include="['A-TAB','B-TAB']">
    <component :is="currentTabComponent"></component>
</keep-alive>
app.component('tab-a', {
    name: 'A-TAB',
    template: `<input type="text" v-model="title">`,
    data: () => ({ title: 'A component' })
})

app.component('tab-b', {
    name: 'B-TAB',
    template: `<input type="text" v-model="title">`,
    data: () => ({ title: 'B component' })
})
app.component('tab-c', {
    name: 'C-TAB',
    template: `<input type="text" v-model="title">`,
    data: () => ({ title: 'C component' })
})
  1. max 限制元件數量

只保留最後引入的兩個狀態

<keep-alive :max="2">
    <component :is="currentTabComponent"></component>
</keep-alive>

特殊生命週期

生命週期中activateddeactivated兩個hooks是給keep-alive使用的。

直接比對有和無keep-alive的差異在生命週期上有什麼不同。

做了同樣的動作: 點擊A標籤→B標籤→C標籤

  • 有keep-alive

    切換時的順序為「建立新元件created」→「暫停目前元件 deactivated」→「掛載新的元件 mounted」→「啟用新的元件 activated」

    Untitled

  • 無keep-alive

    切換時的順序為「建立新元件created」→「銷毀目前元件 unmounted」→「掛載新的元件 mounted」

    Untitled

const app = Vue.createApp({
    data() {
        return {
            currentTab: 'A',
            tabs: ['A', 'B', 'C'],
            msgs: []
        }
    },
    computed: {
        currentTabComponent() {
            return `tab-${this.currentTab.toLowerCase()}`;
        }
    },
    methods: {
        notify(val) {
            this.msgs.push(val)
        }
    },
});
app.component('tab-a', {
    name: 'A-TAB',
    template: `<input type="text" v-model="title">`,
    data: () => ({ title: 'A component' }),
    created() {
        this.$emit('update', `${this.$options.name} Created`)
    },
    mounted() {
        this.$emit('update', `${this.$options.name} Mounted`)
    },
    unmounted() {
        this.$emit('update', `${this.$options.name} Unmounted`)
    },
    activated() {
        this.$emit('update', `${this.$options.name} Activated`)
    },
    deactivated() {
        this.$emit('update', `${this.$options.name} Deactivated`)
    }
})

app.component('tab-b', {
    name: 'B-TAB',
    template: `<input type="text" v-model="title">`,
    data: () => ({ title: 'B component' }),
    created() {
        this.$emit('update', `${this.$options.name} Created`)
    },
    mounted() {
        this.$emit('update', `${this.$options.name} Mounted`)
    },
    unmounted() {
        this.$emit('update', `${this.$options.name} Unmounted`)
    },
    activated() {
        this.$emit('update', `${this.$options.name} Activated`)
    },
    deactivated() {
        this.$emit('update', `${this.$options.name} Deactivated`)
    }
})

app.component('tab-c', {
    name: 'C-TAB',
    template: `<input type="text" v-model="title">`,
    data: () => ({ title: 'C component' }),
    created() {
        this.$emit('update', `${this.$options.name} Created`)
    },
    mounted() {
        this.$emit('update', `${this.$options.name} Mounted`)
    },
    unmounted() {
        this.$emit('update', `${this.$options.name} Unmounted`)
    },
    activated() {
        this.$emit('update', `${this.$options.name} Activated`)
    },
    deactivated() {
        this.$emit('update', `${this.$options.name} Deactivated`)
    }
})

上一篇
Day10-元件溝通傳遞(part2)
下一篇
Day12-slot插槽
系列文
我的Vue學習筆記30

尚未有邦友留言

立即登入留言