天天看點

Vue - Vue元件基礎

推薦:​​Vue彙總​​

Vue - Vue元件基礎

為了介紹Vue的元件,先用Vue的元件來改造之前實作的​

​TodoList​

​:

  • ​​Vue - 用Vue寫一個TodoList小栗子​​

代碼:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>TodoList</title>
    <script src="./js/vue.js"></script>
  </head>
  <body>
    <div id="div">
      <input v-model.trim="inputValue" type="text">
      <button @click.stop="bntClick">送出</button>
      <ul>
        <todo-item v-for="item in list" v-bind:plan="item.value" :key="item.id"></todo-item>
      </ul>
    </div>
  </body>
</html>
<script>

  Vue.component("todo-item" , {
    props: ['plan'],
    template: "<li>{{plan}}</li>"
  });

  var vue = new Vue({
    el: '#div',
    data: {
      inputValue: '',
      id: 1,
      list: []
    },
    methods: {
      bntClick(){
        if(this.inputValue !== ''){
          this.list.unshift({
            id: this.id++,
            value: this.inputValue
          });
          this.inputValue = '';
        }
        else{
          alert('寫點東西再交');
        }
      }
    }
  })
</script>      

這裡用​

​<todo-item v-for="item in list" v-bind:plan="item.value" :key="item.id"></todo-item>​

​​代替原來​

​TodoList​

​​裡的​

​<li v-for="item in list" :key="item.id">{{item.value}}</li>​

​。

元件全局注冊

Vue.component("todo-item" , {
    props: ['plan'],
    template: "<li>{{plan}}</li>"
  });      

效果和之前的​

​TodoList​

​是一樣的。

元件局部注冊

代碼:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>TodoList</title>
    <script src="./js/vue.js"></script>
  </head>
  <body>
    <div id="div">
      <input v-model.trim="inputValue" type="text">
      <button @click.stop="bntClick">送出</button>
      <ul>
        <todo-item v-for="item in list" v-bind:plan="item.value" :key="item.id"></todo-item>
      </ul>
    </div>
  </body>
</html>
<script>

  var TodoItem = {
    props: ['plan'],
    template: "<li>{{plan}}</li>"
  };

  var vue = new Vue({
    el: '#div',
    components: {
      TodoItem: TodoItem
    },
    data: {
      inputValue: '',
      id: 1,
      list: []
    },
    methods: {
      bntClick(){
        if(this.inputValue !== ''){
          this.list.unshift({
            id: this.id++,
            value: this.inputValue
          });
          this.inputValue = '';
        }
        else{
          alert('寫點東西再交');
        }
      }
    }
  })
</script>      

元件局部注冊:

var TodoItem = {
    props: ['plan'],
    template: "<li>{{plan}}</li>"
  };      

需要使用時(局部作用域),在​

​components​

​中指定即可。

components: {
      TodoItem: TodoItem
    }      

效果也是一樣的。

元件基礎介紹

元件是可複用的Vue執行個體,且帶有一個名字:在上面例子中是​

​<todo-item>​

​。我們可以在一個通過 new Vue建立的Vue根執行個體中,把這個元件作為自定義元素來使用:

<ul>
        <todo-item v-for="item in list" v-bind:plan="item.value" :key="item.id"></todo-item>
      </ul>      

因為元件是可複用的Vue執行個體,是以它們與new Vue接收相同的選項,例如​

​data​

​​、​

​computed​

​​、​

​watch​

​​、​

​methods​

​​ 以及生命周期鈎子等。僅有的例外是像 ​

​el​

​ 這樣根執行個體特有的選項。

元件的複用

你可以将元件進行任意次數的複用:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>TodoList</title>
    <script src="./js/vue.js"></script>
  </head>
  <body>
    <div id="div">
      <input v-model.trim="inputValue" type="text">
      <button @click.stop="bntClick">送出</button>
      <ul>
        <todo-item v-for="item in list" v-bind:plan="item.value" :key="item.id"></todo-item>
        <todo-item v-for="item in list" v-bind:plan="item.id" :key="item.id"></todo-item>
      </ul>
    </div>
  </body>
</html>
<script>
  var TodoItem = {
    props: ['plan'],
    template: "<li>{{plan}}</li>"
  };

  var vue = new Vue({
    el: '#div',
    components: {
      TodoItem: TodoItem
    },
    data: {
      inputValue: '',
      id: 1,
      list: []
    },
    methods: {
      bntClick(){
        if(this.inputValue !== ''){
          this.list.unshift({
            id: this.id++,
            value: this.inputValue
          });
          this.inputValue = '';
        }
        else{
          alert('寫點東西再交');
        }
      }
    }
  })
</script>      

效果:

Vue - Vue元件基礎

每個元件都會各自獨立維護它的​

​plan​

​​。因為你每用一次元件,就會有一個它的新執行個體被建立(上面例子中被建立了6次,通過​

​v-for​

​)。

