Vue2.x學習五:組件

Component組件是Vue.js最強大的功能之一,它能夠擴展HTML元素,封裝可重用的代碼(減小代碼冗餘)。
在較高層面上,組件是自定義元素。好比說,咱們本身發明了標籤,別人不承認,可是vue可以幫咱們去解析,幫咱們把它轉換成你們都能承認的。javascript

註冊

咱們開發組件的時候,須要告訴vue,咱們建立了一個新的組件,這裏咱們就須要註冊。
註冊分爲局部註冊和全局註冊。html

全局註冊

就是說咱們能夠註冊一個組件,把它掛載到vue的全局變量上。
它裏面有個屬性叫Vue.component(組件名稱,組件定義)
咱們能夠簡單的來測試一下:vue

<body>
    <div class="container">
        <!-- 引用自定義組件 -->
        <sf-line></sf-line>
        <!-- 若是咱們要寫5個這樣的組件,那麼咱們能夠用v-for -->
        <sf-line v-for="n in 5"></sf-line>
        <!-- 若是咱們想要在sf-line標籤中直接插入值,能夠用slot插槽 -->
        <sf-line>你好</sf-line>
    </div>
    <script>
        // 在建立vue對象以前,註冊組件
        // 經過全局註冊Vue.component(組件名稱,組件定義),調用組件
        // 組件名稱是自定義的
        // 組件定義template(模板),封裝html標籤,它裏面是多個html的綜合體,以反引號包裹html內容,(反引號的好處是能夠任意回車,不用作字符串的拼接)
        Vue.component('sf-line',{
            template:`
                <div style="background-color:orange;border-radius:3px;padding:.5em 1em;margin:.5em">
                    <span>hello</span>
                    <!-- slot 插槽,能夠用來接受sf-line中的值-->
                    <span><slot></slot></span>
                </div>
            `
        });

        //建立vue的實例
        new Vue({
            el:'.container',
            data:{  }
        });
    </script>
</body>

這就是一個最簡單的全局註冊的組件。java

如今,咱們不但願將<span>hello</span>中的值寫死,咱們但願可以動態改變span標籤中的值,即外部組件(咱們能夠把.container理解爲一個根組件)向內部組件傳值。此時咱們能夠借用一個屬性props。
props表明屬性,也就是說,外部能夠傳參數,咱們經過props來接收。node

<body>
    <div class="container">
        <!-- 動態綁定屬性 屬性的綁定用v-bind 
            咱們綁定一個在props中定義的屬性名,
            它的屬性值爲vue實例data中,定義的lineText,而且lineText會被看成變量來解析
        -->
        <sf-line v-bind:node="lineText"></sf-line>
        <!-- 簡寫 -->
        <sf-line :node="lineText"></sf-line>
        <!-- 咱們也能夠定義一個屬性,不用v-bind -->
        <sf-line node="組件"></sf-line>
        <!-- 若是咱們這樣寫,lineText會被直接看成一個值打印出來,不會被看成變量解析 -->
        <sf-line node="lineText"></sf-line>
    </div>
    <script>
        //註冊組件
        Vue.component('sf-line',{
            template:`
                <div style="background-color:orange;border-radius:3px;padding:.5em 1em;margin:.5em">
                    <!--接收到props中的值以後,經過雙大括號來取出,雙大括號中的值會被看成變量來解析-->
                    <span>{{node}}</span>
                </div>
            `,
            // 接收參數,假如參數爲node
            props:['node']
        });

        //建立vue的實例
        new Vue({
            el:'.container',
            // 在.container中,咱們調用了一個子組件<sf-line>,如今咱們能夠在vue實例的data中,定義一個值,好比說lineText
            data:{ 
                lineText:'component'
            }
        });
    </script>
</body>

這裏咱們要補充一下外部組件與內部組件傳值的機制,這裏咱們要記住:
組件是徹底獨立的,它有本身的data,methods,鉤子函數等,而且這些內容都包含在組件定義中——Vue.component(組件名稱,組件定義)。函數

<body>
    <div class="container"> 
        <!-- 這是vue實例中的msg -->       
        <p>{{msg}}</p>
        <!-- 這裏顯示組件中的msg -->
        <sf-line></sf-line>
    </div>
    <script>
        // 註冊組件,組件必定要寫在vue實例上面
        Vue.component('sf-line',{
            template:`
                <div>
                    <!-- 組件是徹底獨立的,因此組件中的msg調用的就是組件data中的msg -->
                    <span>{{msg}}</span>
                </div>
            `,
            // 用來接收外部組件向內部組件傳值
            props:[ ],
            // 組件樣式中,一樣有data,methods,鉤子函數等...
            // 組件中的data是一個方法(不是屬性),要有返回值return
            // data方法會返回一個對象,保證它是多實例的
            // 若是像vue實例中data:{}這樣寫,那麼它就是單例的,它會出現一個問題,好比說:組件會應用屢次,若是改變其中一個組件,那麼其餘組件也會變,這就是單例的
            // 咱們但願這個組件是徹底獨立的,因此咱們要return{...},也就是說:咱們每次調用data的時候,都會新建一個對象
            data(){
                return{
                    msg:'這是組件中的msg'
                }
            },
            methods:{

            },
            created(){
                console.log('created');
            }
        });

        //建立vue的實例
        new Vue({
            el:'.container',
            data:{ 
                msg:'這是vue實例中的msg'
            }
        });
    </script>
