7.可复用性的组件详解

2018-10-10  本文已影响56人  sweetBoy_9126

7.1 使用组件的原因

作用:提高代码的复用性

7.2 组件的使用方法

1. 全局注册

Vue.component('my-component',{
  template: `<div>我是一个组件</div>`
})
优点:所有的vue实例都可以用
缺点:权限太大,容错率降低

2. 局部注册

var app = new Vue({
  el: '#app',
  //在app实例里局部注册的组件只能在#app的子级内使用
  components: {
    'app-component':{
      template: `<div>我是app组件的内容</div>`
    }
  }
})

使用:

<my-component></my-component>
  1. vue组件的模板在某些情况下会受到html标签的限制,比如 <table>中只能有<tr> , <td> 这些元素,而我们定义的组件里面是div,所以直接在table中使用组件是无效的,解决办法在table里加上本该有的元素,在这个元素上使用is属性
<!--未使用is-->
<table>
    <app-component></app-component>
</table>

上面代码未使用is属性,组件并未正确的挂载到table下,而且直接挂载到了它的外面


<!--使用了is-->
<table>
  <tr is="app-component"></tr>
</table>

组件正确的挂在到了table下


7.3 组件使用的奇淫技巧

  1. 必须使用小写字母加­-进行命名或者直接小写字母如: child, my­-componnet命名组件
  2. template中的内容必须被一个DOM元素包括 ,也可以嵌套
  3. 在组件的定义中,除了template之外的其他选项-data,computed,methods
  4. data必须是一个方法,然后返回一个对象
    使用组件里的data解决点击两个调用同一方法的按钮数字同时增加的问题
<div id="app">
  <app-component></app-component>
  <app-component></app-component>
</div>
<script>
var app = new Vue({
  el: '#app',
  //在app实例里局部注册的组件只能在#app的子级内使用
  components: {
    'app-component':{
      template: `<button @click="plus">{{count}}</button>`,
      data:function(){
        return {
          count: 0
        }
      },
      methods: {
        plus: function(){
          this.count += 1;
        }
      }
    }
  }
})
</script>

这样按钮里就可以显示不同的值了,不会点击一个同时增加,主要就是因为每调用一次组件都是返回一个新的对象,所以不会互相影响


7.4 使用props传递数据 父亲向儿子传递数据

  1. 在组件中使用props来从父亲组件接收参数,注意,在props中定义的属性,都可以在组件中直接使用
<div id="app">
  <!--在父组件app里给子组件传入一个msg属性,通过在子组件里的props定义msg就可以直接在子组件中使用-->
  <child-component msg="我是来自父组件的内容"></child-component>
</div>
<script>
  var app = new Vue({
    el: '#app',
    components: {
      'child-component':{
        template: '<div>{{msg}}</div>',
        props: ['msg']
      }
    }
  })
</script>
  1. props里面定义的属性来自父级,而组件中data return的数据就是组件自己的数据,两种情况作用域就是组件本身,可以在template,computed,methods中直接使用
  2. props的值有两种,一种是字符串数组,一种是对象
  3. 可以使用v­bind动态绑定父组件来的内容
<div id="app">
  //在app父组件里双向绑定一个dataMessage
  <input type="text" v-model="dataMessage">
  //然后在父级组件中给子组件动态绑定一个model属性,把dataMessage赋给它,所以要在下面的props里定义一个model,这样input里的内容就和子组件双向绑定了
  <vbind-component :model="dataMessage"></vbind-component>
</div>
<script>
  var app = new Vue({
    el: '#app',
    data: {
      dataMessage: '一个信息'
    },
    components: {
      'vbind-component':{
        template: '<div>{{model}}</div>',
        props: ['model']
      }
    }
  })
</script>

另外通过v-bind可以将html的字符串变成js代码,比如:

<div id="app">
  <!--通过v-bind动态绑定所以它里面的是js代码,也就是数组[1,2,3],所以它的长度是3-->
  <vbind-component :model="[1,2,3]"></vbind-component> //3
  <!--通过html的属性绑定,里面的是html的属性值,是字符串"[1,2,3]",所以它的长度是7-->
  <vbind-component model="[1,2,3]"></vbind-component> //7
</div>
<script>
  var app = new Vue({
    el: '#app',
    components: {
      'vbind-component':{
        template: '<div>{{model.length}}</div>',
        props: ['model']
      }
    }
  })
</script>

7.5 单向数据流

一种是父组件传递初始值进来,子组件将它作为初始值保存起来,在自己的作用域下可以随意使用和修改。这种情况可以在组件 data 内再声明一个数据,引用父组件的 prop
步骤一:注册组件
步骤二:将父组件的数据传递进来,并在子组件中用props接收
步骤三:将传递进来的数据通过初始值保存起来

<div id="app">
  <my-count msg="我是父组件传入的内容"></my-count>
</div>
<script>
  var app = new Vue({
    el: '#app',
    //注册组件
    components: {
      'my-count':{
        template: '<div>{{count}}</div>',
        props: ['msg'],
        data: function(){
          return {
            //将传递进来的数据通过初始值保存起来
            count: this.msg
          }
        }
      }
    }
  })
</script>

另一种情况就是 prop 作为需要被转变的原始值传入。这种情况用计算属性就可以了
步骤一:注册组件
步骤二:将父组件的数据传递进来,并在子组件中用props接收
步骤三:将传递进来的数据通过计算属性进行重新计算

<div id="app">
  <input type="text" v-model="width">
  <my-count :width="width"></my-count>
</div>
<script>
  var app = new Vue({
    el: '#app',
    data: {
      width: 0
    },
    components: {
      'my-count':{
        //这里是组件的作用域,组件里面定义的只能在template中用
        template: '<div :style="style"></div>',
        props: ['width'],
        computed: {
          style:{
            get:function(){
              return{
                width: this.width+'px',
                background: 'red',
                height: '20px'
              }
            }
          }
        }
      }
    }
  })
</script>

上面的代码就可以通过在输入框里输入值,动态的更改显示的div的宽度


7.6 数据验证

vue组件中camelCased (驼峰式) 命名与 kebab­case(短横线命名)

  • 在html中, myMessage 和 mymessage 是一致的,,因此在组件中的html中使用必须使用kebab-­case(短横线)命名方式。在html中不允许使用驼峰!!!!!!
  • 在组件中, 父组件给子组件传递数据必须用短横线。在template中,必须使用驼峰命名方式,若为短横线的命名方式。则会直接保错。
  • 在组件的data中,用this.XXX引用时,只能是驼峰命名方式。若为短横线的命名方式,则会报错。

验证的 type 类型可以是:

• String
• Number
• Boolean
• Object
• Array
• Function

Vue.component ( ’ my-compopent ’, {
    props : {
        //必须是数字类型
        propA : Number ,
        //必须是字符串或数字类型
        propB : [String , Number] ,
        //布尔值,如果没有定义,默认值就是 true
        propC: {
            type : Boolean ,
            default : true
        },
        //数字,而且是必传
        propD: {
            type: Number ,
            required : true
        },
        //如果是数组或对象,默认值必须是一个函数来返回
        propE: {
            type : Array ,
            default : function () {
                return [] ;
            }
        },
        //自定义一个验证函数
        propF: {
            validator : function (value) {
                return value > 10;
            }
        }
    }
});

7.7 组件通信

组件关系可分为父子组件通信、兄弟组件通信、跨级组件通信

7.7.1 自定义事件—子组件给父组件传递数据

使用v-­on 除了监昕 DOM 事件外,还可以用于组件之间的自定义事件。
JavaScript 的设计模式 一一观察者模式, dispatchEvent 和 addEventListener这两个方法。 Vue 组件也有与之类似的一套模式,子组件用emit()来触发事件 ,父组件用on()来监听子组件的事件 。
直接甩代码

第一步:自定义事件
第二步: 在子组件中用$emit触发事件,第一个参数是事件名,后边的参数是要传递的数据
第三步:在自定义事件中用一个参数来接受

<div id="app">
  <!--只要是在app里面使用的属性事件什么的作用域都是app-->
  你的银行卡余额是{{total}}
  <money-component @change="handle"></money-component>
</div>
<script>
    var app = new Vue({
        el: '#app',
        data: {
          total: 2000
        },
        methods: {
          //参数value就是你使用$emit传入的数据this.count
          handle: function(value){
            this.total = value
          }
        },
        components:{
          'money-component':{
            //只要是在tmplate里面定义的作用域都是在当前组件里
            template:
              `<div>
                <button @click="addOne">+1000</button>
                <button @click="muniOne">-1000</button>
                </div>
              `,
            data: function(){
              return {
                //这里的count初始值要和total相同,这样数据才能一致
                count: 2000
              }
            },
            methods: {
              addOne: function(){
                this.count += 1000;
                this.$emit('change',this.count)
              },
              muniOne: function(){
                this.count -= 1000;
                this.$emit('change',this.count)
              }
            }  
          }
        }
    })
