Vue.js 之 组件-监听子组件事件

1、监听子组件事件

前面介绍了父组件如何通过 prop 向子组件传递数据,反过来,子组件如何向父组件通信呢?
在 Vue.js 中,这是通过自定义事件来实现的,子组件使用 $emit() 方法触发事件,父组件使用 v-on 指令监听子组件的自定义事件。

// evenName: 事件名
// args: 事件传递的参数
vm.$emit( evenName, [...args] )
<div id="app">
    <child @greet="sayHello">child>
div>

<script src="vue.js">script>
<script>
    Vue.component('child', {
      
        props: [],
        data() {
      
            return {
      
                name: '张三'
            }
        },
        methods: {
      
            handleChick() {
      
                this.$emit('greet', this.name)
            }
        },
        template: ``
    });
    new Vue({
      
        el: '#app',
        methods: {
      
            sayHello(name) {
      
                alert("Hello, " + name)
            }
        }
    });
script>

1.1、案例:点赞

<div id="app">
    <post-list>post-list>
div>

<script src="vue.js">script>
<script>
    // 父组件
    Vue.component('PostList', {
      
        data() {
      
            return {
      
                posts: [
                    {
      id: 1, title: '《Spring Boot实践》', author: '张三', date: '2019-10-21 20:10:15', vote: 0},
                    {
      id: 2, title: '《Vue.js入门》', author: '李四', date: '2019-10-10 09:15:11', vote: 0},
                    {
      id: 3, title: '《Python数据分析》', author: '王五', date: '2019-11-11 15:22:03', vote: 0}
                ]
            }
        },
        methods: {
      
            // 自定义事件vote的事件处理器方法
            handleVote(post) {
      
                post.vote = ++post.vote
                return post
            }
        },
        template: `
          
`
}); // 子组件 Vue.component('PostListItem', { methods: { handleVote() { // 触发自定义事件 this.$emit('vote'); } }, props: ['post'], template: `
  • 标题:{ { post.title }} | 发帖人:{ { post.author }} | 发帖时间:{ { post.date }} | 点赞数:{ { post.vote }}

  • `
    }); let vm = new Vue({ el: '#app' });
    script>

    2、将原生事件绑定到组件

    在组件上也可以监听原生事件,在使用 v-on 命令时,添加一个 .native 修饰符即可。如:

    <base-input @focus.native="onFocus">base-input>
    

    这种方式最终是在组件的根元素上添加了 focus(聚焦)事件的监听,如果组件模板的根元素是 ,那没有问题,但是如果不是,就有问题了。如:

    Vue.component('MyInput', {
         
        template: `
            
    })
    

    根元素是 ,相当于在上添加了 focus 事件监听器,这时,父级的 .native 监听器将静默失败,它不会报错,但是 onFocus 处理函数不会被如期被调用
    为了解决这个问题,Vue.js 提供了一个 $listeners 属性,它是一个对象,里面包含了作用在这个组件上的所有监听器,如:

    {
         
        focus(event) {
         ...},
        input(value) {
         ...},
        ...
    }
    

    有了 $listeners 属性,就可以使用 v-on="$listeners" 将组件上的所有事件监听器发送到特定的子元素。对于需要那些使用 v-model 的元素(如 )来说,可以为这些监听器创建一个新的计算属性,如下面:

    <div id="app">
        <my-input :label="title" v-model="msg" @focus="onFocus">my-input>
        <p>{
        { msg }}p>
    div>
    
    <script src="vue.js">script>
    <script>
        Vue.component('MyInput', {
          
            inheritAttrs: false,
            // 父级传入数据:title -> label;msg -> value
            props: ['label', 'value'],
            data() {
          
                return {
          }
            },
            computed: {
          
                inputListeners() {
          
                    let vm = this
                    // 将所有的对象合并为一个新对象
                    return Object.assign({
          },
                        // 从父级添加的所有监控
                        this.$listeners,
                        // 添加自定义监控器或覆写一些监听器的行为
                        {
          
                            // 确保组件和 v-model 一起工作
                            input(event) {
          
                                vm.$emit('input', event.target.value)
                            }
                        }
                    )
                }
            },
            template: `
              `
        });
        new Vue({
          
            el: '#app',
            data: {
          
                title: '输入框:',
                msg: '请输入'
            },
            methods: {
          
                onFocus() {
          
                    console.log("不要摸人家么,好痒,臭流氓!")
                }
            }
        });
    script>
    

    2.1、.sync 修饰符

    在某些情况下,可能需要对一个组件的 prop 进行双向绑定,Vue.js 推进以 update: myPropName 模式触发事件来实现。例如:

    <div id="app">
        <span>父组件计数值:{
         {
          counter }}</span>
        <!--    <child :val="counter" @update:val="addCounter"></child>-->
        <!--  $event:自定义事件的附加参数  -->
        <child :val="counter" @update:val="counter = $event"></child>
    </div>
    
    <script src="vue.js"></script>
    <script>
        Vue.component('child', {
         
            props: {
         
                val: {
         
                    type: Number,
                    default: 0
                }
            },
            data() {
         
                return {
         
                    count: this.val
                }
            },
            methods: {
         
                handleChick() {
         
                    this.$emit('update:val', ++this.count)
                }
            },
            template: `
              
    子组件计数值:{ { val }}
    `
    }); new Vue({ el: '#app', data: { counter: 0 }, methods: { addCounter(val) { return this.counter = val; } } }); </script>

    为了方便起见,Vue.js 为了上述这种模式提供了一个缩写,即 .sync 修饰符(在 v-bind 指令上使用),修改如下:

    <child :val="counter" @update:val="counter = $event">child>
    

    <child :val.sync="counter">child>
    

    当用一个对象同时设置多个 prop 的时候,也可以将 .sync 修饰符和 v-bind 一起使用:

    <text-document v-bind.sync="doc">text-document>
    

    这里会把 doc 对象中的每一个属性作为一个单独的 prop 传进去,然后为每个属性添加 v-on:update 监听器。

    <body>
    <div id="app">
        <span>父组件 post:{
        { post.title }} | {
        { post.author }} | {
        { post.time }} | {
        { post.vote }} | {
        { post.price }}span>
        <child v-bind.sync="post">child>
    div>
    
    <script src="vue.js">script>
    <script>
        Vue.component('child', {
          
            props: {
          
                title: {
           type: String },
                author: {
           type: String },
                time: {
           type: String },
                vote: {
           type: Number },
                price: {
           type: Number },
            },
            data() {
          
                return {
          
                    title: this.vote,
                    author: this.vote,
                    time: this.vote,
                    vote: this.vote,
                    price: this.price
                }
            },
            methods: {
          
                handleChick() {
          
                    this.$emit('update:vote', this.vote += 1)
                    this.$emit('update:price', this.price += 4)
                }
            },
            template: `
              
    子组件 post:{ { title }} | { { author }} | { { time }} | { { vote }} | { { price }}
    `
    }); new Vue({ el: '#app', data: { post: { title: '《Spring Boot 从入门到入土》', author: '张三', time: '2021年05月16日00:05:50', vote: 0, price: 0 } }, methods: { } });
    script>

    注:本篇主要来源:《Vue.js 从入门到实践》第十一章 组件,作者:孙鑫,出版社:中国水利水电出版社

    你可能感兴趣的:(vue.js)