Vue.js 2.x筆記:元件(5)

libingql發表於2018-06-12

1. 元件簡介

  元件(Component)是 Vue.js 最強大的功能之一,元件可以擴充套件 HTML 元素,封裝可重用的程式碼。

  元件:為了拆分Vue例項的程式碼量,以不同的元件來劃分不同的功能模組,需要什麼樣的功能,可以去呼叫對應的元件。

  模組化和元件化的區別:

  ◊ 模組化:是從程式碼邏輯的角度進行劃分的;方便程式碼分層開發,保證每個功能模組的職能單一。

  ◊ 元件化:是從UI介面的角度進行劃分的;前端的元件化,方便UI元件的重用。  

2. 註冊元件

2.1 全域性元件

  註冊全域性元件語法格式:

Vue.component(tagName, options)

  其中,tagName 為元件名,options 為配置選項。

  註冊元件後呼叫方式:

<tagName></tagName>

  所有例項都能用全域性元件。

  元件名定義方式:PascalCase和kebab-case。在元件命名時可以採用PascalCase或kebab-case,但在DOM中只能使用kebab-case。

  PascalCase示例:

<div id="app">
    <my-component></my-component>
</div>
<script>
    Vue.component('MyComponent', {
        template: '<div>標題</div>'
    });

    var vm = new Vue({
        el: "#app"
    });
</script>

  kebab-case示例:

<div id="app">
    <my-component></my-component>
</div>
<script>
    Vue.component('my-component', {
        template: '<div>標題</div>'
    });

    var vm = new Vue({
        el: "#app"
    });
</script>
<div id="app">
    <home></home>
</div>
<script>
    Vue.component("home", {
        template: "<div>{{text}}</div>",
        data: function () {
            return {
                text: "主頁"
            };
        }
    });

    new Vue({
        el: "#app"
    });
</script>
<div id="app">
    <home></home>
</div>
<script>
    var homeTpl = Vue.extend({ 
        template: "<div>{{text}}</div>",
        data: function () {
            return {
                text: "主頁"
            };
        }
    });

    Vue.component('home', homeTpl);

    new Vue({
        el: "#app"
    });
</script>

  使用template標籤:

<div id="app">
    <home></home>
</div>
<template id="tpl">
    <div>{{text}}</div>
</template>
<script>
    Vue.component("home", {
        template: "#tpl",
        data: function () {
            return {
                text: "主頁"
            };
        }
    });

    new Vue({
        el: "#app"
    });
</script>

2.2 區域性元件

  區域性元件只能在當前Vue示例中使用。

<div id="app">
    <home></home>
</div>
<script>
    new Vue({
        el: "#app",
        components: {
            "home": {
                template: "<div>{{text}}</div>",
                data: function () {
                    return {
                        text: "主頁"
                    };
                }
            }
        }
    });
</script>

2.3 Vue.extend

2.3.1 基本使用

<div id="app">
    <home></home>
</div>
<script>
    var home = Vue.extend({
        template: "<div>標題</div>"
    });

    Vue.component("home", home);

    new Vue({
        el: "#app"
    });
</script>

2.3.2 引數data

  data:在 Vue.extend() 中必須是函式。

<body>
    <task></task>

    <script>
        var task = Vue.extend({
            template:"<div>{{ taskName }}</div>",
            data:function(){
                return {
                    taskName:"任務名稱"
                }
            }
        });

        new task().$mount("task");
    </script>
</body>

2.3.3 使用$mount

  mount:掛載,將vue例項掛靠在某個dom元素上的一個過程。

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>libing.vue</title>
    <script src="node_modules/vue/dist/vue.min.js"></script>
</head>

<body>
    <div id="app"></div>
    <script>
        var home = Vue.extend({
            template: "<div>標題</div>"
        });

        new home().$mount("#app");
    </script>
</body>

</html>

3. 元件通訊

3.1 props:父元件向子元件傳遞資料

  prop 是元件用來傳遞資料的自定義特性,在元件上註冊自定義屬性。

  prop特性註冊成為元件例項的屬性。

   props :父元件向子元件傳遞資料。

  一個元件預設可以擁有任意數量的 prop,任何值都可以傳遞給任何 prop。

