JS組件系列——又一款MVVM組件:Vue(二:構建自己的Vue組件)


前言:轉眼距離上篇 JS組件系列——又一款MVVM組件:Vue(一:30分鍾搞定前端增刪改查) 已有好幾個月了,今天打算將它撿起來,發現好久不用,Vue相關技術點都生疏不少。經過這幾個月的時間,Vue的發展也是異常迅猛,不過這好像和博主都沒什么太大的關系,博主還是老老實實研究自己的技術吧。技術之路還很長,且行且研究吧。

本文原創地址:http://www.cnblogs.com/landeanfen/p/6518679.html

一、為什么組件很重要

前兩天,看到一篇關於 匯總vue開源項目 的文章,資源非常豐富,不得不感嘆開源社區的強大。隨便點進去看了幾個UI組件,基本都不是原生的html用法,如果你不懂Vue的組件相關概念,看到一些“稀奇古怪”的標簽寫法,可能會使用,但肯定無法理解為什么可以這么寫。比如我們隨便找了一個名叫IView的來看看:

<i-input type="text" :value.sync="formInline.user" placeholder="Username">
     <Icon type="ios-person-outline" slot="prepend"></Icon>
</i-input>

這樣一段代碼就能得到如下效果:

博主好奇心重,打算一探究竟,今天就和大家一起來看一看這些“古怪”寫法的出處。希望通過本文,讓你有一種“哦,原來是這樣,不過如此嘛!”的感覺!

二、Vue里面的組件基礎知識

1、組件的概念

官方定義:組件(Component)是 Vue.js 最強大的功能之一。組件可以擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素, Vue.js 的編譯器為它添加特殊功能。在有些情況下,組件也可以是原生 HTML 元素的形式,以 is 特性擴展。

博主理解:Vue里面的組件可以理解為通過對普通html標簽的封裝,得到一套獨立而且可以通用的html標簽,我們在頁面里面使用這些標簽傳入相應的參數即可調用封裝好的組件。通過下面這張圖相信可以一目了然。

由普通的html標簽form、input、button、label組成了一個新的元素集合,我們命名為i-form,這個i-form就是vue里面組件的概念。我們在頁面里面使用<i-form></i-form>時,通過vue的組件渲染機制,在瀏覽器里面最終就可以顯示成為普通的html標簽form、input、button、label。

2、組件原理

 通過上圖我們知道,vue里面的組件實際上就是一些普通html元素的集合。那么,它是如何將這些自定義標簽轉換為普通html標簽的呢?在介紹組件原理之前,還是先來看一個最簡單的組件實例。

  <div style="text-align:center;margin-top:200px;" id="app">
        <!-- 3. 在Vue實例里面使用組件-->
        <b-component></b-component>
    </div>

    <script src="Content/vue/dist/vue.js"></script>
    <script type="text/javascript">
        // 1.創建組件構造器
        var myComponent = Vue.extend({
            template: '<div id="bComponent">我是自定義組件的內容</div>'
        });

        //2.注冊組件到vue里面
        Vue.component('b-component', myComponent)

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

得到效果:

整個過程不難理解,主要分為三個大的步驟:

  1. 定義一個組件構造器,聲明組件要渲染的html內容
  2. 將組件構造器注冊到Vue的組件系統里面,使其成為Vue的一個組件,給組件取一個名稱,比如b-component
  3. 在Vue的實例里面使用組件。因為上面兩步定義了Vue的組件,既然是Vue的組件,那么要使用組件,首先得有一個Vue的實例,組件必須要在Vue的實例里面使用。

在網上找到一張圖可以清晰地解釋組件的整個渲染過程。

其實有時為了簡便,我們常將1、2步合並,代碼如下:

  <div style="text-align:center;margin-top:200px;" id="app">
        <!-- 2. 在Vue實例里面使用組件-->
        <b-component></b-component>
    </div>

    <script src="Content/vue/dist/vue.js"></script>
    <script type="text/javascript">
        //1.創建組件構造器,注冊組件到vue里面
        Vue.component('b-component', {
            template: '<div id="bComponent">我是自定義組件的內容</div>'
        })

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

得到的結果和上述相同。 

3、組件使用

上述解釋了下組件的定義和原理,關於組件的簡單實用,我們主要介紹以下幾個方面。

(1)組件的作用域

