Vue組件

1、組件概述

1.什麼是組件?

組件 (Component) 是 Vue.js 最強大的功能之一。組件能夠擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素,Vue.js 的編譯器爲它添加特殊功能。html

全部的 Vue 組件同時也都是 Vue 的實例,因此可接受相同的選項對象 (除了一些根級特有的選項) 並提供相同的生命週期鉤子。 vue

  

2、組件定義

一、組件定義步驟

組件定義2步驟:
a、建立組件構造器
Vue.extend()定義要渲染的HTMLapp

b、註冊組件
Vue.component()定義組件名和構造器函數

 

調用Vue.extend()建立的是一個組件構造器,構造器有一個選項對象,選項對象的template屬性用於定義組件要渲染的HTML; ui

調用Vue.component()註冊組件時,須要提供2個參數:組件的標籤名 和 組件構造器;註冊的組件要掛載到某個Vue實例下,不然它不會生效; this

Vue.extend() 和 Vue.component():因爲 Vue 自己是一個構造函數,Vue.extend() 是一個類繼承方法,它用來建立一個 Vue 的子類並返回其構造函數; 而Vue.component() 的做用在於:創建指定的構造函數與 ID 字符串間的關係,從而讓 Vue.js 能在模板中使用它;直接向 Vue.component() 傳遞 options 時,它會在內部調用 Vue.extend()。 spa

  

 

二、全局組件

全局組件定義2個步驟:
// 1.建立組件構造器
  let Profile = Vue.extend({
// 1.1 模板選項
  template: `
    <div>
    <input type="date">
    <p>今天已是冬天了!</p>
    </div>
    `
 });component

// 2. 註冊一個全局的組件,任何實例均可以使用它
  Vue.component('my-date', Profile);
全局組件能夠直接在每個vue實例中使用。htm

 

三、局部組件

// 1.建立組件構造器
  let Profile = Vue.extend({
// 1.1 模板選項
  template: `
    <div>
    <input type="date">
    <p>今天已是冬天了!</p>
    </div>
    `
  });
//二、註冊到一個指定的Vue實例中,這樣就只能在某一個實例中使用這個組件了
  new Vue({
    el: '#app',
//經過註冊到特定的實例,來達到局部應用,本例中只能應用在id=app的實例中
    components: {
      'my-date': Profile,
    }
    });對象

 注意:

  • 全局組件須要單獨註冊;

  • 局部組件必須註冊到特定的實例中,以components屬性的方式;

 

3、父子組件

// 1. 子組件構造器
        let Child1 = Vue.extend({
            template: `<img src="img/img_01.jpg" width="200">`
        });
let Child2 = Vue.extend({
            template: `<p>我認爲本身很美!</p>`
        });
//若是想單獨使用子組件,必須單獨註冊。這樣就能夠做爲一個單獨的全局組件使用。
Vue.component('child', Child1);

// 2. 父組件構造器
        Vue.component('parent', {
            components: {
                'my-child1': Child1,
                'my-child2': Child2
            },
            template:
                `
                  <div>
                      <my-child1></my-child1>
                      <my-child2></my-child2>
                  </div>
                `
        });
//三、使用
<div id="app">
        <parent></parent>
    //child爲全局組件,能夠單獨使用!
        <child></child>
    </div>

  

4、組件簡化

一、template方式簡化

<body>
    <div id="app">
        <my-div></my-div>
    </div>

    <template id="my_div">
    //template裏面只能有一個根,也就是全部內容都包含在一個div裏面! <div> <div>我是MT!</div> <input type="date"> <img src="img/img_02.jpg" alt="" width="400"> </div> </template> <script src="js/vue.js"></script> <script> // 1. 實例化組件 Vue.component('my-div', { template: '#my_div' }); new Vue({ el: '#app', }); </script> </body>

  

二、script方式簡化

<body>
    <div id="app">
        <my-div></my-div>
    </div>

    <script type="text/x-template" id="my_div">
        <div>
            <img src="img/img_02.jpg" alt="" width="200">
            <p>我是花姑娘!</p>
        </div>
    </script>

    <script src="js/vue.js"></script>
    <script>

        // 1. 實例化組件
        Vue.component('my-div', {
            template: '#my_div'
        });

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

注意事項:

1. 組件掛載數據必須是函數,返回一個對象
 
 Vue.component('my-btn', {
            template: '#my_btn',
            data(){
                return {
                    counter: 0
                }
            }
        });
 

  

 

 組件掛載數據,必須是一個函數,且返回一個對象,這樣子組件被任何對象引用時,data就互不干涉,都是一個獨立的對象,能夠隨時修改,數據都互不影響。 

 二、組件的template中,必須包含在一個根下面

 

