【单向数据流 / 单向绑定原则】自定义组件绑定事件未触发 | 避坑

1,961 阅读3分钟

一、前言

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情。正如各位所见,Rookie 易踩的一个坑!!自定义的一个组件发现用的时候绑定事件,发现并没有触发!百思不得其解,后面检查了好几遍代码, 内心OS:这么简单的代码不可能写错的,NO POSSIABLE,绝对 NO POSSIABLE ……

二、需求 & 效果

单击触发绑定在自定义组件上的点击事件,改变标题文字即可!对,你没听错就是这么简单。

直接上效果:

效果图1.gif

三、踩坑代码

3.1 自定义组件

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  }
}
</script>

非常简单的自定义组件写法,仅用了一个 props 自定义属性,接收来自父组件的数据。

3.2 使用

使用的时候仅需将自定义组件导入到该文件中,使用 HTML 式的标签写法就可以使用它了。

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" width="200" height="200" >
    <HelloWorld :msg="title" @click="showTitle"/>
  </div>
</template>

<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'

export default {
  name: 'HomeView',
  components: {
    HelloWorld
  },
  data() {
    return {
      title: "Welcome to my Vue.js App"
    }
  },
  methods: {
    showTitle() {
      this.title = "HUALEI";
    }
  }
}
</script>

注意使用 v-bind 或缩写 : 来进行动态绑定的 props 而未不使用静态只形式的 props ,以此来达到点击后动态变化标题文字的效果!

监听事件我们可以使用 v-on 指令 (简写为 @) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript 。用法:v-on:click="methodName"@click="handler"

点击事件发生后,会调用 showTitle() 方法来改变动态绑定的变量 title ,变换后网页渲染加载就得到想要的效果了。

四、问题分析

一通操作和分析之后发现无论点击多少次都没有进入到触发的点击事件中去,到底是为什么呢?代码并未报错但是就是让人头大、费解 ……

4.1 props 单向绑定

通俗的讲,就是自定义的组件拥有自己规定的暴露“接口”和专属的状态,不能说你使用了它就像给它扣上不属于 props 中的东西,从而改变它原属的状态,这不就乱套了嘛!

这就避免了子组件意外修改父组件的状态的情况,这就是所谓的单向数据流、props 的单向绑定。

也就是说,你自定义的组件中没有在 props 中暴露 @click 事件,便不在用的时候直接绑定一个点击事件,这样容易错乱父组件的数据流,不符合上述原则!!

五、方法解决

那么如何向外告知自定义组件要处理一个点击事件呢?

5.1 方法一

在自定义组件的 props 中暴露这个点击事件,这样就等同于说,你现在可以使用它然后绑定点击事件了:

<template>
  <div class="hello">
    <h1 @click = "triggerMethod">{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String,
    triggerMethod: Function
  }
}
</script>

使用:

<template>
  <div class="home">
    ... ...
    <HelloWorld :msg="title" :triggerMethod="showTitle"/>
  </div>
</template>

<script>
... ....
</script>

在自定义组件中暴露出来的 triggerMethod 相当于是沟通函数调用执行到父组件的点击事件的桥梁。

5.2 方法二

使用 .native 事件修饰符

在封装/自定义的组件中,必须应用 .native 事件修饰符监听根元素的原生事件才能触发父组件上的事件,将事件传递至组件内,也符合组件间的单向数据流。

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" width="200" height="200" >
    <HelloWorld :msg="title" @click.native="showTitle"/>
  </div>
</template>

<script>
  ... ...
</script>

5.3 方法三

使用 $emit 来实现事件传递

仅需改动自定义组件,其他跟原先保持一致:

<template>
  <div class="hello">
    <h1 @click="triggerMethod">{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String,
    //triggerMethod: Function
  },
  methods: {
    triggerMethod() {
      this.$emit("click")
    }
  }
}
</script>

子组件内部处理点击事件然后向外发送这个事件,父组件不需改动。

六、结尾

撰文不易,欢迎大家点赞、评论,你的关注、点赞是我坚持的不懈动力,感谢大家能够看到这里!Peace & Love。

七、参考

单向数据流