這個應該不難理解,組件分為全局組件和局部組件,也就是說,你可以在頁面上面定義一個全局組件,頁面上面的任何Vue實例都可使用;而對於局部組件,是和具體的Vue實例相關的,只能在當前Vue實例里面使用組件。還有一點需要說明:組件必須在Vue的實例里面使用,在Vue實例之外使用組件無效。通過下面一個例子即可清晰說明它們的區別。

<body>
    <div style="text-align:center;margin-top:50px;" id="app">
        <b-component></b-component>
        <b-component2></b-component2>
    </div>
    <div style="text-align:center;margin-top:50px;" id="app2">
        <b-component></b-component>
        <b-component2></b-component2>
    </div>

    <b-component></b-component>
    <b-component2></b-component2>
   
    <script src="Content/vue/dist/vue.js"></script>
    <script type="text/javascript">

        //定義組件
        Vue.component('b-component', {
            template: '<div id="bComponent">我是全局組件,任何Vue實例都可使用</div>'
        })

        new Vue({
            el: '#app',
            components: {
                'b-component2': {
                    template: '<div id="bComponent">我是局部組件,只能在app這個div里面使用</div>'
                }
            }
        });
        new Vue({
            el: '#app2',
        });
        
    </script>
</body>

得到結果:

(2)組件的傳值

組件實例的作用域是孤立的。這意味着不能並且不應該在子組件的模板內直接引用父組件的數據。可以使用 props 把數據傳給子組件。這段話怎么理解呢?我們先來看幾個例子。

  • 靜態Prop

我們先來看看下面的一段簡單的代碼

<body>
    <div style="text-align:center;margin-top:50px;" id="app">
        <b-component componentmessage="你好"></b-component>
    </div>
   
    <script src="Content/vue/dist/vue.js"></script>
    <script type="text/javascript">
        Vue.component('b-component', {
            template: '<div>{{componentmessage}}</div>',
            props: ['componentmessage'],
        })

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

通過在組件里面使用props屬性,將外部的值傳入組件模板。最終渲染到頁面上面就得到“<div>你好</div>”這么一段html

  • 動態Prop

在多數情況下,我們在使用Vue實例的時候,一般通過data屬性傳入模型,比如

    new Vue({
            el: '#app',
            data: {
                name: 'Jim',
                Age: '28'
            }
        });

這個時候,我們的name和age如何傳到組件實例里面呢?

<body>
    <div style="text-align:center;margin-top:50px;" id="app">
        <b-component v-bind:my-name="name" v-bind:my-age="Age"></b-component>
    </div>
    <script src="Content/vue/dist/vue.js"></script>
    <script type="text/javascript">
        Vue.component('b-component', {
            template: '<div>姓名:{{myName}},年齡:{{myAge}}</div>',
            props: ['myName', 'myAge'],
        })

        new Vue({
            el: '#app',
            data: {
                name: 'Jim',
                Age: '28'
            }
        });
    </script>
</body>

得到結果

需要說明幾點:

  • 在使用標簽<b-component>的時候,通過v-bind命令,將Vue實例里面的name、Age屬性以別名my-name、my-age的形式傳入組件實例。
  • 為什么my-name、my-age傳到組件里面就變成了['myName', 'myAge']呢?這是因為在子組件中定義prop時,使用了camelCase命名法。由於HTML特性不區分大小寫,camelCase的prop用於特性時,需要轉為 kebab-case(短橫線隔開)。
  • 很多情況下,v-bind可以簡寫為冒號(:),所以上述代碼也可以這么寫: <b-component :my-name="name" :my-age="Age"></b-component> 。效果也是一樣。
  • 這里很惡心的還有一點,在Props里面定義的必須要使用所謂“駝峰式”的方式來定義變量,否則會因為一個變量名大小寫搞死你。比如props:["myName"]這樣可以正確,但是如果props:["myname"]這樣的話就錯誤,使用myname取值會是undefined。博主第一次玩這個玩意找了好半天,新手一定注意,大坑,大坑,大坑!慎入!

在封裝組件里面,props屬性使用非常多,更多props用法可參見文檔https://vuefe.cn/v2/guide/components.html#Prop

(3)組件的插槽

在使用組件的時候,我們經常需要在組件實例向組件模板傳入html元素,這個時候我們就需要在組件的模板標簽里面留一些占位符(俗稱“坑”),然后在具體的組件實例里面傳入標簽來填“坑”,在Vue里面這些“坑”也叫插槽,使用<slot>來解決。對於開發人員來說,這個其實不陌生,從原來的母版頁到現在的layout頁面,基本都是使用的這種原理。