<template id="my_div">
    //template裏面只能有一個根,也就是全部內容都包含在一個div裏面!
        <div>
            <div>我是MT!</div>
            <input type="date">
            <img src="img/img_02.jpg" alt="" width="400">
        </div>
    </template>



<template id="my_div">    
       //這是錯誤的,不能有多個根元素!
            <div>我是MT!</div>
            <input type="date">
            <img src="img/img_02.jpg" alt="" width="400">
    </template>

 

  

 

5、組件間通信

一、單層通信

a、父子通訊
<body>
    <div id="app">
    //第三步、父組件給子組件傳值,單層傳值無需動態綁定。父子傳值,讓子組件更活起來!
        <my-div message="今天要下雨" imgsrc="img/img_02.jpg"></my-div>
        <my-div message="明天要下冰雹" imgsrc="img/img_01.jpg"></my-div>
    </div>

    <template id="my_div">
  //第一步:子組件定義接受的值
        <div>
            <h1>{{message}}</h1>
            <img :src="imgsrc" width="200" alt="">
        </div>
    </template>

    <script src="js/vue.js"></script>
    <script>
        // 1. 建立組件,
  //第二步、經過props定義從父組件可以接受的值
        Vue.component('my-div', {
            template: '#my_div',
            props: ['message', 'imgsrc']//注意,要支持駝峯式imgSrc的寫法,須要在template和父組件裏面修改爲img-src或img-Src。
        });

        new Vue({
           el: '#app',
            data: {
               msg: '今天的天氣很好!'
            }
        });
    </script>
</body>

  

二、多層通信

<body>
//四、祖父組件使用和傳值
<div id="app">
    <my-parent :imgtitle="title" :imgsrc="img"></my-parent>
</div>

<template id="my_img">
    <img :src="imgsrc" width="200">
</template>

<template id="my_title">
    <h2>{{title}}</h2>
</template>

//三、父組件定義和傳值
<template id="my_parent">
    <div>
        <child1 :childimg="imgsrc"></child1>
        <child2 :childtitle="imgtitle"></child2>
    </div>
</template>

<script src="js/vue.js"></script>
<script>

    // 1. 子組件的實例
    let Child1 = Vue.extend({
        template: '#my_img',
        props: ['childmyimg']  #父組件傳遞屬性
    });

    let Child2 = Vue.extend({
        template: '#my_title',
        props: ['childtitle']
    });

    // 2. 子組件註冊到父組件
    Vue.component('my-parent', {
        props: ['imgtitle', 'imgsrc'],  #曾祖父傳遞的屬性
        components: {
            'child1': Child1,
            'child2': Child2
        },
        template: '#my_parent'
    });

    new Vue({
        el: '#app',
        data: {
            title: '我是否是很漂亮',
            img: 'img/img_01.jpg'
        }
    });
</script>
</body>

祖父組件=>父組件=>子組件,注意事項:
一、多層傳值必須是動態綁定的方式 :
二、相對子組件必須經過props屬性接受父組件傳過來的數據

 

6、自定義事件

子組件通知給外界,他執行了什麼操做,此時,外界會作出相應的操做。
<body>
    <div id="app">
       <!--父組件經過on監聽total事件,一旦執行total事件,則會修改Vue實例中的數據,這樣就達到了在父組件中屢次調用子組件,數據都是獨立的-->
        <my-btn @total="allcounter()"></my-btn>
        <my-btn @total="allcounter()"></my-btn>
        <my-btn @total="allcounter()"></my-btn>
        <my-btn @total="allcounter()"></my-btn>
        <my-btn @total="allcounter()"></my-btn>
        <my-btn @total="allcounter()"></my-btn>
        <my-btn @total="allcounter()"></my-btn>

        <p>全部按鈕一共點擊了{{totalCounter}}次</p>
    </div>

    <template id="my_btn">
         <button @click="total()">點擊了{{counter}}次</button>
    </template>

    <script src="js/vue.js"></script>
    <script>
//定義子組件
        Vue.component('my-btn', {
            template: '#my_btn',
            data(){
                return { 
                    counter: 0  //注意:在子組件中掛載數據,必須返回對象,這樣在任何地方調用子組件都是獨立的,數據互不干涉。
                }
            },
            methods: {
                total(){
                    this.counter += 1;

                    // 通知外界,我觸發了total方法,而後他就會調用後面的allcounter(),這樣就把全部的點擊次數加起來了
                    this.$emit('total');
                }
            }
        });

        new Vue({
           el: '#app',
            data: {
                totalCounter: 0
            },
            methods: {
               allcounter(){
                   this.totalCounter += 1;
               }
            }
        });
    </script>
