深入理解vue元件

胡志武98發表於2019-05-13

一、元件中的細節點

is的使用

html語法中,table標籤內就必須是tr標籤,tr標籤下就必須是td標籤,ul標籤下就必須是li標籤 等等 標籤間的巢狀規則

在運用元件的時候,會出現想在這個標籤內使用元件,但這個標籤內並不支援的情況,如下

<div id="app">
      <table>
        <tbody>
        	<tr>
            	<td>this  is a row</td>
            </tr>
            <tr>
            	<td>this  is a row</td>
            </tr>
            <tr>
            	<td>this  is a row</td>
            </tr>
        </tbody>
      </table>
    </div>

複製程式碼

我們看到tr標籤這一部分重複了幾次,我們可以試著用元件的方式,寫出它,如下

<div id="app">
      <table>
        <tbody>
          <row></row>
          <row></row>
          <row></row>
        </tbody>
      </table>
    </div>

    <script>
      Vue.component("row", {
          template:"<tr><td>this is a row</td></tr>"
      });
      var app = new Vue({
        el: "#app"
      });
    </script>
複製程式碼

然後,我們開啟瀏覽器測試下,

1557638536778

雖然展示結果一樣,但我們可以看待tr這部分標籤出現在了table標籤外部,這就是table標籤不支援內部巢狀不是tr標籤的情況

這時候,我們就可以使用is了

<table>
    <tbody>
    	<tr is="row"></tr>//採用table內支援的標籤,is後面接元件的名字
    </tbody>
</table>
複製程式碼

這裡就不貼圖了,這時,table標籤的巢狀就正常了

元件中data的使用

在子元件中,data必須是一個函式,並且該函式要返回一個物件

這是因為子元件會重複使用,而他們之間需要有獨立資料儲存記憶體,這樣資料才不會互相影響,如果在元件中data是一個物件,那麼,所有元件的資料都指向同一塊記憶體。

需要操作dom例項時,$refs出現了

如果你需要在父元件中,直接訪問子元件的例項

可以通過ref給子元件加個id

ref="one"

然後父元件中,這樣訪問

this.$refs.one

現在我們用ref來做個小例子

錄製_2019_05_12_14_03_45_881

當我們點選上面的兩個小數,會自增,最下的數字是兩個小數是總回

子元件程式碼

Vue.component("count",{
        data:function(){
            return {
                number:0,
            }
        },
        template:"<div @click='add'>{{number}}</div>",
        methods:{
            add:function(){
                this.number++;
                this.$emit("change")//當被點選時,會觸發父元件的change事件
            }
        }
    })
複製程式碼

html程式碼

<div id="app">
        <count ref="one" @change="allNum"></count>//用ref給子元件設定一個id,one
        <count ref="two" @change="allNum"></count>
        <div>{{all}}</div>
    </div>
複製程式碼

父元件程式碼

var app = new Vue({
        el:"#app",
        data:{
            all:0
        },
        methods:{
            allNum:function(){
                this.all=this.$refs.one.number+this.$refs.two.number;
                //在父元件中,可以通過this.$refs.ref來獲取子元件的例項,
            }
        }
    })
複製程式碼

二、父子元件間的傳值

父元件想要給子元件傳值,必須在呼叫子元件的html上寫上要傳值的屬性名和值,如下

<count :count="3"></count>這裡要注意:屬性名後面接的都是一個js表示式,所以這裡的3是一個數值型別

而在子元件中,要接受父元件的傳值,則必須在propps:[]中加一個同樣的屬性名,props:["count"]

重點,在這裡要注意vue的單向資料流概念,就是父元件可以給子元件傳值,但子元件不能隨意直接修改父元件傳過來的值,必須先將父元件傳過來的值複製一份,放在子元件的data中後,然後對data中的值進行修改

如下

Vue.component("count",{
    props:['count'],
    data:function(){
        return {
            number:this.count
        }
    }
})
複製程式碼

如上,只能對父元件傳過來的值的副本進行操作,這是因為,傳進的是基礎資料型別還好,如果父元件傳遞的是一個引用型的資料,那麼子元件直接改動這個資料,就有可能對其他引用該資料的元件造成干擾

子元件給父元件傳值,要藉助$emit去觸發父元件自定義的事件,

this.$emit("change",1);change是父元件自定義的事件,而1,就是我們要傳遞的值了,

現在我們來利用父子元件的傳值,做一個簡單的計數器

<div id="app">
        <count :count="one" @change="allNum"></count>
    //@change是父元件自定義的事件,觸發了change//就會去執行allNum函式
        <count :count="two" @change="allNum"></count>
    //:count 是要傳遞的屬性名,two是要傳遞的值,在data中,two對應0;
        {{all}}
    </div>
    <script>
    var count = {
        props:['count'],//用count來接受父元件傳遞過來的值
        data:function(){
            return {
                number:this.count,//記得單項資料流,要將父元件的傳值,複製一份,放在子元件data中
            }
        },
        template:'<div @click="add">{{number}}</div>',
        methods:{
            add:function(){
                this.number++;
                this.$emit("change",1);//觸發change事件,傳值是1;
            }
        }
    }
    var app = new Vue({
        el:"#app",
        data:{
            all:0,
            one:0,
            two:0
        },
        components:{
            count:count,
        },
        methods:{
            allNum:function(value){//value來接受$emit的傳值
                this.all+=value;
            }
        }
    })
