1. 程式人生 > >Vue.js 2.x:元件的定義和註冊(詳細的圖文教程)

Vue.js 2.x:元件的定義和註冊(詳細的圖文教程)

本文最初發表於部落格園,並在GitHub上持續更新前端的系列文章。歡迎在GitHub上關注我,一起入門和進階前端。

以下是正文。

前言

什麼是元件

元件: 元件的出現,就是為了拆分Vue例項的程式碼量的,能夠讓我們以不同的元件,來劃分不同的功能模組,將來我們需要什麼樣的功能,就可以去呼叫對應的元件即可。

模組化和元件化的區別

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

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

全域性元件的定義和註冊

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

全域性元件的定義和註冊有三種方式,我們接下來講一講。

寫法一

寫法一:使用Vue.extend方法定義元件,使用 Vue.component方法註冊元件。

程式碼舉例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="vue2.5.16.js"></script>
</head>

<body>
    <div id="app">
        <!-- 如果要使用元件,直接把元件的名稱,以 HTML 標籤的形式,引入到頁面中,即可 -->
        <account> </account>
    </div>

    <script>
        //第一步:使用 Vue.extend 定義元件
        var myAccount = Vue.extend({
            template: '<div><h2>登入頁面</h2> <h3>註冊頁面</h3></div>' // 通過 template 屬性,指定了元件要展示的HTML結構。template 是 Vue 中的關鍵字,不能改。
        });
        //第二步:使用 Vue.component 註冊元件
        // Vue.component('元件的名稱', 創建出來的元件模板物件)
        Vue.component('account', myAccount); //第一個引數是元件的名稱(標籤名),第二個引數是模板物件

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

</html>

上方程式碼中,在註冊元件時,第一個引數是標籤名,第二個引數是元件的定義。

執行結果如下:

程式碼截圖如下:

上圖中,注意兩點:

注意1、紅框部分,要保證二者的名字是一致的。如果在註冊時,元件的名稱是駝峰命名,比如:

Vue.component('myComponent', myAccount); //第一個引數是元件的名稱(標籤名),第二個引數是模板物件

那麼,在標籤中使用元件時,需要把大寫的駝峰改為小寫的字母,同時兩個單詞之間使用-進行連線:

<my-component> </my-component>

Vue.component('my')

注意2、綠框部分,一定要用一個大的根元素(例如<div>

)包裹起來。如果我寫成下面這樣,就沒有預期的效果:

            template: '<h2>登入頁面</h2> <h3>註冊頁面</h3>'

結果如下:(並非預期的效果)

寫法二

寫法二:Vue.component方法定義、註冊元件(一步到位)。

程式碼如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="vue2.5.16.js"></script>
</head>

<body>
    <div id="app">
        <account> </account>
    </div>

    <script>

        //定義、註冊元件:第一個引數是元件的名稱(標籤名),第二個引數是元件的定義
        Vue.component('account', {
            template: '<div><h2>登入頁面</h2> <h3>註冊頁面</h3></div>'   // template 是 Vue 中的關鍵字,不能改。
        });

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

</html>

程式碼截圖如下:

上圖中,同樣注意兩點:

1、紅框部分,要保證二者的名字是一致的。

2、綠框部分,一定要用一個大的根元素(例如<div>)包裹起來。如果我寫成下面這樣,就沒有預期的效果:

            template: '<h2>登入頁面</h2> <h3>註冊頁面</h3>'

結果如下:(並非預期的效果)

寫法三

上面的寫法一、寫法二並不是很智慧,因為在定義模板的時候,沒有智慧提示和高亮,容易出錯。我們不妨來看看寫法三。

寫法三:將元件內容定義到template標籤中去。

程式碼如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="vue2.5.16.js"></script>
</head>

<body>
    <!-- 定義模板 -->
    <template id="myAccount">
        <div>
            <h2>登入頁面</h2>
            <h3>註冊頁面</h3>
        </div>
    </template>

    <div id="app">
        <!-- 使用元件 -->
        <account> </account>
    </div>

    <script>

        //定義、註冊元件
        Vue.component('account', {
            template: '#myAccount'    // template 是 Vue 中的關鍵字,不能改。
        });

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

</html>

程式碼截圖如下:

寫法三其實和方法二差不多,無非是把綠框部分的內容,單獨放在了<template>標籤中而已,這樣有利於 html 標籤的書寫。

使用components定義私有元件

我們在上一段中定義的是全域性元件,這樣做的時候,多個Vue例項都可以使用這個元件。

我們還可以在一個Vue例項的內部定義私有元件,這樣做的時候,只有當前這個Vue例項才可以使用這個元件。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="vue2.5.16.js"></script>
</head>

<body>

    <div id="app">
        <!-- 使用Vue例項內部的私有元件 -->
        <my-login></my-login>
    </div>

    <script>

        new Vue({
            el: '#app',
            data: {},
            components: { // 定義、註冊Vue例項內部的私有元件
                myLogin: {
                    template: '<h3>這是私有的login元件</h3>'
                }
            }


        });
    </script>
</body>

</html>

執行效果:

當然,我們還可以把模板的定義存放在<template>標籤中,這樣的話,模板裡的html標籤就可以出現智慧提示和高亮,避免出錯。如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="vue2.5.16.js"></script>
</head>

<body>

    <!-- 定義模板 -->
    <template id="loginTmp">
        <h3>這是私有的login元件</h3>
    </template>

    <div id="app">
        <!-- 呼叫Vue例項內部的私有元件 -->
        <my-login></my-login>
    </div>

    <script>
        new Vue({
            el: '#app',
            data: {},
            components: { // 定義、註冊Vue例項內部的私有元件
                myLogin: {
                    template: '#loginTmp'
                }
            }
        });
    </script>
</body>

</html>

執行效果不變。

為元件新增 data 和 methods

既然元件是一個頁面,那麼,頁面中可能會有一些功能要動態展示。因此,我們有必要為元件新增 data 和 methods。

程式碼舉例如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="vue2.5.16.js"></script>
</head>

<body>
    <!-- 定義元件的模板 -->
    <template id="myAccount">
        <div>
            <!-- 在元件的模板中,呼叫本元件中的data -->
            {{myData}}
            <a href="#" v-on:click="login">登入1</a>
            <h2>登入頁面</h2>
            <h3>註冊頁面</h3>

        </div>
    </template>

    <div id="app">
        <!-- 第一次呼叫元件 -->
        <account> </account>
        <!-- 第二次呼叫元件 -->
        <account> </account>
    </div>

    <script>

        //定義、註冊元件
        Vue.component('account', {
            template: '#myAccount',
            //元件中的 data
            //【注意】元件中的data,不再是物件,而是一個方法(否則報錯);而且這個方法內部,還必須返回一個物件才行
            // 元件中 的data 資料,使用方式,和例項中的 data 使用方式完全一樣!!!
            data: function () {
                return {
                    myData: 'smyhvae'
                }
            },
            //元件中的 method
            methods: {
                login: function () {
                    alert('login操作');
                }
            }
        });

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

</html>

上方程式碼所示,我們在account元件中新增的data 和 methods,其作用域只限於account元件裡,保證獨立性。

注意,在為元件新增資料時,data不再是物件了,而是function,而且要通過 return的形式進行返回;否則,頁面上是無法看到效果的。通過 function返回物件的形式來定義data,作用是:

  • 上方程式碼中,元件<account>被呼叫了兩次(不像根元件那樣只能呼叫一次),但是每個元件裡的資料 myData是各自獨立的,不產生衝突。

  • 換而言之,通過函式返回物件的目的,是為了讓每個元件都有自己獨立的資料儲存,而不應該共享一套資料。

為什麼元件的data必須是一個function

我們先來看下面這樣的例子:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="vue2.5.16.js"></script>
</head>

<body>
    <div id="app">
        <!-- 第一次呼叫元件 -->
        <counter></counter>
        <hr>

        <!-- 第二次呼叫元件 -->
        <counter></counter>
    </div>

    <!-- 定義模板 -->
    <template id="tmpl">
        <div>
            <input type="button" value="讓count加1" @click="increment">
            <h3>{{count}}</h3>
        </div>
    </template>

    <script>
        var dataObj = { count: 0 }

        // 這是一個計數器的元件, 身上有個按鈕,每當點選按鈕,讓 data 中的 count 值 +1
        Vue.component('counter', {
            template: '#tmpl',
            data: function () {
                return dataObj //當我們return全域性的dataObj的時候,子元件們會共享這個dataObj
            },
            methods: {
                increment() {
                    this.count++
                }
            }
        })

        // 建立 Vue 例項,得到 ViewModel
        var vm = new Vue({
            el: '#app',
            data: {},
            methods: {}
        });
    </script>
</body>

</html>

執行效果如下:

上面的例子中,將元件<counter>呼叫了兩次,由於dataObj全域性物件,導致兩個元件例項都可以共享這個dataObj資料。於是,我們點選任何一個元件例項的按鈕,都可以讓count資料加1。

現在問題來了,如果我們想讓元件<counter>的兩個例項去單獨操作count資料,應該怎麼做呢?我們應該修改 data中 return出去的內容:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="vue2.5.16.js"></script>
</head>

<body>
    <div id="app">
        <counter></counter>
        <hr>
        <counter></counter>
        <hr>
        <counter></counter>
    </div>

    <template id="tmpl">
        <div>
            <input type="button" value="讓count加1" @click="increment">
            <h3>{{count}}</h3>
        </div>
    </template>

    <script>
        var dataObj = { count: 0 }

        // 這是一個計數器的元件, 身上有個按鈕,每當點選按鈕,讓 data 中的 count 值 +1
        Vue.component('counter', {
            template: '#tmpl',
            data: function () {
                // return dataObj //當我們return全域性的dataObj的時候,這個dataObj是共享的
                return { count: 0 } // 【重要】return一個**新開闢**的物件資料
            },
            methods: {
                increment() {
                    this.count++
                }
            }
        })

        // 建立 Vue 例項,得到 ViewModel
        var vm = new Vue({
            el: '#app',
            data: {},
            methods: {}
        });
    </script>