</body>

  

 

使用 $on(eventName) 監聽事件

 

使用 $emit(eventName) 觸發事件

 

7、插槽slot

slot的意思是插槽,其目的在於讓組件的可擴展性更強。打個比方說:假如父組件須要在子組件內放一些DOM,那麼這些DOM是顯示、不顯示、在哪一個地方顯示、如何顯示,就是slot分發負責的。

注意:若是使用子組件時,在裏面添加DOM,默認這些Dom是不會顯示的,由於子組件根本不認識。例如:

<child><div>我想在子組件中顯示,可是顯示不出來</div></child>

插槽就像電路板,是用來方便插入內容的。slot分爲匿名插槽和具名插槽。
匿名插槽就像公交車,誰均可以上,任何人均可以使用這個插槽,很相似娛樂圈中的女明星啦;
具名插槽則是私家車,一我的對應一輛車,一個內容對應一個插槽,這個就是生活中的良家婦女啦。

一、匿名插槽

<body>
    <div id="app">
        <my-slot>
            <!--date會覆蓋默認的插槽-->
            <input type="date">
        </my-slot>
    </div>

    <template id="my_slot">
        <div id="panel">
            <h2 class="panel-header">插槽的頭部</h2>
            <!--預留一個插槽-->
            <slot>能夠替換任何標籤,默認顯示提示的內容</slot>
            <footer>插槽的尾部</footer>
        </div>
    </template>

    <script src="js/vue.js"></script>
    <script>
        Vue.component('my-slot', {
            template: '#my_slot'
        });

        new Vue({
           el: '#app',
            data: {
               msg: '今天的天氣很好!'
            }
        });
    </script>
</body>

  

二、具名插槽

<body>
    <div id="app">
        <my-computer>
 		<!--只能根據slot的name插入對應的數據-->
            <div slot="cpu">Inter Core i8</div>
            <img slot="gpu" src="img/img_02.jpg" width="100" alt="">
            <div slot="memory">128G</div>
            <input type="color" slot="hard-drive">
        </my-computer>
    </div>

    <template id="my_computer">
        <div id="main">
            <slot name="cpu">這裏是插cpu的</slot>
            <slot name="gpu">這裏是插gpu的</slot>
            <slot name="memory">這裏是插內存條的</slot>
            <slot name="hard-drive">這裏是插硬盤的</slot>
        </div>
    </template>

    <script src="js/vue.js"></script>
    <script>
        Vue.component('my-computer', {
            template: '#my_computer'
        });

        new Vue({
           el: '#app',
            data: {
               msg: '今天的天氣很好!'
            }
        });
    </script>
</body>

  

 

8、綜合案例

組件以及父子組件傳值

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="app">
    <div>
        <input type="text" v-model="inputValue">
        <button @click="handleClickBtn">添加</button>
    </div>
    <ul>
        <todo-item
                v-for="(item,index) in list"
                :content="item"
                :key="item"
                :index="index"
                :msg="msg"
                @delete="handleParentDelete"
        >
            {{item}}

        </todo-item>
    </ul>
</div>

<script src="js/vue.js"></script>
<script>
    //定義全局組件todoItem
    var todoItem = {
        //子組件接受來自父組件的傳值,能夠直接使用
        props: ['content', 'index', 'msg'],
        template: "<li @click='handleItemDelete'>{{content}}-{{msg}}</li>",
        methods: {
            handleItemDelete: function () {
                //當點擊子組件時,會向外觸發delete事件,同時帶上對應的參數,父組件監聽到delete被觸發後,則會執行對應的方法
                this.$emit('delete', this.index)
            }
        },
    }
    // 1. 建立Vue的實例
    let vm = new Vue({
        el: '#app',
        data: {
            list:[],
            inputValue:'',
            msg:'我是來自父組件的msg'
        },
        components:{
            //全局組件註冊到vm實例中
            todoItem
        },
        methods:{
            handleClickBtn:function(){
                if(this.inputValue){
                    this.list.push(this.inputValue);
                    this.inputValue = '';
                }else{
                    alert('輸入內容不能爲空!');
                }
            },
            handleParentDelete:function(id){
                //$emit傳給父組件的參數,在這裏接受。
                alert(id);
                this.list.splice(id,1);
            },

        }
    });
</script>
</body>
</html>

 VUE的組件其實也是一個Vue實例,她具有vue的全部屬性和方法。

  

9、組件的注意事項

一、is屬性