</script>  

上面的代码就可以通过点击子组件里的按钮同步更改父组件里的数据了

7.7.2 在组件中使用v-­model

$emit的代码,这行代码实际上会触发一个 input事件, ‘input’后的参数就是传递给v-­model绑定的属性的值。
v-­model 其实是一个语法糖,这背后其实做了两个操作:

  1. v­-bind 绑定一个 value 属性
  2. v-­on 指令给当前元素绑定 input 事件

要使用v-­model,要做到:

<div id="app">
  你的银行卡余额是{{total}}
  <!--通过v-model将子组件的数据传给父组件,只需在子组件的方法中触发input事件,
  然后把参数传入参数即可-->

  <!--接受一个value属性-->
  <money-component v-model="total"></money-component>
</div>
<script>
    var app = new Vue({
        el: '#app',
        data: {
          total: 2000
        },
        components:{
          'money-component':{
            template:
              `<div>
                <button @click="addOne">+1000</button>
                <button @click="muniOne">-1000</button>
                </div>
              `,
            data: function(){
              return {
                count: 2000
              }
            },
            methods: {
              addOne: function(){
                this.count += 1000;
                //在有新的 value 时触发 input 事件
                //触发input事件,把this.count传给total
                this.$emit('input',this.count)
              },
              muniOne: function(){
                this.count -= 1000;
                this.$emit('input',this.count)
              }
            }  
          }
        }
    })
</script>  

7.7.3 非父组件之间的通信

官网描述:


图形实例:


<div id="app">
    <a-component></a-component>
    <b-component></b-component> 
</div>
<script>
    var app = new Vue({
        el: '#app',
        data: {
          //中介
          bus: new Vue()
        },
        components: {
          'a-component':{
            template: '<button @click="handle">点击我向b组件传递数据</button>',
            data: function(){
              return {
                a: '我是来自a组件的内容'
              }
            },
            methods: {
              handle: function(){
                this.$root.bus.$emit('vistied',this.a)
              }
            }
          },
          'b-component':{
            template: '<div></div>',
            //b组件在实例创建完成的时候就监听vistied事件
            created: function(){
              this.$root.bus.$on('vistied',function(value){
                alert(value)
              })
            }
            
          }
        }
    })
</script>  

父链:this.$parent

<div id="app">
  {{msg}}
    <a-component></a-component>
</div>
<script>
    var app = new Vue({
        el: '#app',
        data: {
          msg: '我是父组件的数据'
        },
        components: {
          'a-component':{
            template: '<button @click="handle">点击我改变父组件的数据</button>',
            methods: {
              handle: function(){
                this.$parent.msg = '我被子组件改变了' 
              }
            }
          }
        }
    })
</script>  

子链:this.$refs
提供了为子组件提供索引的方法,用特殊的属性ref为其增加一个索引。

  1. 如果在父组件中给子组件使用ref,那么通过this.$refs.a拿到的是子组件的实例对象。
<div id="app">
    <button @click="getChild">我是父组件我要拿到子组件的内容</button>
      -----------{{msg}}
    <a-component ref='a'></a-component>
    <b-component ref='b'></b-component>
</div>
<script>
    var app = new Vue({
        el: '#app',
        data: {
          msg: '未定义'
        },
        methods: {
          getChild: function(){
            //用来拿指定子组件中的内容 ---- $refs
            this.msg=this.$refs.a.msg
          }
        },
        components: {
          'a-component':{
            template: '<div></div>',
            data: function(){
              return {
                msg: '我是a组件的内容'
              }
            }
          },
          'b-component':{
            template: '<div></div>',
            data: function(){
              return {
                msg: '我是b组件的内容'
              }
            }
          }
        }
    })
</script>  
  1. 如果在当前组件中给当前的某个标签元素使用ref,那么this.$refs.a拿到的就是这个元素的dom结构(包括它里面的所有子元素标签)
<template>
  <div class="line" ref="a"></div>