</body>

瞭解了組件傳值以後,咱們來學習一下如何來給組件綁定事件
咱們根據下面代碼來學習:學習

<body>
    <div class="container">
        <sf-line :node="lineText"></sf-line>
    </div>
    <script>
        Vue.component('sf-line',{
            template:`
                <div style="background-color:orange;border-radius:3px;padding:.5em 1em;margin:.5em;position:relative">
                    <span>{{node}}</span>
                    <!-- 目標:當點擊關閉時,div消失 -->
                    <!-- 第一種方式:在父元素的角度上,將它隱藏 -->
                    <!-- 第二種方式:子元素本身將本身隱藏 -->
                    <!-- 父組件:.container ; 子組件:<sf-line> -->
                    <span style="position:absolute;right:1em;cursor:pointer">關閉</span>
                </div>
            `,
            props:['node'],
            methods:{

            }
        });

        new Vue({
            el:'.container',
            data:{ 
                lineText:'component'
            }
        });
    </script>
</body>

這裏咱們主要講解
第一種方式:在父元素的角度上,將它隱藏測試

<body>
    <div class="container">
        <sf-line :node="lineText"></sf-line>
    </div>
    <script>
        Vue.component('sf-line',{
            template:`
                <!-- 咱們在div中添加一個v-show,將隱藏操做放在這上面,在組件data中定義它是否顯示 -->
                <div 
                    v-show="isShow"
                    style=" background-color:orange;
                            border-radius:3px;
                            padding:.5em 1em;
                            margin:.5em;
                            position:relative"
                >
                    <span>{{node}}</span>
                    <!-- 點擊下面按鈕關閉
                         因爲close是組件內部函數,因此在組件的methods中定義屬性
                     -->
                    <span
                        @click="close" 
                        style=" position:absolute;
                                right:1em;
                                cursor:pointer"
                    >關閉</span>
                </div>
            `,
            props:['node'],
            data(){
                return{
                    // 默認狀況下讓它顯示
                    isShow:true
                }
            },
            methods:{
                close(){
                    // 當點擊close時,讓它隱藏
                    this.isShow=false;
                }
            }
        });

        new Vue({
            el:'.container',
            data:{ 
                lineText:'component'
            },
            methods:{
                
            }
        });
    </script>
</body>

此刻咱們點擊的是外部<sf-line>標籤的事件,是在component裏面,可是外部vue實例中的methods並無定義方法。也就是說,子組件本身偷偷摸摸就沒了,可是父組件根本就不知道。
若是說,父組件這時候想要在子組件被刪除以後,從新刷新整個頁面。即子組件要通知外面的父組件,它點擊了按鈕。那麼咱們如今應該怎麼作?
這裏有一個知識點,子組件向父組件傳遞事件,相似於一種事件傳遞,但此處叫事件發射this

這裏代碼能夠這樣完善:spa

<body>
    <div class="container">
        <!-- 父組件此時要接收子組件發射的自定義事件
             v-on此時監聽的是子組件發射的事件close,
             咱們爲它在父組件中綁定一個方法closeHandler
         -->
        <sf-line :node="lineText" @close="closeHandler"></sf-line>
    </div>
    <script>
        Vue.component('sf-line',{
            template:`
                <!-- 咱們在div中添加一個v-show,將隱藏操做放在這上面,在組件data中定義它是否顯示 -->
                <div 
                    v-show="isShow"
                    style=" background-color:orange;
                            border-radius:3px;
                            padding:.5em 1em;
                            margin:.5em;
                            position:relative"
                >
                    <span>{{node}}</span>
                    <!-- 點擊下面按鈕關閉
                         因爲close是組件內部函數,因此在組件的methods中定義屬性
                     -->
                    <span
                        @click="close" 
                        style=" position:absolute;
                                right:1em;
                                cursor:pointer"
                    >關閉</span>
                </div>
            `,
            props:['node'],
            data(){
                return{
                    // 默認狀況下讓它顯示
                    isShow:true
                }
            },
            methods:{
                close(){
                    // 通知父元素
                    //(子組件向父組件發射emit自定義事件close)
                    this.$emit('close');
                    // 當點擊close時,讓它隱藏
                    this.isShow=false;
                }
            }
        });

        new Vue({
            el:'.container',
            data:{ 
                lineText:'component'
            },
            methods:{
                // 父組件中綁定的方法
                closeHandler(){
                    alert('closeHandler');
                }
            }
        });
    </script>