<body>
    <div style="text-align:center;margin-top:50px;" id="app">
        <b-component>
            <h1 slot="header">這里可能是一個頁面標題</h1>
            <h2 slot="content">姓名:{{name}},年齡:{{Age}}</h2>
            <h1 slot="footer">尾部</h1>
        </b-component>
    </div>
    <template id="slottest">
        <div class="container">
            <header>
                <slot name="header"></slot>
            </header>
            <main>
                <slot name="content"></slot>
            </main>
            <footer>
                <slot name="footer"></slot>
            </footer>
        </div>
    </template>
   
    <script src="Content/vue/dist/vue.js"></script>
    <script type="text/javascript">

        Vue.component('b-component', {
            template: '#slottest',
        })

        new Vue({
            el: '#app',
            data: {
                name: 'Jim',
                Age: '28'
            }
        });
    </script>
</body>

得到結果

上述代碼應該不難理解,就是一個“挖坑”和“填坑”的過程。順便要提一筆的是,Vue的組件支持使用<templete>的模式來定義標簽模板,使用更加靈活和方便。

三、封裝自己的Component

以上講了這么多,都是關於Vue里面Component組件的一部分主要知識點,其他還有很多都沒有展開說,因為這方面的文檔也是相當豐富,園子里面keepfool的博文關於Vue組件的部分就介紹得非常詳細,再者,Vue中文文檔也是有很詳細的用法說明。接下來,博主打算通過幾個實例來說明使用組件給我們前端開發帶來的好處。

1、使用Component封裝bootstrapTable

對於項目里面的表格展示,可以基於Vue可以自己開發一套,但是說實話,這個工程量還是蠻大的,並且如果要做好,要兼容很多表格的功能,從零開始去重復造輪子實在是有點太耗時。博主項目里面大部分的表格用的bootstrapTable組件,於是博主一直在想能不能封裝一套基於Vue的bootstrapTable的用法。網上也找不到類似的封裝示例,大部分使用vue的框架都會自己去實現一套自己的表格樣式。於是打算自己動手試試,正好也可以熟悉下component的用法。

首先新建一個js文件命名為vue.bootstrapTable.js。博主直接將代碼貼出來,如果有不完善的地方,希望大家斧正。

(function ($) {
    //表格初始化的默認參數
    var defaults = {
        method: 'get',                      
        toolbar: '#toolbar',                
        striped: true,                      
        cache: false,                       
        pagination: true,                   
    };
    //注冊bootstrapTable組件
    Vue.component('bootstrap-table', {
        template: '<table></table>',
        props: {
            'tableParam': { type: Object }
        },
        //組件渲染之前
        created: function () {
            //debugger;

        },
        //組件渲染之后
        mounted: function () {
            debugger;
            var params = $.extend({}, defaults, this.tableParam || {});
            this.bootstraptable = $(this.$el).bootstrapTable(params);
        }
    });

})(jQuery);

然后再界面上面

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
    <link href="Content/bootstrap/css/bootstrap.css" rel="stylesheet" />
    <link href="Content/bootstrap-table/bootstrap-table.css" rel="stylesheet" />
</head>
<body>
    <div id="app">
        <bootstrap-table :table-param="tableParam"></bootstrap-table>
    </div>

    <script src="Content/jquery-1.9.1.min.js"></script>
    <script src="Content/bootstrap/js/bootstrap.js"></script>
    <script src="Content/bootstrap-table/bootstrap-table.js"></script>
    <script src="Content/vue/dist/vue.js"></script>
    <script src="Content/vue-component/vue.bootstrapTable.js"></script>
    <script type="text/javascript">
        var testData = [
      { Name: 'Jim', Age: 30, Remark: '雞母格林' },
      { Name: 'Kate', Age: 28, Remark: '凱特' },
      { Name: 'Lucy', Age: 20, Remark: '露西' },
      { Name: 'Uncle Wang', Age: 45, Remark: '嚴厲的王老師' }
        ];

        new Vue({
            el: '#app',
            data: {
                tableParam: {
                    data: testData,
                    columns: [
                        {
                            field: 'Name',
                            title:'姓名'
                        }, {
                            field: 'Age',
                            title: '年齡'
                        }, {
                            field: 'Remark',
                            title: '備注'
                        }]
                },
            }
        });
        
    </script>
