iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 28
1

Vue.$set()

在開發過程中,我們時常會遇到這樣一種情況:當 Vue 的 data 裡面的宣告或者已經賦值過的對像或者陣列(陣列裡面的值是對象)時,向對像中添加新的屬性,如果更新此屬性的值,是不會更新視圖的。

根據官方文檔定義:如果在實例創建之後添加新的屬性到實例上,它不會觸發視圖更新。

受現代 JavaScript 的限制(以及廢棄 Object.observe),Vue 不能檢測到對象屬性的添加或刪除。由於 Vue 會在初始化實例時對屬性執行 getter/setter 轉化過程,所以屬性必須在 data 對像上存在才能讓 Vue 轉換它,這樣才能讓它是響應的。

範例:

<div id="app">
    <table class="table">
        <tbody>
            <tr is="row-component" 
                v-for="(item, key) in data" 
                :item="item" 
                :key="key">
            </tr>
        </tbody>
    </table>
</div>
<script type="text/x-template" id="row-component">
    <tr>
        <td>{{ item.name }}</td>
        <td>{{ item.cash }}</td>
        <td>{{ item.icash }}</td>
        <td>
            <span v-if="data.item">{{ data.item.name }}</span>
            <button class="btn btn-sm btn-primary" @click="addData()">寫入資料</button>
        </td>
    </tr>
</script>
var child = {
    props: ['item'],
    template: '#row-component',
    data: function() {
        return {
            data: {}
        }
    },
    methods: {
        addData: function() {
            this.data.item = {
                name: this.item.name
            }
            console.log(this.data, this);
        }
    },
    mounted: function() {
        console.log('Component:', this)
    }
}

var app = new Vue({
    el: '#app',
    data: {
        data: [{
            name: '小明',
            cash: 100,
            icash: 500,
        }, {
            name: '杰倫',
            cash: 10000,
            icash: 5000,
        }, {
            name: '漂亮阿姨',
            cash: 500,
            icash: 500,
        }, {
            name: '老媽',
            cash: 10000,
            icash: 100,
        }, ]
    },
    components: {
        "row-component": child
    },
    mounted: function() {
        console.log('Vue init:', this)
    }
});

https://ithelp.ithome.com.tw/upload/images/20190929/20109609XpPg40rLY5.png

我們先打開 console 看看 this 裡面是什麼東西:
https://ithelp.ithome.com.tw/upload/images/20190929/201096093B44eO5WZG.png
這邊先看 Vue init 裡面,裡面有個 data ,我們打開之後會看到 (...) 的字樣:

會有這個結果是因為是使用 gettersetter 的綁定,然後監控這個數據,所以當數據有更動的時候才會及時的顯示到畫面上,那麼也就是說如果你在寫入之料的時候,你發現沒有 gettersetter 就代表說你的資料結構並沒有進入 vue 的綁定裡面。

接下來來看一下 component 的部分,我們如果在寫入資料的時候並沒有預先定義 data 裡面的資料結構的話會出錯,出錯的話就不會出現 gettersetter

var child = {
    props: ['item'],
    template: '#row-component',
    data: function() {
        return {
            data: {}
        }
    },
    methods: {
        addData: function() {
            this.data.item = {
                name: this.item.name
            }
            console.log(this.data, this);
        }
    },
    mounted: function() {
        console.log('Component:', this)
    }
}


這邊我們就來做測試,當我們按下按鈕的時候 addData() 看看會發生什麼事情:

可以看到畫面上並沒有顯示出來人名,而且往 console 看 this 結構明明確實是有把資料寫入到 data 中,,也無法使用 vue 的其他語法,是因為它並沒有確實的寫進去。

使用 set 解決

https://ithelp.ithome.com.tw/upload/images/20190929/20109609I6Zk6esmjA.png
addData 改寫成以下:

addData: function() {
        this.$set(this.data, 'item', {
            name: this.item.name
        });
        console.log(this.data, this);
    }
}


這邊就是介紹到我們在寫 vue 的時候並沒有辦法預先的把資料結構訂得非常完整,尤其是像 Ajax 我們所找回來的資料沒辦法預先定義,那這個時候就可以使用 set 寫到 vue 的資料結構內。

Mixin

Mixins 是一種讓元件共用功能的方法,它和 extend 有一點點像,但差別就是在於 extend 是由單一元件來作延伸,而 Minxins 可以混合多個元件的行為。使用方式即是將共用功能以物件(以下稱為 mixin object)的方式傳入 mixins option。mixin object 可傳入元件的任何 option,例如:data、computed 或 methods。當元件選用 Mixins 時,使用的 mixin object 會和此元件的其他 option 混用。

範例:

<div id="app">
    <table class="table">
        <tbody>
            <tr is="row-component" v-for="(item, key) in data" :item="item" :key="key"></tr>
        </tbody>
    </table>
</div>
<script type="text/x-template" id="row-component">
    <tr>
        <td>{{ item.name }}</td>
        <td>{{ item.cash | currency | dollarSign }}</td>
        <td>{{ item.icash | currency | dollarSign }}</td>
    </tr>
</script>
Vue.component('row-component', {
    props: ['item'],
    data: function() {
        return {
            data: {},
        }
    },
    template: '#row-component',
    filters: {
        dollarSign: function(n) {
            return `$ ${n}`
        },
        currency: function(n) {
            return n.toFixed(2).replace(/./g, function(c, i, a) {
                return i && c !== "." && ((a.length - i) % 3 === 0) ? ',' + c : c;
            });
        }
    },
    mounted() {
        console.log('這段是 Mixin 產生')
    }
});


var app = new Vue({
    el: '#app',
    data: {
        data: [{
            name: '小明',
            cash: 100,
            icash: 500,
        }, {
            name: '杰倫',
            cash: 10000,
            icash: 5000,
        }, {
            name: '漂亮阿姨',
            cash: 500,
            icash: 500,
        }, {
            name: '老媽',
            cash: 10000,
            icash: 100,
        }, ]
    },
    mounted: function() {
        console.log('Vue init:', this)
    }
});

https://ithelp.ithome.com.tw/upload/images/20190929/20109609NxyogPcsPu.png

這邊我們嘗試使用 mixins 來把元件建起來:

var mixin = {
    template: '#row-component',
    filters: {
        dollarSign: function(n) {
            return `$ ${n}`
        },
        currency: function(n) {
            return n.toFixed(2).replace(/./g, function(c, i, a) {
                return i && c !== "." && ((a.length - i) % 3 === 0) ? ',' + c : c;
            });
        }
    },
}

var mixinHook = {
    mounted() {
        console.log('這段是 Mixin 產生')
    }
}

然後加入到元件中, mixins 吃的是陣列,所以可以有多個 mixin object:

Vue.component('row-component', {
    props: ['item'],
    data: function() {
        return {
            data: {},
        }
    },
    template: '#row-component',
    mixins: [mixin, mixinHook]
});

出來的畫面會跟先前一樣,額外的 hook 也能成功啟用。
https://ithelp.ithome.com.tw/upload/images/20190929/201096094Vey6cOhaD.png

Option Merging

Hooks

當元件與使用的 mixin object 有相同的 option 時,如果是鉤子(hooks),就會全部被合併為一個陣列,因此皆能被執行,並且 mixin 物件中的鉤子會先被執行。

var mixin = {
  created: function () {
    console.log('mixin hook called');
  }
};

new Vue({
  mixins: [mixin],
  created: function () {
    console.log('component hook called');
  }
});

參考資料

Vue-给对象新增属性(使用Vue.$set())
Vue.js: Mixins


上一篇
Vue 27 常用的 API [1]
下一篇
Vue 28 常用的 API [3]
系列文
學習 vue 30天30

尚未有邦友留言

立即登入留言