data 必須是一個函數

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>count</title>
    <script src="./js/vue.js"></script>
  </head>
  <body>
    <div id="div">
      <count></count>
      <count></count>
      <count></count>
    </div>
  </body>
</html>
<script>
  Vue.component('count',{
    data: function () {
      return {
        cnt: 1
      }
    },
    template: '<button @click="cnt++">點選{{cnt}}</button>'
  });
  var vue = new Vue({
    el: '#div'
  })
</script>      

效果:

這樣點選一個元件,不會影響其他元件,因為元件的​

​data​

​選項是一個函數,是以每個執行個體可以維護一份被傳回對象的獨立的拷貝。

Vue - Vue元件基礎

如果​

​data​

​​不是一個函數呢?我的Vue版本(​

​v2.6.10​

​)是用不了的,和官網有點不同。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>count</title>
    <script src="./js/vue.js"></script>
  </head>
  <body>
    <div id="div">
      <count></count>
      <count></count>
      <count></count>
    </div>
  </body>
</html>
<script>
  Vue.component('count',{
    data:{
      cnt: 1
    },
    template: '<button @click="cnt++">點選{{cnt}}</button>'
  });
  var vue = new Vue({
    el: '#div'
  })
</script>      

效果:

沒有數字。

Vue - Vue元件基礎

在官網文檔中是可以使用的,可能是因為版本不同,官網文檔大概的意思是:如果​

​data​

​​不是一個函數,元件複用就可能會存在問題,因為所有執行個體的​

​data​

​​實際上是指向同一個​

​data​

​​,某一個執行個體改變了​

​data​

​​的值,其他執行個體的​

​data​

​同樣也就改變了。

Vue - Vue元件基礎

元件的組織通常一個應用會以一棵嵌套的元件樹的形式來組織:

Vue - Vue元件基礎

例如,你可能會有頁頭、側邊欄、内容區等元件,每個元件又包含了其它的像導航連結之類的元件。

為了能在模闆中使用,這些元件必須先注冊,以便Vue能夠識别。有兩種元件注冊方式:全局注冊和局部注冊,在前面也都介紹過了。全局注冊的元件可以用在其被注冊之後的任何 (通過new Vue) 新建立的Vue根執行個體,也包括其元件樹中的所有子元件的模闆中。

通過 Prop 向子元件傳遞資料

在第一個用Vue元件改造​

​TodoList​

​​的例子中就使用過​

​Prop​

​​向子元件傳遞資料,​

​Prop​

​​是你可以在元件上注冊的一些自定義​

​attribute​

​​。當一個值傳遞給一個​

​prop attribute​

​​的時候,它就變成了那個元件執行個體的一個​

​property​

​​。為了給​

​todo-item​

​​元件傳遞​

​plan​

​​,我們可以用一個​

​props​

​​選項将其包含在該元件可接受的 ​

​prop​

​清單中:

Vue.component("todo-item" , {
    props: ['plan'],
    template: "<li>{{plan}}</li>"
  });      

一個元件預設可以擁有任意數量的 ​

​prop​

​​,任何值都可以傳遞給任何 ​

​prop​

​​。在上述模闆中,你會發現我們能夠在元件執行個體中通路這個值,就像通路​

​data​

​中的值一樣。

例子:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>TodoList</title>
  <script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
  <input v-model.trim="inputValue" type="text">
  <button @click.stop="bntClick">送出</button>
  <ul>
    <todo-item v-for="item in list" :plan="item.value" :key="item.id"></todo-item>
    <todo-item v-for="item in list" plan="+1" :key="item.id"></todo-item>
  </ul>
</div>
</body>
</html>
<script>

  Vue.component("todo-item" , {
    props: ['plan'],
    template: "<li>{{plan}}</li>"
  });

  var vue = new Vue({
    el: '#div',
    data: {
      inputValue: '',
      id: 1,
      list: []
    },
    methods: {
      bntClick(){
        if(this.inputValue !== ''){
          this.list.unshift({
            id: this.id++,
            value: this.inputValue
          });
          this.inputValue = '';
        }
        else{
          alert('寫點東西再交');
        }
      }
    }
  })
</script>      

效果:

通過​

​v-bind​

​可以傳遞動态的值,通過變量名直接傳遞靜态的值。

<todo-item v-for="item in list" :plan="item.value" :key="item.id"></todo-item>
    <todo-item v-for="item in list" plan="+1" :key="item.id"></todo-item>      
Vue - Vue元件基礎

監聽子元件事件

代碼:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>count</title>
  <script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
  <count :count="count" v-on:bnt-click="bntClick"></count>
</div>
</body>
</html>
<script>

  Vue.component("count" , {
    props: ['count'],
    template: `
            <button v-on:click="$emit('bnt-click')">{{count}}</button>
        `
  });

  var vue = new Vue({
    el: '#div',
    data: {
      count: 1
    },
    methods: {
      bntClick(){
        this.count++;
      }
    }
  })
