第十一课时: 关于Vue组件一些注意的地方

组件的基本使用

注册组件

1.注册组件就是利用Vue.component()方法
2.可以理解为一个vue文件就是一个组件

模板要求

组件的模板中只能有一个根元素

组件中的data必须是函数

var app = new Vue({
    data () {
      return {
        message: 'hello world'
      }
    }
})

组件的属性和事件

例,一个父组件:

<my-component v-bind:foo="baz" v-on:event-a="doThis(arg1,...arg2)"></my-component>

上面代码

  • foo<my-component>组件内部定义的一个prop属性,baz是父组件的一个data属性,
  • event-a是子组件定义的一个事件,doThis是父组件的一个方法

执行过程应该是这样的:

  • 父组件把baz数据通过prop传递给子组件的foo
  • 子组件内部得到foo的值,就可以进行相应的操作;
  • 当子组件内部发生了一些变化,希望父组件能知道时,就利用代码触发event-a事件,把一些数据发送出去
  • 父组件把这个事件处理器绑定为doThis方法,子组件发送的数据,就作为doThis方法的参数被传进来
  • 然后父组件就可以根据这些数据,进行相应的操作

属性Props

Vue组件通过props属性来声明一个自己的属性,然后父组件就可以往里面传递数据。

Vue.component('mycomponent',{
    template: '<div>这是一个自定义组件,父组件传给我的内容是:{{myMessage}}</div>',
    props: ['myMessage'],
    data () {
      return {
        message: 'hello world'
      }
    }
  })

然后调用该组件

<div id="app">
    <mycomponent my-message="hello"></mycomponent>
</div>

注意,由于HTML特性是不区分大小写的,所以传递属性值时,myMessage应该转换成 kebab-case (短横线隔开式)my-message="hello"

v-bind绑定属性值

<div attr="message">hello</div>

上面这样,div元素的attr特性值就是message。而这样

<div v-bind:attr="message">hello</div>

这里的message应该是Vue实例的data的一个属性,这样div元素的attr特性值就是message这个属性的值。
之所以说是一般情况,是因为classstyle特性并不是这样。用v-bind:classclass传入正常的类名,效果是一样的,因为对于这两个特性,Vue采用了合并而不是替换的原则。

子组件希望对传入的prop进行操作

根据上面,想要把父组件的属性绑定到子组件,应该使用v-bind,这样,父组件中数据改变时能反映到子组件。
注意 在子组件中最好不要更改父组件传入的值,如果非要更改的话,加上.sync修饰符

例父组件

<my-component :child-array.sync="parentArray"></my-component>

子组件

methods: {
     changeArray () {
       this.counter++
       this.$emit('update:childArray', this.counter)
     }
}

给子组件传递正确类型的值

同样是上面的原因,静态的给子组件的特性传递值,它都会把他当做一个字符串。

<!-- 传递了一个字符串 "1" -->
<comp some-prop="1"></comp>

子组件中,特性的值是字符串 “1” 而不是 number 1。如果想传递正确的数值,应该使用v-bind传递,这样就能把传递的值当做一个表达式来处理,而不是字符串。

<!-- 传递实际的 number 1 -->
<comp v-bind:some-prop="1"></comp>

Prop验证

我们可以给组件的props属性添加验证,当传入的数据不符合要求时,Vue会发出警告。

Vue.component('example', {
  props: {
    // 基础类型检测 (`null` 意思是任何类型都可以)
    propA: Number,
    // 多种类型
    propB: [String, Number],
    // 必传且是字符串
    propC: {
      type: String,
      required: true
    },
    // 数字,有默认值
    propD: {
      type: Number,
      default: 100
    },
    // 数组/对象的默认值应当由一个工厂函数返回
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
  }
})

type 可以是下面原生构造器:

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

type 也可以是一个自定义构造器函数

自定义事件

通过prop属性,父组件可以向子组件传递数据,而自定义事件就是用来将子组件的内部数据报告给父组件的。

<div id="app3">
    <my-component2 v-on:myclick="onClick"></my-component2>
</div>
<script>
  Vue.component('my-component2', {
    template: `<div>
    <button type="button" @click="childClick">点击我触发自定义事件</button>
    </div>`,
    methods: {
      childClick () {
        this.$emit('myclick', '这是我暴露出去的数据', '这是我暴露出去的数据2')
      }
    }
  })
  new Vue({
    el: '#app3',
    methods: {
      onClick () {
        console.log(arguments)
      }
    }
  })
</script>

如上所示,共分为以下步骤:

  1. 子组件在自己的方法中将自定义事件以及需要发出的数据通过以下代码发送出去

    this.$emit('myclick', '这是我暴露出去的数据', '这是我暴露出去的数据2')
    第一个参数是自定义事件的名字
    后面的参数是依次想要发送出去的数据
    
  2. 父组件利用v-on为事件绑定处理器

    <my-component2 v-on:myclick="onClick"></my-component2>

    这样,在Vue实例的methods方法中就可以调用传进来的参数了