3.1.1 靜態props

  示例:

<div id="app">
    <home text="主頁"></home>
</div>
<script>
    var homeTpl = Vue.extend({
        props:["text"],
        template: "<div>{{text}}</div>"
    });

    Vue.component('home', homeTpl);

    new Vue({
        el: "#app"
    });
</script>

3.1.2 動態props

  使用 v-bind 動態繫結 props 的值到父元件的資料中。每當父元件的資料變化時,該變化也會傳導給子元件。

<div id="app">
    <home v-bind:text="text"></home>
</div>
<script>
    var homeTpl = Vue.extend({
        props: ["text"],
        template: "<div>{{text}}</div>"
    });

    Vue.component('home', homeTpl);

    new Vue({
        el: "#app",
        data: {
            text: "主頁"
        }
    });
</script>

  由於HTML Attribute不區分大小寫,當使用DOM模板時,camelCase的props名稱要轉為kebab-case。

<div id="app">
    <home warning-text="提示資訊"></home>
</div>
<script>
    Vue.component('home', {
        props: ['warningText'],
        template: '<div>{{ warningText }}</div>'
    });

    var vm = new Vue({
        el: "#app"
    });
</script>

  傳遞的資料可以是來自父級的動態資料,使用指令v-bind來動態繫結props的值,當父元件的資料變化時,也會傳遞給子元件。

<div id="app">
    <home v-bind:warning-text="warningText"></home>
</div>
<script>
    Vue.component('home', {
        props: ['warningText'],
        template: '<div>{{ warningText }}</div>'
    });

    var vm = new Vue({
        el: "#app",
        data: {
            warningText: '提示資訊'
        }
    });
</script>

注:prop 是單向傳遞,當父元件的屬性變化時,將傳遞給子元件,但是不會反過來。這是為了防止子元件無意修改了父元件的狀態。

3.1.3 props驗證

  為元件的 prop 指定驗證要求,如果有一個需求沒有被滿足,則 Vue 會在控制檯中警告。

Vue.component('my-component', {
    props: {
        // 基礎的型別檢查 (`null` 匹配任何型別)
        propA: Number,
        // 多個可能的型別
        propB: [String, Number],
        // 必填的字串
        propC: {
            type: String,
            required: true
        },
        // 帶有預設值的數字
        propD: {
            type: Number,
            default: 100
        },
        // 帶有預設值的物件
        propE: {
            type: Object,
            // 物件或陣列且一定會從一個工廠函式返回預設值
            default: function () {
                return {
                    message: 'hello'
                }
            }
        },
        // 自定義驗證函式
        propF: {
            validator: function (value) {
                // 這個值必須匹配下列字串中的一個
                return ['success', 'warning', 'danger'].indexOf(value) !== -1
            }
        }
    }
});

  型別檢查:type可以是下列原生建構函式中的一個:String、Number、Boolean、Array、Object、Date、Function、Symbol,也可以是一個自定義的建構函式,並且通過 instanceof 來進行檢查確認。

  示例:

<div id="app">
    <parent-component></parent-component>
</div>

<template id="child-component1">
    <h2>{{ message }}</h2>
</template>
<template id="child-component2">
    <h2>{{ message }}</h2>
</template>
<template id="parent-component">
    <div>
        <child-component1></child-component1>
        <child-component2></child-component2>
    </div>
</template>

<script>
    Vue.component('parent-component', {
        template: '#parent-component',
        components: {
            'child-component1': {
                template: '#child-component1',
                data() {
                    return {
                        message: '子元件1'
                    };
                }
            },
            'child-component2': {
                template: '#child-component2',
                data() {
                    return {
                        message: '子元件2'
                    };
                }
            }
        }
    });

    var vm = new Vue({
        el: "#app"
    });
</script>

  示例:

<div id="app">
    <todo :todo-data="taskList"></todo>
</div>

<template id="tpl-todo-item">
    <li>{{ id }} - {{ text }}</li>
</template>