解決h5的一些小bug

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="app">
    <table>
        <tbody>
            //tbody標籤直接使用row會在h5中有問題,解析後會跳到table外面去
            <row></row>
            <row></row>
            <row></row>
            //修改成:
            <tr is="row"></tr>
            <tr is="row"></tr>
            <tr is="row"></tr>
            //ul標籤裏面須要使用li,select標籤中須要使用option
        </tbody>
    </table>
</div>

    <script src="js/vue.js"></script>
    <script>

        Vue.component('row',{
            template:'<tr><td>this is a row</td></tr>'
        })

        // 1. 建立Vue的實例
        let vm = new Vue({


        });
    </script>
    </body>
    </html>

  

二、子組件的data

 組件中的data必須是一個函數,返回一個對象

 

        data:function(){
                return {
                    number:0
                }
            },        

 

三、ref使用

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <div id="app">
        <counter ref="one" @change="handleChange"></counter>
        <counter ref="two" @change="handleChange"></counter>
        <div>{{total}}</div>
    </div>

    <script src="js/vue.js"></script>
    <script>

        Vue.component('counter',{
            template:'<div @click="handleClick">{{number}}</div>',
            data:function(){
                return {
                    number:0
                }
            },
            methods:{
                handleClick:function(){
                    this.number++
                    this.$emit('change')
                }
            }

        })

        // 1. 建立Vue的實例
        let vm = new Vue({
            el:'#app',
            data:{
                total:0
            },
            methods:{
                handleChange:function () {
                    this.total = this.$refs.one.number + this.$refs.two.number
                }
            }

        });
    </script>
    </body>
    </html>

 

  

四、父子傳值

父組件傳值給子組件,子組件最好不要修改父組件的數據,由於加入父組件傳遞的是一個對象,可能被多個子組件反覆的修改,致使數據混亂。

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
   <div id="app">
       <counter :count='1' @change='handleChange'></counter>
       <counter :count='2' @change='handleChange'></counter>
       <span>{{total}}</span>
   </div>

<script src="js/vue.js"></script>
<script>
    var counter={
      props:['count'],
      template:"<div @click='handleClick'>{{number}}</div>",
      data:function(){
        return {
          //拷貝一個父本出來
          number:this.count
        }
      },
      methods:{
        handleClick:function(){
          //注意:避免直接修改父組件傳過來的數據
          //this.count++

          //每次步長爲1
          this.number += 1
          //每次子組件被點擊後都告知父組件觸發對應的事件
          this.$emit('change',1)
        }
      }
    }
    // 1. 建立Vue的實例
    let vm = new Vue({
        el: '#app',
        components:{
          counter
        },
        data:{
          total:3
        },
        methods:{
          handleChange:function(num){

            this.total+=num
          }
        }

    });
</script>
</body>
</html>

 

五、props和非props特性

 子組件對父組件傳過來的數據進行約束檢測,則爲props特性

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <div id="app">
        <child content="hello world"></child>
    </div>

    <script src="js/vue.js"></script>
    <script>

        Vue.component('child',{
            props:{
                content:{
                    type:String,
                    required:false,
                    default:'default value',
                    validator:function (value) {
                        return (value.length>5)
                    }
                }
            },
            template:'<div>{{content}}</div>',

        })

        // 1. 建立Vue的實例
        let vm = new Vue({
            el:'#app',

        });
    </script>
    </body>
    </html>

  

props特色:

  • 一、父組件傳遞過來
  • 二、子組件必須驗證
  • 三、子組件可使用
  • 四、不會渲染在子組件中

父組件向子組件傳遞數據,可是在子組件沒有明確接受,則不能經過{{}}使用,也稱之爲非props

非props特色:

  • 一、父組件傳遞過來
  • 二、子組件沒明確寫在props裏面接受
  • 三、子組件不能經過{{}}使用
  • 四、會渲染在子組件當中,好比上面的content='hello wold‘會在子組件元素的html屬性中渲染’。

 

六、給組件綁定原生事件

正常狀況下,在父組件中綁定事件,咱們只能在子組件經過this.$emit('events')進行觸發後執行:

<child @click='handleClick'></child>
若是須要在父組件中也生效,須要經過給父組件綁定原生事件:
<child @click.native='handleClick'></child>
完整示例以下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
   <div id="app">
      <child @click.native='handleClick'></child>
   </div>

<script src="js/vue.js"></script>
<script>
    Vue.component('child',{
      template:'<div>Child</div>',

    })
    // 1. 建立Vue的實例
    let vm = new Vue({
        el: '#app',
        methods:{
          handleClick:function(){
            alert('aaa')
          }
        }

    });
</script>
</body>
</html>
相關文章
相關標籤/搜索