</body>

</html>

執行效果:

如上圖所示,每當我們建立一個新的元件例項時,就會呼叫data函式,data函式裡會return一個新開闢的物件資料。這樣做,就可以保證每個元件例項有獨立的資料儲存

元件的切換

使用v-if和v-else結合flag進行切換

程式碼舉例:(登入元件/註冊元件,二選一)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="vue2.5.16.js"></script>
</head>

<body>
    <div id="app">
        <!-- 溫馨提示:`.prevent`可以阻止超連結的預設事件 -->
        <a href="" @click.prevent="flag=true">登入</a>
        <a href="" @click.prevent="flag=false">註冊</a>

        <!-- 登入元件/註冊元件,同時只顯示一個 -->
        <login v-if="flag"></login>
        <register v-else="flag"></register>

    </div>

    <script>
        Vue.component('login', {
            template: '<h3>登入元件</h3>'
        })

        Vue.component('register', {
            template: '<h3>註冊元件</h3>'
        })

        // 建立 Vue 例項,得到 ViewModel
        var vm = new Vue({
            el: '#app',
            data: {
                flag: false
            },
            methods: {}
        });
    </script>
</body>

</html>

執行效果如下:

使用Vue提供的<component>標籤實現元件切換

上面的例子中,我們是通過flag的值來進行元件的切換。但是,flag的值只可能有兩種情況,也就是說,v-if和v-else只能進行兩個元件之間的切換。

那如何實現三個甚至三個以上的元件切換呢?這裡,我們可以用到Vue提供的<component>標籤。

我們先來看一下<component>標籤的用法。

基於上面的程式碼,如果我想讓login元件顯示出來,藉助<component>標籤可以這樣做:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="Vue2.5.16.js"></script>
</head>

<body>
    <div id="app">

        <!-- Vue提供了 component ,來展示對應名稱的元件 -->
        <!-- 【重要】component 是一個佔位符, `:is` 屬性,可以用來指定要展示的元件名稱。這裡,我們讓 login 元件顯示出來 -->
        <component :is="'login'"></component>

    </div>

    <script>
        // 元件名稱是 字串
        Vue.component('login', {
            template: '<h3>登入元件</h3>'
        })

        Vue.component('register', {
            template: '<h3>註冊元件</h3>'
        })

        // 建立 Vue 例項,得到 ViewModel
        var vm = new Vue({
            el: '#app',
            data: {
                comName: 'login' // 當前 component 中的 :is 繫結的元件的名稱
            },
            methods: {}
        });
    </script>
</body>

</html>

上方程式碼中,提取關鍵程式碼如下:

        <component :is="'login'"></component>

如果我想讓register元件顯示出來,藉助<component>標籤可以這樣做:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="Vue2.5.16.js"></script>
</head>

<body>
    <div id="app">

        <!-- Vue提供了 component ,來展示對應名稱的元件 -->
        <!-- 【重要】component 是一個佔位符, `:is` 屬性,可以用來指定要展示的元件名稱 -->
        <component :is="'register'"></component>

    </div>

    <script>
        // 元件名稱是 字串
        Vue.component('login', {
            template: '<h3>登入元件</h3>'
        })

        Vue.component('register', {
            template: '<h3>註冊元件</h3>'
        })

        // 建立 Vue 例項,得到 ViewModel
        var vm = new Vue({
            el: '#app',
            data: {
                comName: 'login' // 當前 component 中的 :is 繫結的元件的名稱
            },
            methods: {}
        });
    </script>
</body>

</html>

上方程式碼中,提取關鍵程式碼如下:

        <component :is="'register'"></component>

因此,如果要實現元件之間的切換,我們可以給<component>標籤裡的is屬性值設定為變數即可,來看看程式碼實現。

實現元件切換的完整程式碼:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="vue2.5.16.js"></script>
</head>

<body>
    <div id="app">
        <!-- 點選按鈕後,設定變數`comName`為不同的值,代表著後面的component裡顯示不同的元件 -->
        <a href="" @click.prevent="comName='login'">登入</a>
        <a href="" @click.prevent="comName='register'">註冊</a>

        <!-- Vue提供了 component ,來展示對應名稱的元件 -->
        <!-- component 是一個佔位符, :is 屬性,可以用來指定要展示的元件的名稱 -->
        <!-- 此處的`comName`是變數,變數值為元件名稱 -->
        <component :is="comName"></component>

    </div>

    <script>
        // 元件名稱是 字串
        Vue.component('login', {
            template: '<h3>登入元件</h3>'
        })

        Vue.component('register', {
            template: '<h3>註冊元件</h3>'
        })

        // 建立 Vue 例項,得到 ViewModel
        var vm = new Vue({
            el: '#app',
            data: {
                comName: 'login' // 當前 component 中的 :is 繫結的元件的名稱
            },
            methods: {}
        });
    </script>
</body>

</html>

效果:

我的公眾號

想學習程式碼之外的軟技能?不妨關注我的微信公眾號:生命團隊(id:vitateam)。

掃一掃,你將發現另一個全新的世界,而這將是一場美麗的意外: