天天看点

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组件基础

继续阅读