從零開始學習Vue(三)


 我們從一個例子來學習組件,vuejs2.0實戰:仿豆瓣app項目,創建自定義組件tabbar

這個例子用到其他組件,對於初學者來說,一下子要了解那么多組件的使用,會變得一頭霧水。所以我把這個例子改寫了一下,只需要依賴Vue.

然后最好FQ安裝一個chrome的擴展 vue-devtools,這樣可以更好看到組件的內容

 

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

在較高層面上,組件是自定義元素, Vue.js 的編譯器為它添加特殊功能。在有些情況下,組件也可以是原生 HTML 元素的形式,以 is 特性擴展。 

父子組件的關系可以總結為 props down, events up 。父組件通過 props 向下傳遞數據給子組件,子組件通過 events 給父組件發送消息。

 

SLOT的理解: 子組件有名字的slot,將會給父組件同名的slot替換掉, 子組件匿名的slot, 將會給父組件的其他沒slot名字的內容替換掉.

             <m-tabbar-item id='tab1'>                
                <img src="../assets/images/ic_tab_home_normal.png" alt="" slot="icon-normal"> 
                <img src="../assets/images/ic_tab_home_active.png" alt="" slot="icon-active"> 
                首頁
              </m-tabbar-item>

子組件模板:

            <a class="m-tabbar-item" :class="{'is-active':isActive}" @click="$parent.$emit('input',id)">
                <span class="m-tabbar-item-icon" v-show="!isActive"><slot name="icon-normal"></slot></span>
                <span class="m-tabbar-item-icon" v-show="isActive"><slot name="icon-active"></slot></span>
                <span class="m-tabbar-item-text"><slot></slot></span>
            </a>

 最終生成的HTML:

<a class="m-tabbar-item">
<span class="m-tabbar-item-icon">
<img src="../assets/images/ic_tab_home_normal.png" alt=""></span>
<span class="m-tabbar-item-icon" style="display: none;">
<img src="../assets/images/ic_tab_home_active.png" alt=""></span>
<span class="m-tabbar-item-text">
首頁
</span>
</a>
<img src="../assets/images/ic_tab_home_normal.png" alt="" slot="icon-normal"> 會替換掉 <slot name="icon-normal"></slot>
<img src="../assets/images/ic_tab_home_active.png" alt="" slot="icon-active"/> 會替換掉 <slot name="icon-active"></slot>,並把slot的名字去掉
首頁  會替換掉  <slot></slot> 這個匿名的.


整個頁面的代碼如下:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <meta content="width=device-width,initial-scale=1,user-scalable=no" name="viewport" />
    <script src="Scripts/vue.js" type="text/javascript"></script>
    <style type="text/css">
        .m-tabbar
        {
            display: flex;
            flex-direction: row;
            position: fixed;
            bottom: 0;
            left: 0;
            right: 0;
            width: 100%;
            overflow: hidden;
            height: 50px;
            background: #fff;
            border-top: 1px solid #e4e4e4;
        }
        .m-tabbar-item
        {
            flex: 1;
            text-align: center;
        }
        .m-tabbar-item-icon
        {
            display: block;
            padding-top: 2px;
        }
        .m-tabbar-item-icon img
        {
            width: 28px;
            height: 28px;
        }
        .m-tabbar-item-text
        {
            display: block;
            font-size: 10px;
            color: #949494;
        }
        .is-active
        {
            color: #42bd56;
        }
    </style>