複製程式碼

效果如下:

錄製_2019_05_12_21_11_30_550

三、元件引數校驗與非Props特性

元件引數校驗

當父元件給子元件傳值時,我們希望對該值進行約束,如下

props:{
    content:{
        type:String,//限制傳過來的值必須是字串,
        required:false,//是否必須傳遞該值
        default:'default value',//預設值
        validator:function(value){//
            return (value.length>5);//該值的長度必須大於5
        }
    }
}
複製程式碼

props特性

父元件給子元件傳值時,切好子元件的props中有對應的prop進行接收,那麼這就是props特性,,父元件給子元件傳遞的值,不會顯示在html中,

向相反,非props特性,則會顯示在html中

四、給元件繫結原生事件

父元件在子元件上繫結了一個事件,想要觸發該事件,只能通過觸發自定義事件的方式去觸發,或者在加上事件修飾符.native

先看第一種,不加修飾符,不觸發自定義事件

<div id="app">
        <child @click="onClick"></child>
    </div>
    <script>
    Vue.component('child',{
        template:'<div>child</div>',
    })
    var app = new Vue({
        el:"#app",
        methods:{
            onClick:function(){
                alert('click');
            }
        }
    })
    </script>
複製程式碼

在瀏覽器中,我們發現,點選child並沒有任何效果,

錄製_2019_05_13_16_15_36_19

這就是想要觸發父元件在子元件上繫結的事件,只能通過觸發自定義事件的方式去觸發,現在改程式碼如下:

<div id="app">
        <child @click="onClick"></child>
    </div>
    <script>
    Vue.component('child',{
        template:'<div @click="childClick">child</div>',
        methods:{
            childClick:function(){
                this.$emit("click");//觸發父元件的自定義事件,click
            }
        }
    })
    var app = new Vue({
        el:"#app",
        methods:{
            onClick:function(){
                alert('click');
            }
        }
    })
    </script>
複製程式碼

錄製_2019_05_13_16_18_18_610

或者給加上一個事件修飾符.native也可以達到相同的效果

<child @click.native="onClikc"></child>
複製程式碼

五、非父子元件間的傳值

這裡先介紹一種匯流排的模式。

<div id="app">
        <child content="胡"></child>
        <child content="志武"></child>
    </div>
    <script>
        Vue.prototype.bus = new Vue();
        Vue.component("child",{
            props:['content'],
            data:function(){
                return {
                    name:this.content,
                }
            },
            template:'<div @click="exchange">{{name}}</div>',
            methods:{
                exchange:function(){
                    this.bus.$emit("change",this.name)
                }
            },
            //元件被掛載時,
            mounted:function(){
                var that = this;
                this.bus.$on("change",function(msg){
                    that.name=msg;
                })
            }
        })
        var app = new Vue({
            el:"#app",
        })
    </script>
複製程式碼

我們在Vueprototype上掛載了一個bus,這個bus是一個vue的例項,子元件要互相傳值時,就可以用這個共同的祖先bus來監聽和觸發相應的事件,並藉助這個共同的祖先來傳值

1557737928725

六、使用插槽

slot可以讓我們在元件的某個位置插入想要的內容

 Vue.component("child",{
            template:`<div>
                        <slot name="header">預設內容</slot>
                        <div>content</div>
                        <slot name="footer">預設內容</slot>
                      </div>`
        })V
複製程式碼
<div id="app">
        <child>
            <div slot="header">我是頭部</div>
            <div slot="footer">我是尾部</div>
        </child>
    </div>
複製程式碼

如果在插槽位置沒有插入內容,則會顯示預設內容

作用域插槽

當子元件在做遍歷,而希望這個遍歷的dom結構由外部決定時,可以使用作用域插槽

<div id="app">
        <child >
            <template slot-scope="props">//template是必須寫的
                <h1>{{props.item}}</h1>//props可以任意寫,而item必須和template中的`:item`一致
            </template>
        </child>
    </div>
    <script>
        Vue.component("child",{
            data:function(){
                return {
                    list:[1,2,3,4,5]
                }
            },
            template:`
                    <ul>
                        <slot v-for="item of list"
                            :item=item>//這裡的:item是要傳值給html實際插槽部分的
                        </slot>
                    </ul>
            `
        })
        new Vue({
            el:"#app",
        })
    </script>
複製程式碼

動態元件和v-once

<component>是vue自帶標籤,

<component :is="元件名">component會根據is後面元件名的不同而動態載入不同的元件

v-once可以把元件放在記憶體中,可以有效提高效能

template:`<div v-once>child ONE</div>`
複製程式碼

結語

因為本人水平有限,如果有錯漏的地方,還請看官多多指正

本文作者胡志武,寫於2019/5/13,如果要轉載,請註明出處,

如果覺得寫的不錯, 請點個贊吧

相關文章