</body>

局部註冊

局部註冊沒必要將每一個組件都註冊到全局。咱們能夠經過某個Vue實例或組件的實例的選項components註冊在,僅在其做用域中可用的組件上。

<body>
    <div class="container">
        <sf-list :node="msg"></sf-list>
    </div>
    <script type="text/javascript">
        //我聲明的組件
        let sfList={
            template:`
                <div>{{node}}</div>
            `,
            props:['node']
        };

        new Vue({
            el:'.container',
            data:{
                msg:'hello component'
            },
            // 局部註冊
            components:{
                // 能夠直接將組件定義寫在這裏面
                // 可是,爲了避免使代碼顯得臃腫,我在上面聲明一個組件sfList
                // 將上面註冊的組件聲明進來
                // 屬性值:自定義的組件名
                // 屬性值:上面聲明的組件名
                'sf-list':sfList
                /*
                 sfList:sfList                
                 這裏也能夠用駝峯式命名
                */
                /*
                 sfList
                 直接這樣簡寫也是能夠的
                */
                //以上三種方式:<sf-list :node="msg"></sf-list>均可以解析
            }
        });
    </script>
</body>

slot插槽(補充)

插槽定義在組件中,用於接受組件內部的內容。
在全局註冊的第一個例子裏面咱們簡單的用了slot插槽。
這種直接插入,不須要命名的插槽,咱們稱爲匿名插槽
咱們先來簡單回顧一下:

<body>
    <div class="container">
        <!-- 在組件中,用slot接收插入的內容 -->
        <sf-container>hello</sf-container>
    </div>
    <script type="text/javascript">
        //註冊組件
        Vue.component('sf-container',
            template`
                <div class="sf-container">
                    <div class="sf-header">
                        <!-- 用slot接收插入的內容 -->
                        <slot></slot>
                    </div>
                    <div class="sf-content"></div>
                    <div class="sf-footer"></div>
                </div>
            `,
        );
    </script>
</body>

在一個模板中,能夠出現不少插槽。
咱們要去區分它們,咱們能夠給它取個名字,叫作具名插槽

<body>
    <div class="container">
        <sf-container>
            <!-- 此時的屬性值與slot中命名的名字相同 -->
            <div slot="header">header</div>
            <div slot="content">content</div>
            <div slot="footer">footer</div>
        </sf-container>
    </div>
    <script type="text/javascript">
        //註冊組件
        Vue.component('sf-container',
            template`
                <div class="sf-container">
                    <div class="sf-header">
                        <!-- 爲不一樣的插槽取不一樣的名字 -->
                        <slot name="header"></slot>
                    </div>
                    <div class="sf-content">
                        <slot name="content"></slot>
                    </div>
                    <div class="sf-footer">
                        <slot name="footer"></slot>
                    </div>
                </div>
            `,
        );
    </script>
</body>

組件中除了具名插槽外,還有一種插槽,就是做用域插槽

<body>
    <div class="container">
        <!-- 調用一個組件,裏面綁定屬性stuList,將studentList中的值傳給stuList -->
        <sf-list :stulist="studentList">
            <!-- 提供一個模板,它裏面可讓咱們定義td的具體格式 -->
            <!-- slot-scope用於將元素或組件表示爲做用域插槽,此屬性不支持動態綁定 -->
            <!-- 屬性名自定義 -->
            <template slot-scope="stuScope">
                <!-- 在這裏顯示插入的內容 -->
                <!-- 咱們能夠在這裏選擇要插入的數據 -->
                <!-- 做用域插槽,至關於爲咱們多了一個定製的功能 -->
                {{stuScope.foo.id}}
                <a href="#">{{stuScope.foo.name}}</a>
            </template>
        </sf-list>
    </div>
    <script type="text/javascript">
        //註冊組件
        Vue.component('sf-list',{
            template:`
                <div>
                    <p>原來的寫法</p>
                    <ul>
                        <li v-for="item in stulist">{{item.name}}</li>
                    </ul>
                    <p>做用域插槽</p>
                    <ul>
                        <li v-for="item in stulist">
                            <!-- 定義一個插槽,在裏面隨意綁定一個值foo-->
                            <slot :foo="item"></slot>
                        </li>
                    </ul>
                </div>
                
            `,
            // 接收studentList中的內容(外部組件向內部組件傳值)
            // props接收的值不區分大小寫,不要用駝峯式命名,不然可能報錯
            props:['stulist']
        });
        //建立實例
        new Vue({
            el:'.container',
            data:{
                studentList:[{
                    id:1,
                    name:'terry'
                },{
                    id:2,
                    name:'larry'
                },{
                    id:3,
                    name:'tom'
                }]
            }
        });
    </script>
</body>
相關文章
相關標籤/搜索