<template id="tpl-todo-list">
    <ul>
        <todo-item v-for="item in todoData" :id="item.id" :text="item.text"></todo-item>
    </ul>
</template>

<script>
    // 構建一個子元件
    var todoItem = Vue.extend({
        template: "#tpl-todo-item",
        props: {
            id: {
                type: Number,
                required: true
            },
            text: {
                type: String,
                default: ''
            }
        }
    })

    // 構建一個父元件
    var todoList = Vue.extend({
        template: "#tpl-todo-list",
        props: {
            todoData: {
                type: Array,
                default: []
            }
        },
        // 區域性註冊子元件
        components: {
            todoItem: todoItem
        }
    })

    // 註冊到全域性
    Vue.component('todo', todoList)

    new Vue({
        el: "#app",
        data: {
            taskList: [{
                    id: 1,
                    text: 'New'
                },
                {
                    id: 2,
                    text: 'InProcedure'
                },
                {
                    id: 3,
                    text: 'Done'
                }
            ]
        }
    });
</script>

3.2 自定義事件:子元件向父元件傳遞資料

  每一個Vue例項都實現事件介面:

  $on(eventName):監聽事件

  $emit(eventName) :觸發事件

  子元件需要向父元件傳遞資料時,子元件用$emit(eventName)來觸發事件,父元件用$on(eventName)來監聽子元件的事件。

<div id="app">
    <searchbar></searchbar>
</div>

<template id="tpl-search-form">
    <div class="input-group form-group" style="width: 500px;">
        <input type="text" class="form-control" placeholder="請輸入查詢關鍵字" v-model="keyword" />
        <span class="input-group-btn">
            <input type="button" class="btn btn-primary" value="查詢" @click="search">
        </span>
    </div>
</template>
<template id="tpl-search-bar">
    <searchform @onsearch="search"></searchform>
</template>

<script>
    // 構建一個子元件
    var searchform = Vue.extend({
        template: "#tpl-search-form",
        data: function () {
            return {
                keyword: 'libing'
            };
        },
        methods: {
            search: function () {
                this.$emit('onsearch', this.keyword);
            }
        }
    });

    // 構建一個父元件
    var searchbar = Vue.extend({
        template: "#tpl-search-bar",
        components: {
            searchform: searchform
        },
        methods: {
            search(keyword) {
                console.log(keyword);
            }
        }
    })

    // 註冊到全域性
    Vue.component('searchbar', searchbar);

    new Vue({
        el: "#app"
    });
</script>

  購物車示例:

<div id="app">
    <shoppingcart :shopppingcarts="products" @calc="getTotal"></shoppingcart>
    <div>總計:{{ totalPrice }}</div>
</div>
<template id="shoppingcart">
    <table>
        <tr>
            <th>商品ID</th>
            <th>商品名稱</th>
            <th>單價</th>
            <th>數量</th>
        </tr>
        <tr v-for="item in shopppingcarts">
            <td>{{ item.ID }}</td>
            <td>{{ item.ProductName }}</td>
            <td>{{ item.UnitPrice }}</td>
            <td><input type="text" v-model="item.Quantity" @change="calcTotal" /></td>
        </tr>
    </table>
</template>
<script>
    var shoppingcart = Vue.extend({
        template: "#shoppingcart",
        props: ["shopppingcarts"],
        methods: {
            calcTotal: function () {
                this.$emit("calc");
            }
        }
    });

    new Vue({
        el: "#app",
        components: {
            shoppingcart: shoppingcart
        },
        data: {
            totalPrice: 100,
            products: [{
                ID: 1,
                ProductName: "手機",
                UnitPrice: 1000,
                Quantity: 2
            }, {
                ID: 2,
                ProductName: "電腦",
                UnitPrice: 5000,
                Quantity: 5
            }]
        },
        methods: {
            getTotal() {
                console.log(new Date());
                this.totalPrice = 0;
                this.products.forEach(product => {
                    this.totalPrice += product.UnitPrice * product.Quantity;
                });
            }
        },
        mounted() {
            //當vue執行完畢之後,去執行函式
            this.getTotal();
        }
    });
</script>

相關文章