vue组件之事件

自定义事件

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

<div id="app3">
    <my-component v-on:myclick="onClick"></my-component>
</div>
<script>
  Vue.component('my-component', {
    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

点击按钮 控制台打印出以下
vue

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

子组件在本身的方法中将自定义事件以及须要发出的数据经过如下代码发送出去缓存

this.$emit('myclick', '这是我暴露出去的数据', '这是我暴露出去的数据2')

第一个参数是自定义事件的名字
后面的参数是依次想要发送出去的数据
父组件利用v-on为事件绑定处理器app

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

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

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

绑定原生事件

若是想在某个组件的根元素上监听一个原生事件。可使用 .native 修饰 v-onthis

<my-component v-on:click.native="doTheThing"></my-component>
探究v-model

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

<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>

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

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

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

使用自定义事件建立自定义的表单输入组件

理解了v-model的内幕,也就能够把这个效果用在自定义表单组件上了。
一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,可是像单选框、复选框等类型的输入控件可能会将 value 特性用于不一样的目的。model 选项能够用来避免这样的冲突:

<div id="app5">
        <base-checkbox v-model="lovingVue"></base-checkbox>
        <div>{{lovingVue}}</div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script>
        Vue.component('base-checkbox', {
            model: {
                prop: 'checked',
                event: 'change'
                },
            props: {
                checked: Boolean
            },
            template: `<input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change', $event.target.checked)"> `
        })
        var vm = new Vue({
            el: "#app5",
            data: {
                lovingVue: false
            }
        })
    </script>

这里的 lovingVue 的值将会传入这个名为 checked 的 prop。同时当 触发一个 change 事件并附带一个新的值的时候,这个 lovingVue 的属性将会被更新。

注意 你仍然须要在组件的 props 选项里声明 checked 这个 prop。

动态组件

经过使用保留的 元素,动态地绑定到它的 is 特性,可让多个组件使用同一个挂载点,并动态切换

<div id="app6">
                <select v-model="currentComponent">
                  <option value="home">home</option>
                  <option value="posts">post</option>
                  <option value="archive">about</option>
                </select>
                <component :is="currentComponent"></component>
              </div>
              <script src="https://cdn.jsdelivr.net/npm/vue"></script>
              <script>
                  new Vue({
                    el: '#app6',
                    data: {
                      currentComponent: 'home'
                    },
                    components: {
                      home: {
                        template: `<header>这是home组件</header>`
                      },
                      posts: {
                        template: `<header>这是posts组件</header>`
                      },
                      archive: {
                        template: `<header>这是archive组件</header>`
                      }
                    }
                  })
            </script>
保留切换出去的组件,避免从新渲染

若是把切换出去的组件保留在内存中,能够保留它的状态或避免从新渲染。为此能够添加一个 keep-alive 指令参数:

<!-- 失活的组件将会被缓存!-->
<keep-alive>
  <component :is="currentComponent">
   
  </component>
</keep-alive>
插槽
单个slot

上面用到的不少组件的使用方式是这样的:

<component></component>

也就是说组件中是空的,没有放置任何文本或元素。可是原生的html元素都是能够进行嵌套的,div里面放table
什么的。自定义组件开闭标签之间也能够放置内容,不过须要在定义组件时使用slot。
slot至关于子组件设置了一个地方,若是在调用它的时候,往它的开闭标签之间放了东西,那么它就把这些东西放到slot中。

  • 当子组件中没有slot时,父组件放在子组件标签内的东西将被丢弃;
  • 子组件的slot标签内能够放置内容,当父组件没有放置内容在子组件标签内时,slot中的内容会渲染出来;
  • 当父组件在子组件标签内放置了内容时,slot中的内容被丢弃

列子

<div id="app"> //父组件模板:
        <h1>我是父组件的标题</h1>
        <my-component>
            <p>这是一些初始内容</p>
        </my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script>
        new Vue({
            el: "#app",
            data: {},
            components: {
                'my-component': {
                    template: `
                    <div>//子组件的模板
                        <h2>我是子组件的标题</h2>
                        <slot>
                            只有在没有要分发的内容时才会显示。
                        </slot>
                    </div> `
                }
            }
        })
    </script>

渲染 结果:

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

slot能够有不少个。那么子组件对于父组件放置的多余的内容如何放到各个slot中呢?方法就是子组件给每一个slot起一个名字name,父组件放置多余的元素时,给每一个元素的slot属性分配一个表明slot的名字。到时候,多余的内容就会根据本身的slot属性去找具备对应名字的slot元素。

注意

  • 子组件能够有一个匿名的slot,当分发的多余内容找不到对应的slot时,就会进入这里面
  • 若是子组件没有匿名的slot,当分发的多余内容找不到对应的slot时,就会被丢弃、

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

<div id="app">
        <app-layout>
            <h1 slot="header">这里多是一个页面标题</h1>
            <p>主要内容的一个段落。</p>
            <p>另外一个主要段落。</p>
            <p slot="footer">这里有一些联系信息</p>
        </app-layout>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script>
        new Vue({
            el: "#app",
            data: {},
            components: {
                'my-component': {
                    template: `
                    <div class="container">
                        <header>
                            <slot name="header"></slot>
                        </header>
                        <main>
                            <slot></slot>
                        </main>
                        <footer>
                            <slot name="footer"></slot>
                        </footer>
                    </div> `
                }
            }
        })
          </script>

渲染结果:

<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>

4.Vue支持将做用域插槽的属性解构。因此上述代码能够简写为:

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

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

相关文章
相关标签/搜索