</script>      

效果:

Vue - Vue元件基礎

當點選這個按鈕時,我們需要告訴父級元件将​

​count​

​​加一。Vue執行個體提供了一個自定義事件的系統來解決這個問題。父級元件可以像處理native DOM(如​

​click​

​​)事件一樣通過 ​

​v-on​

​ 監聽子元件執行個體的任意事件:

<count :count="count" v-on:bnt-click="bntClick"></count>      

同時子元件可以通過調用内建的 ​

​$emit​

​ 方法并傳入事件名稱來觸發一個事件:

Vue.component("count" , {
    props: ['count'],
    template: `
            <button v-on:click="$emit('bnt-click')">{{count}}</button>
        `
  });      

事件名稱要用​

​bnt-click​

​​這種格式,不要用​

​bntClick​

​這種格式,不然就不起作用(我這個版本是這樣的)。

使用事件抛出一個值

代碼:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>count</title>
  <script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
  <count :count="count" v-on:bnt-click="bntClick"></count>
</div>
</body>
</html>
<script>

  Vue.component("count" , {
    props: ['count'],
    template: `
            <button v-on:click="$emit('bnt-click' , 5)">{{count}}</button>
        `
  });

  var vue = new Vue({
    el: '#div',
    data: {
      count: 1
    },
    methods: {
      bntClick(value){
        this.count += value;
      }
    }
  })
</script>      

點四下:

Vue - Vue元件基礎

當在父級元件監聽這個事件的時候,我們可以通過​

​$event​

​​通路到被抛出的這個值。

代碼:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>count</title>
  <script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
  <count :count="count" v-on:bnt-click="count += $event"></count>
</div>
</body>
</html>
<script>

  Vue.component("count" , {
    props: ['count'],
    template: `
            <button v-on:click="$emit('bnt-click' , 5)">{{count}}</button>
        `
  });

  var vue = new Vue({
    el: '#div',
    data: {
      count: 1
    }
  })
</script>      

效果是一樣的。

在元件上使用 v-model

自定義事件也可以用于建立支援 ​

​v-model​

​ 的自定義輸入元件。記住:

<input v-model="searchText">      

等價于:

<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>      

當用在元件上時,​

​v-model​

​ 則會這樣:

<custom-input
  v-bind:value="searchText"
  v-on:input="searchText = $event"
></custom-input>      

為了讓它正常工作,這個元件内的 ​

​<input>​

​ 必須:

  • 将其 ​

    ​value attribute​

    ​​ 綁定到一個名叫 ​

    ​value​

    ​​ 的 ​

    ​prop​

    ​ 上
  • 在其 ​

    ​input​

    ​​ 事件被觸發時,将新的值通過自定義的 ​

    ​input​

    ​ 事件抛出。

寫成代碼之後是這樣的:

Vue.component('custom-input', {
  props: ['value'],
  template: `
    <input
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
  `
})      

現在 ​

​v-model​

​ 就應該可以在這個元件上完美地工作起來了:

<custom-input v-model="searchText"></custom-input>      

通過插槽分發内容

和HTML元素一樣,我們經常需要向一個元件傳遞内容,Vue自定義的 ​

​<slot>​

​ 元素讓這變得非常簡單:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>count</title>
  <script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
  <slot-demo>kaven</slot-demo>
</div>
</body>
</html>
<script>

  Vue.component("slot-demo" , {
    template: `
           <div>
              <p>welcome </p>
              <slot></slot>
           </div>
        `
  });

  var vue = new Vue({
    el: '#div'
  })
</script>      

效果:

Vue - Vue元件基礎

動态元件

有的時候,在不同元件之間進行動态切換是非常有用的。

可以通過 Vue的 ​

​<component>​

​​ 元素加一個特殊的 ​

​is attribute​

​ 來實作:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>count</title>
  <script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
  <component v-bind:is="currentComponent">kaven</component>
  <button @click="click">切換</button>
</div>
</body>
</html>
<script>

  Vue.component("slot-welcome" , {
    template: `
           <div>
              <p>welcome </p>
              <slot></slot>
           </div>
        `
  });

  Vue.component("slot-hello" , {
    template: `
           <div>
              <p>hello </p>
              <slot></slot>
           </div>
        `
  });

  var vue = new Vue({
    el: '#div',
    data: {
      currentComponent: 'slot-hello'
    },
    methods: {
      click(){
        if(this.currentComponent === 'slot-hello'){
          this.currentComponent = 'slot-welcome'
        }
        else{
          this.currentComponent = 'slot-hello'
        }
      }
    }
  })
</script>      

效果:

Vue - Vue元件基礎

點選按鈕。

Vue - Vue元件基礎

繼續閱讀