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

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

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

1、爲何組件很重要

前兩天,看到一篇關於 彙總vue開源項目 的文章,資源很是豐富,不得不感嘆開源社區的強大。隨便點進去看了幾個UI組件,基本都不是原生的html用法,若是你不懂Vue的組件相關概念,看到一些「稀奇古怪」的標籤寫法,可能會使用,但確定沒法理解爲何能夠這麼寫。好比咱們隨便找了一個名叫IView的來看看:html

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

這樣一段代碼就能獲得以下效果:前端

博主好奇心重,打算一探究竟,今天就和你們一塊兒來看一看這些「古怪」寫法的出處。但願經過本文,讓你有一種「哦,原來是這樣,不過如此嘛!」的感受!vue

2、Vue裏面的組件基礎知識

一、組件的概念

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

博主理解:Vue裏面的組件能夠理解爲經過對普通html標籤的封裝,獲得一套獨立並且能夠通用的html標籤,咱們在頁面裏面使用這些標籤傳入相應的參數便可調用封裝好的組件。經過下面這張圖相信能夠一目瞭然。jquery

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

二、組件原理

 經過上圖咱們知道,vue裏面的組件實際上就是一些普通html元素的集合。那麼,它是如何將這些自定義標籤轉換爲普通html標籤的呢?在介紹組件原理以前,仍是先來看一個最簡單的組件實例。ios

  <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>

獲得效果:web

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

  1. 定義一個組件構造器,聲明組件要渲染的html內容
  2. 將組件構造器註冊到Vue的組件系統裏面,使其成爲Vue的一個組件,給組件取一個名稱,好比b-component
  3. 在Vue的實例裏面使用組件。由於上面兩步定義了Vue的組件,既然是Vue的組件,那麼要使用組件,首先得有一個Vue的實例,組件必需要在Vue的實例裏面使用。

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

其實有時爲了簡便,咱們常將一、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>

獲得的結果和上述相同。 

三、組件使用

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

(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>的模式來定義標籤模板,使用更加靈活和方便。

3、封裝本身的Component

以上講了這麼多,都是關於Vue裏面Component組件的一部分主要知識點,其餘還有不少都沒有展開說,由於這方面的文檔也是至關豐富,園子裏面keepfool的博文關於Vue組件的部分就介紹得很是詳細,再者,Vue中文文檔也是有很詳細的用法說明。接下來,博主打算經過幾個實例來講明使用組件給咱們前端開發帶來的好處。

一、使用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對象。

二、封裝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判斷,若是哪位有更好的方法,歡迎指出,不勝感激!

三、查看其餘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的基礎,組件的封裝還得結合不少其餘的東西,要讀懂那些框架的源碼還須要學習一些其餘知識,但至少經過本文但願可以讓你瞭解這些東西的由來。

4、總結

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

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

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

歡迎各位轉載,可是未經做者本人贊成,轉載文章以後必須在文章頁面明顯位置給出做者和原文鏈接,不然保留追究法律責任的權利

相關文章
相關標籤/搜索