</template>
<script>
export default {
  mounted(){
    console.log(this.$refs.a)
    //<div class="line"></div>
  }
}
</script>

7.8使用slot(插槽)分发内容

7.8.1 编译的作用域

<div id="app">
  <child-component>
    {{ message }}
  </child-component>
</div>

message 应该绑定到父组件的数据,还是绑定到子组件的数据?答案是父组件。

组件作用域简单地说是:
父组件模板的内容在父组件作用域内编译;
子组件模板的内容在子组件作用域内编译。

7.8.2 插槽的用法

父组件的内容与子组件相混合,从而弥补了视图的不足
混合父组件的内容与子组件自己的模板
单个插槽:

<div id="app">
  <one-component>
    <p>我是父组件的内容,插入到了子组件中</p>
  </one-component>
</div>
<script>
    Vue.component('one-component',{
      template: `
      <div>
        <slot>如果没有插入父组件内容,我就默认出现</slot>
      </div>
      `
    })
    var app = new Vue({
        el: '#app',
        data: {}
    })
</script>  

上面的代码中的<p>我是父组件的内容,插入到了子组件中</p>就是通过slot插入子组件的,如果你在全局里面的这个子组件中没有插入任何html,它就默认显示‘如果没有插入父组件内容,我就默认出现’这句话。
具名插槽:

<div id="app">
  <one-component>
    <!--对应下面的name为header的slot,也就是把h1直接插入到了一个class为header的div下-->
    <h1 slot="header">我是标题</h1>
    <p>我是内容一</p>
    <p>我是内容二</p>
    <!--对应name为footer的slot-->
    <div slot="footer">我是页脚</div>
  </one-component>
</div>
<script>
    Vue.component('one-component',{
      template: `
      <div>
        <div class="header">
          <slot name="header"></slot>
        </div>
        <div>
          <slot></slot>
        </div>
        <div class="footer">
          <slot name="footer"></slot>
        </div>
      </div>
      `
    })
    var app = new Vue({
        el: '#app',
        data: {}
    })
</script>  

页面显示dom结构为:


7.8.3 作用域插槽

作用域插槽是一种特殊的slot,使用一个可以复用的模板来替换已经渲染的元素
——从子组件获取数据
====template模板是不会被渲染的

<div id="app">
  <one-component>
    <template slot="abc" slot-scope="prop">
      {{prop.text}}
    </template>
  </one-component>
</div>
<script>
    Vue.component('one-component',{
      template: `
      <div>
        <slot text="我是一段文字" name="abc"></slot>
      </div>
      `
    })
    var app = new Vue({
        el: '#app',
        data: {}
    })
</script>  

7.8.4 访问slot

通过this.$slots.(NAME)

<div id="app">
  <one-component>
    <h1 slot="header">我是标题</h1>
  </one-component>
</div>
<script>
    Vue.component('one-component',{
      template: `
      <div>
        <div class="header">
          <slot name="header"></slot>
        </div>
      </div>
      `,
      mounted:function(){
        var slotHeader = this.$slots.header;
        var text = slotHeader[0].elm.innerText;
        console.log(text)
      }
    })
    var app = new Vue({
        el: '#app',
        data: {}
    })
</script>  

7.9 组件高级用法–动态组件

VUE给我们提供 了一个元素叫component
作用是: 用来动态的挂载不同的组件
实现:使用is特性来进行实现的

<div id="app">
  <component :is="thisComponet"></component>
  <button @click="changeComponent('a')">第一句</button>
  <button @click="changeComponent('b')">第二句</button>
  <button @click="changeComponent('c')">第三句</button>
  <button @click="changeComponent('d')">第四句</button>
</div>
<script>
    Vue.component('coma',{
      template: '<div>记得曾来过</div>'
    })
    Vue.component('comb',{
      template: '<div>悲伤留心坐</div>'
    })
    Vue.component('comc',{
      template: '<div>望眼笔走锋</div>'
    })
    Vue.component('comd',{
      template: '<div>竟是一场空</div>'
    })
    var app = new Vue({
        el: '#app',
        data: {
          thisComponet: 'coma'
        },
        methods: {
          changeComponent: function(component){
            this.thisComponet = 'com' + component
          }
        }
    })
</script>  

上面的代码可以实现点击不同按钮,显示不同组件里的内容

上一篇 下一篇

猜你喜欢

热点阅读