注意: 在使用v-on绑定事件处理方法时,不应该传进任何参数,而是直接写v-on:myclick="onClick",不然,子组件暴露出来的数据就无法获取到了

绑定原生事件

如果想在某个组件的根元素上监听一个原生事件。可以使用 .native 修饰 v-on

探究v-model

v-model可以对表单控件实现数据的双向绑定,它的原理就是利用了绑定属性和事件来实现的。比如input控件。不使用v-model,可以这样实现数据的双向绑定:

<div id="app4">
    <input type="text" v-bind:value="text" v-on:input="changeValue($event.target.value)">
    {{text}}
</div>
<script>
  new Vue({
    el: '#app4',
    data: {
      text: '444'
    },
    methods: {
      changeValue (value) {
        this.text = value
      }
    }
  })
</script>

上面的代码同样实现了数据的双向绑定。其本质就是:

  • inputvalue特性绑定到Vue实例的属性text上,text改变,input中的内容也会改变
  • 然后把表单的input事件处理函数设置为Vue实例的一个方法,这个方法会根据输入参数改变Vue中text的值
  • 相应的,在input中输入内容时,触发了input事件,把event.target.value传给这个方法,最后就实现了改变绑定的数据的效果。

v-model就是上面这种方式的语法糖,也就是把上面的写法封装了一下,方便我们使用。

使用slot分发内容

单个slot

上面用到的很多组件的使用方式是这样的:

<component></component>

也就是说组件中是空的,没有放置任何文本或元素。但是原生的html元素都是可以进行嵌套的,div里面放table什么的。自定义组件开闭标签之间也可以放置内容,不过需要在定义组件时使用slot

slot相当于子组件设置了一个地方,如果在调用它的时候,往它的开闭标签之间放了东西,那么它就把这些东西放到slot中。

  1. 当子组件中没有slot时,父组件放在子组件标签内的东西将被丢弃;
  2. 子组件的slot标签内可以放置内容,当父组件没有放置内容在子组件标签内时,slot中的内容会渲染出来;
  3. 当父组件在子组件标签内放置了内容时,slot中的内容被丢弃

子组件的模板:

<div>
  <h2>我是子组件的标题</h2>
  <slot>
    只有在没有要分发的内容时才会显示。
  </slot>
</div>

父组件模板:

<div>
  <h1>我是父组件的标题</h1>
  <my-component>
    <p>这是一些初始内容</p>
  </my-component>
</div>

渲染结果:

<div>
  <h1>我是父组件的标题</h1>
  <div>
    <h2>我是子组件的标题</h2>
    <p>这是一些初始内容</p>
  </div>
</div>

具名slot

slot可以有很多个。那么子组件对于父组件放置的多余的内容如何放到各个slot中呢?方法就是子组件给每个slot起一个名字name,父组件放置多余的元素时,给每个元素的slot属性分配一个代表slot的名字。到时候,多余的内容就会根据自己的slot属性去找具有对应名字的slot元素。

注意:

  1. 子组件可以有一个匿名的slot,当分发的多余内容找不到对应的slot时,就会进入这里面
  2. 如果子组件没有匿名的slot,当分发的多余内容找不到对应的slot时,就会被丢弃

例如,假定我们有一个 app-layout 组件,它的模板为:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

父组件模版:

<app-layout>
  <h1 slot="header">这里可能是一个页面标题</h1>
  <p>主要内容的一个段落。</p>
  <p>另一个主要段落。</p>
  <p slot="footer">这里有一些联系信息</p>
</app-layout>

渲染结果为:

<div class="container">
  <header>
    <h1>这里可能是一个页面标题</h1>
  </header>
  <main>
    <p>主要内容的一个段落。</p>
    <p>另一个主要段落。</p>
  </main>
  <footer>
    <p>这里有一些联系信息</p>
  </footer>
</div>

作用域插槽

作用域插槽也是一个插槽slot,但是他可以把数据传递给到父组件的特定元素内,然后有父组件决定如何渲染这些数据。

  1. 首先,子组件的slot需要有一些特性(prop)

    Vue.component('my-component4', {
         template: `<div>
           <slot :text="hello" message="world"></slot>
         </div>`,
         data () {
           return {
             hello: [1,'2']
           }
         }
       })
  2. 父组件在调用子组件时,需要在里面添加一个template元素,并且这个template元素具有scope特性

    <div id="app7">
      <my-component4>
        <template scope="props">
        </template>
      </my-component4>
     </div>

    scope特性的值,就代表了所有子组件传过来的数据组成的对象。相当于

    props = {
        text: '',
       message: ''
    }
  3. 最后,父组件就可以在template中渲染子组件传过来的数据了

    <div id="app7">
       <my-component4>
         <template slot-scope="props">
           <span>{{props.text}}</span>
           <span>{{props.message}}</span>
         </template>
       </my-component4>
      </div>

    作用域插槽也是插槽,只不过是多加了些特性,然后父组件多进行了些处理。

    原文作者:岳_轻风
    原文地址: https://segmentfault.com/a/1190000019127927
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