</body>

最后測試結果:

縱觀這數十行代碼,基本原來其實很簡單,通過組件的props功能將<bootstrap-table>實例中的初始化參數傳到組件模板里面,然后再組件加載完成之后初始化bootstrapTable,最后將bootstrapTable的實例給到組件,這樣在就可以通過Vue的實例通過子組件調用到當前初始化的bootstrapTable對象。

2、封裝select

關於select的封裝,還是打算基於第三方組件來做。同樣的,我們新建一個js文件,命名為vue.bootstrapSelect.js,其代碼如下:

(function ($) {
    $("body").append('<template id="bootstrapSelect">' +
        '<select class="selectpicker" v-if="myMultiple" v-bind:data-live-search="mySearch" multiple>' +
            '<option v-for="item in myDatasource" v-bind:value="item.value">{{item.text}}</option>'
        +'</select>' +
        '<select class="selectpicker" v-else v-bind:data-live-search="mySearch">' +
            '<option v-for="item in myDatasource" v-bind:value="item.value">{{item.text}}</option>'
        +'</select>' +
    '</template>');

    Vue.component('bootstrap-select', {
        template: '#bootstrapSelect',
        props: ['myDatasource', 'myMultiple', 'mySearch'],
        //組件渲染之前
        created: function () {
        },
        //組件渲染之后
        mounted: function () {           
        }
    });

})(jQuery);

頁面使用

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
    <link href="Content/bootstrap/css/bootstrap.css" rel="stylesheet" />
    <link href="Content/bootstrap-table/bootstrap-table.css" rel="stylesheet" />
    <link href="Content/bootstrap-select/css/bootstrap-select.css" rel="stylesheet" />
</head>
<body>
    <div id="app">
        <bootstrap-select :my-datasource="selectOptions.data"
                          :my-multiple="selectOptions.multiple" 
                          :my-search="selectOptions.search">
        </bootstrap-select>
    </div>
    <script src="Content/jquery-1.9.1.min.js"></script>
    <script src="Content/bootstrap/js/bootstrap.js"></script>
    <script src="Content/bootstrap-table/bootstrap-table.js"></script>
    <script src="Content/bootstrap-select/js/bootstrap-select.js"></script>
    <script src="Content/bootstrap-select/js/i18n/defaults-zh_CN.js"></script>
    <script src="Content/vue/dist/vue.js"></script>
    <script src="Content/vue-component/vue.bootstrapSelect.js"></script>
    <script type="text/javascript">
        $(function () {
            var vm = new Vue({
                el: '#app',
                data: {
                    selectOptions:{
                        multiple: false,//多選
                        search: true,//搜索
                        data: [
                            { text: "北京市", value: 1 },
                            { text: "上海市", value: 2 },
                            { text: "重慶市", value: 3 },
                        ]
                    }
                },
            });
        });
    </script>
</body>
</html>

得到效果:

然后可配置多選,將初始化參數multiple設置為true即可。

為什么模板里面會有兩個select標簽?原因就在於那個multiple,因為只要標簽里面出現了multiple,select就自動多選,把multiple的值設置為任何屬性都不好使,這不做了一個if判斷,如果哪位有更好的方法,歡迎指出,不勝感激!

3、查看其他Vue框架源碼

現在再來看文章的開頭那段html

<i-input type="text" :value.sync="formInline.user" placeholder="Username">
     <Icon type="ios-person-outline" slot="prepend"></Icon>
</i-input>

結合Vue組件的文檔,其實上述就是一個對input標簽做的封裝。

當然,以上只是component的基礎,組件的封裝還得結合很多其他的東西,要讀懂那些框架的源碼還需要學習一些其他知識,但至少通過本文希望能夠讓你了解這些東西的由來。

四、總結

本篇到此結束,通過本文,相信你對Vue的component有了一個大概的了解。接下來如果有時間將結合webpack介紹Vue的一些高級用法。

最近打算做點自己的東西出來,將博客里面的一些好的技術(包括vue)融合進去。有項目合作的小伙伴趕快聯系博主吧!

本文原創出處:http://www.cnblogs.com/landeanfen/

歡迎各位轉載,但是未經作者本人同意,轉載文章之后必須在文章頁面明顯位置給出作者和原文連接,否則保留追究法律責任的權利


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM