iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 27
1

會分兩天去寫常用的 Vue API。在卡斯伯老師的課程上之第八節,提到了以下的常用的 Vue API:

  • Extend
  • Filter
  • Set
  • Mixin
  • Directive

這邊我們就先來看看 ExtendFilter 可以如何使用吧:

Extend

什麼事 Vue Extend

Vue.extend 返回的是一個擴展實例構造器,也就是預設了部分選項的 Vue 實例構造器。其主要用來服務於 Vue.component,用來生成組件。可以簡單的理解為當在模板中遇到該組件名稱作為標籤的自定義元素時,會自動調用擴展實例構造器來生產組件實例,並掛載到自定義元素上。

我們用以下範例來解釋:

<div id="app">
    <table class="table">
        <tbody>
            <tr is="row-component-one" 
                v-for="(item, key) in data" 
                v-if="key % 2" :item="item" :key="key">
            </tr>
            <tr is="row-component-two" 
                v-for="(item, key) in data" 
                v-if="(key - 1) % 2" :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>

<script type="text/x-template" id="row-component-two">
    <tr class="bg-primary text-white">
        <td>{{ item.name }}</td>
        <td>{{ item.cash | currency | dollarSign }}</td>
        <td>{{ item.icash | currency | dollarSign }}</td>
    </tr>
</script>
var childOne = {
    props: ['item'],
    data: function() {
        return {
            data: {},
            extendData: '這段文字是 extend 得到'
        }
    },
    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: function() {
        console.log('Extend:', this)
    }
}

var childTwo = {
    props: ['item'],
    data: function() {
        return {
            data: {},
            extendData: '這段文字是 extend 得到'
        }
    },
    template: '#row-component-two',
    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: function() {
        console.log('Extend:', 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-one": childOne,
        "row-component-two": childTwo,
    },
    mounted: function() {
        console.log('Vue init:', this)
    }
});

https://ithelp.ithome.com.tw/upload/images/20190928/20109609zh6GnMQb8S.png
假設我們有兩個或多個差不多的元件,差異非常小只有少部分的不同,以上面的範例來看,元件之間的差異只有顏色上的不同而已,我們就可以用 extend 來處理,這裡可以把重複的部分抓取出來,之後要套用的時候可以更為快速。

我們先使用 Vue 的建構子,創建一個子類別,參數是一個包含 Component 選項的物件。這裏我們先把重複的元件剪下貼到 extend 中:

var newExtend = Vue.extend({
    data: function() {
        return {
            data: {},
            extendData: '這段文字是 extend 得到'
        }
    },
    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: function() {
        console.log('Extend:', this)
    }
})

然後我們在 childOne 上使用 extend:

var childOne = {
    props: ['item'],
    extends: newExtend
}

https://ithelp.ithome.com.tw/upload/images/20190928/20109609zh6GnMQb8S.png
這裏 childOne 使用 newExtend 作為基底然後來延伸使用,可以看到跟先前的畫面是一樣的。說到 延伸 這個詞有著可以多做些變化的意思,意思是說我們可以對延伸的元件做些調整,以 childTwo 為例改成以下樣子:

var childTwo = {
    props: ['item'],
    template: '#row-component-two',
    extends: newExtend
}

這個樣子好像看不太出個所以然,那我們多加一個 mounted hook以及新增 data 上去看看:

var childTwo = {
    props: ['item'],
    template: '#row-component-two',
    extends: newExtend,
    mounted() {
        console.log('childTwo')
    },
}

https://ithelp.ithome.com.tw/upload/images/20190928/20109609SyFc35zuhg.png
https://ithelp.ithome.com.tw/upload/images/20190928/20109609FhaM5gOwDA.png
所以用 extend 它不僅會執行它自己本身的生命週期內的函式之外,如果在生命週期內加入新的函式的話,新的也同時會執行。此外可以看到新加入的 data 並沒有取代 extend 裡面的 data 而是同時並存,extend 不僅可以繼承原本的資料結構外,新的公能也可以不斷的加上去,當然如果更動的變數是 extend 內的 data 也是可以對他做變更的。

var childTwo = {
    props: ['item'],
    data: function() {
        return {
            childTwo: "元件2",
            extendData: '這段文字是 元件 變更'
        }
    },
    template: '#row-component-two',
    extends: newExtend,
    mounted() {
        console.log('childTwo')
    },
}

https://ithelp.ithome.com.tw/upload/images/20190928/20109609jVRtzDUJW6.png

Filter

Vue 提供的 filters 可以讓你自定義過濾器,filters 最主要的功能是將文字資料格式化。

Filter 使用方法:

  1. mustache 模板插值(mustache interpolations)
{{ msg | filterA }}
  1. v-bind 表達式(v-bind expressions)
<div v-bind:id="msg | filterA"></div>

範例:

<div id="app">
<table class="table">
    <tbody>
        <tr is="row-component" 
            v-for="(item, key) in data" 
            :item="item" :key="key">
        </tr>
    </tbody>
</table>
{{ data[1].cash }}
</div>
<script type="text/x-template" id="row-component">
    <tr>
        <td>{{ item.name }}</td>
        <td>{{ item.cash }}</td>
        <td>{{ item.icash }}</td>
    </tr>
</script>
var child = {
    props: ['item'],
    template: '#row-component',
    data: function() {
        return {
            data: {}
        }
    },
    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/20190928/201096099lhJ49Q1R6.png

這邊我們做貨幣轉換格式以及錢符號的 filter

<script type="text/x-template" id="row-component">
    <tr>
        <td>{{ item.name }}</td>
        <!-- pipe(|) 的先後順序很重要,有機會影響結果  -->
        <td>{{ item.cash | currency | dollarSign}}</td>
        <td>{{ item.icash | currency | dollarSign}}</td>
    </tr>
</script>
var child = {
    props: ['item'],
    template: '#row-component',
    data: function() {
        return {
            data: {}
        }
    },
    filters: {
        dollarSign: function(n) {
                return `$ ${n}`;
            },
        currency: function(n) {
            return n.toString().replace(/^(-?\d+?)((?:\d{3})+)(?=\.\d+$|$)/, function(all, pre, groupOf3Digital) {
                return pre + groupOf3Digital.replace(/\d{3}/g, ',$&');
            })
        }
    },
    mounted: function() {
        console.log('Component:', this)
    }
}

https://ithelp.ithome.com.tw/upload/images/20190928/20109609EhvcvAlvzP.png

filters的種類分為本地過濾器(local filter)與全局過濾器(global filter):

  1. 本地過濾器(local filter)
<div id="app">
    <span>{{ text | toUpperCase }}</span>
</div>
var vm = new Vue ({
    el: '#app',
    data: {
        text: 'hello vue'
    },
    filters: {
        toUpperCase(value) {
            return value.toUpperCase();
        }
    }
});
  1. 全局過濾器(global filter)
    filters 將資料的第一個字變成大寫,順便練習前面使用過的 v-model
<div id="app">
    <input type="text" v-model="mytext">
    <p>{{ mytext | capitalize }}</p>
</div>
Vue.filter('capitalize', function(value) {
    return value.charAt(0).toUpperCase() + value.slice(1);
});

var vm = new Vue ({
    el: '#app',
    data: {
        mytext: 'hello'
    }
});

filters 可以用 pipe(|)串聯:

{{ message | filterA | filterB }}

filters 是 JavaScript 函數,所以可以傳入參數:

{{ message | filterA('arg1', 'arg2') }}

參考資料

用範例理解 Vue.js #17:Global API(extend, nextTick, directive)
Vue 2.0學習筆記:Vue.extend構造器的延伸
Day14 - [Options] filters & computed
Vue.js: Filter 過濾器


上一篇
Vue 26 使用 is 動態元件 Dynamic Componen
下一篇
Vue 28 常用的 API [2]
系列文
學習 vue 30天30

尚未有邦友留言

立即登入留言