</head>
<body>
    <div id="app">
        <div>
            <m-tabbar v-model="select">
             <m-tabbar-item id='tab1'>
                
                <img src="../assets/images/ic_tab_home_normal.png" alt="" slot="icon-normal"> 
                <img src="../assets/images/ic_tab_home_active.png" alt="" slot="icon-active"> 
                首頁
              </m-tabbar-item>
              <m-tabbar-item id='tab2'>
                <img src="../assets/images/ic_tab_subject_normal.png" alt="" slot="icon-normal"> 
                <img src="../assets/images/ic_tab_subject_active.png" alt="" slot="icon-active"> 
                書影音
            </m-tabbar-item>
              <m-tabbar-item id='tab3'>
                <img src="../assets/images/ic_tab_status_normal.png" alt="" slot="icon-normal"> 
                <img src="../assets/images/ic_tab_status_active.png" alt="" slot="icon-active"> 
                廣播
              </m-tabbar-item>
              <m-tabbar-item id='tab4'>
                <img src="../assets/images/ic_tab_group_normal.png" alt="" slot="icon-normal"> 
                <img src="../assets/images/ic_tab_group_active.png" alt="" slot="icon-active"> 
                小組
              </m-tabbar-item>
               <m-tabbar-item id='tab5'>
                <img src="../assets/images/ic_tab_profile_normal.png" alt="" slot="icon-normal"> 
                <img src="../assets/images/ic_tab_profile_active.png" alt="" slot="icon-active"> 
                我的
              </m-tabbar-item>
          </m-tabbar>
        </div>
    </div>
    <script type="text/javascript">
        Vue.component("m-tabbar", {
            props: ['value'],
            template:'<div class="m-tabbar"><slot></slot></div>'
        });

        Vue.component("m-tabbar-item", {
            props: ['id'],
            computed: {
               isActive(){
                   if(this.$parent.value===this.id){
                       return true;
                   }
                   else
                        return false;
               }
            },
            template:'<a class="m-tabbar-item" :class="{\'is-active\':isActive}" @click="$parent.$emit(\'input\',id)">'
                +'<span class="m-tabbar-item-icon" v-show="!isActive"><slot name="icon-normal"></slot></span>'
                +'<span class="m-tabbar-item-icon" v-show="isActive"><slot name="icon-active"></slot></span>'
                + '<span class="m-tabbar-item-text"><slot></slot></span>'
                +'</a>'
        });
        var app = new Vue({
            el: '#app',
            data: { 
                select:"tab1"
            }
        });
    </script>
</body>
</html>
m-tabbar組件的雙向綁定 v-model="select" 說明m-tabbar暴露出的屬性value是和data里的select字段關聯的,頁面默認選中的是第一個tab,它的Id是tab1.
m-tabbar-item組件里有一個判斷 this.$parent.value===this.id,父組件的value等於子組件的Id,則該子組件為選中的。
@click="$parent.$emit(\'input\',id)" 這個方法, 請參考這里 https://cn.vuejs.org/v2/guide/components.html#使用自定義事件的表單輸入組件
<m-tabbar v-model="select">等價於 <m-tabbar v-bind:value="select" v-on:input="select=arguments[0]">
所以要讓組件的 v-model 生效,它必須:
接受一個 value 屬性 在有新的 value 時觸發 input 事件

所以@click="$parent.$emit(\'input\',id)" 這個方法就是在子組件click的事件,觸發父組件的input事件把子組件的Id傳個父組件的value屬性

 

問題來了:為什么父組件有個input事件呢? 它只是個div而已,我可以把這個事件名字改成其他的嗎?

改成下面的代碼,把事件名改成abcdef,是可以通過的
<m-tabbar v-bind:value="selectId" v-on:abcdef="tabSelect">
    <script type="text/javascript">
        Vue.component("m-tabbar", {
            props: ['value'],
            template:'<div class="m-tabbar"><slot></slot></div>',
            mounted: function () {
                console.log("m-tabbar mounted");
            }
        });

        Vue.component("m-tabbar-item", {
            props: ['id'],
            mounted: function () {
                console.log("m-tabbar-item mounted");
            },
            computed: {
               isActive(){
                   if(this.$parent.value===this.id){
                       return true;
                   }
                   else
                        return false;
               }
            },
            template:'<a class="m-tabbar-item" :class="{\'is-active\':isActive}" @click="$parent.$emit(\'abcdef\',id)">'
                +'<span class="m-tabbar-item-icon" v-show="!isActive"><slot name="icon-normal"></slot></span>'
                +'<span class="m-tabbar-item-icon" v-show="isActive"><slot name="icon-active"></slot></span>'
                + '<span class="m-tabbar-item-text"><slot></slot></span>'
                +'</a>'
        });
        var app = new Vue({
            el: '#app',
            data: { 
                selectId:"tab1"
            },
            methods:{
                tabSelect:function(Id){
                    console.log("tabSelect");
                    this.selectId= Id;
                }
            }
        });
    </script>

是任意名字都可以嗎? 不是的,我試過有2個是不行的。 當事件名里有包含Select或者Index這2個詞時,父組件就監聽不到這個事件了。 難道這2個是什么保留字, 有人知道嗎?




免責聲明!

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



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