没时间学 Vue (14) —— 组件(三):双向数据传递

本文深入探讨了Vue组件间的数据传递,包括v-model双向绑定、$emit自定义事件与回调函数的使用,通过实例演示如何解决不同场景下的组件数据同步问题。

上一篇 《没时间学 Vue (13) —— 绑定(五):计算属性和侦听器》中 “乱入” 讲了计算属性和侦听器,本篇咱们接着《没时间学 Vue (12) —— 组件(二):组件的创建、使用和数据传递》,继续讲组件的数据传递。

1、组件把数据传递给使用者

我们前篇的例子中,组件本身只有展示功能,而没有编辑功能 —— 也就是没有各种 <input> 的功能。
实际的项目中会用到这类组件,但也有不少情况下会用到带编辑功能的组件 —— 比如说,基于某些数据字典(Master)数据的选择组件(如:权限、用户类型、或者其他的某业务数据类型等),也可能是点赞、打分这种点击后执行某种业务逻辑的组件。
我们先从模拟选择地理位置的组件开始入手。为了简化模型,我们只做选择省级行政区的组件。
民政部对县级和以上的行政区划有标准编码(http://www.mca.gov.cn/article/sj/xzqh/2020/),咱们从中选择省级行政区的即可。比如:

行政区划代码地名
110000北京市
120000天津市
130000河北省
140000山西省
150000内蒙古自治区

我们还是写好省级行政区的组件,然后在 App.vue 中使用和测试。
在这里插入图片描述

2、 第一版(v-bind 版,无法更新)

根据前两篇的内容,我们很快就能写出 “第一版” 来,大概是这样的。
省份选择组件 Location.vue:

<template>
  <span class="location">
    <select v-model="selected">
      <option
        v-for="location in locations"
        :key="location.code"
        :value="location.code"
        >{{ location.name }}</option
      >
    </select>
  </span>
</template>

<script>
export default {
  name: "Location",
  props: {
    selected: Number,
  },
  data: function() {
    return {
      locations: [
        { code: 110000, name: "北京市" },
        { code: 120000, name: "天津市" },
        { code: 130000, name: "河北省" },
        { code: 140000, name: "山西省" },
      ]
    };
  }
};
</script>

<style></style>

App.vue

<template>
  <div id="app">
    <div>请选择位置:<Location :selected="location"></Location></div>
    <div>您选择的位置是:{{ location }}</div>
  </div>
</template>

<script>
import Location from "./components/Location";

export default {
  name: "App",
  components: {
    Location,
  },
  data: function() {
    return {
      location: 110000,
    };
  },
};
</script>

<!-- 没有特殊的样式 -->
<style></style>

验证一下效果。初始显示 OK!
但是,修改位置之后,App.vue 中的值并没有跟着改变 …
在这里插入图片描述
如果你对之前的章节还有印象,那么你很容易能推测出来:

3、第二版 (v-model 版,好大一颗语法糖)

接着上面的原因分析,我们很容易想到:
既然 v-bind 是单向绑定的,那我们用双向绑定的 v-model 就好了。
于是我们像下面这样修改了代码:在这里插入图片描述
结果发现画面显示反而更糟糕了 😳😳😳
在这里插入图片描述
不要吃惊,其实 Vue 官网(《表单输入绑定 - 基础用法》)上已经说了,

你可以用 v-model 指令在表单 <input>、<textarea> 及 <select> 元素上创建双向数据绑定。
它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法糖
它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。

那我们自己写的组件里能不能用 v-model 呢?
可以,不过得遵循 《组件基础 - 在组件上使用 v-model》中的规则。
其中给出的实例代码如下:

Vue.component('custom-input', {
  props: ['value'],
  template: `
    <input
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
  `
})

我们可以类比着如下修改我们的 Location.vue:
在这里插入图片描述
检验一下结果,数据修改后可以正确更新 App.vue 了,但是初始显示不正确 …
在这里插入图片描述
虽然参照了 Vue 官网的做法,还是吃了个瘪。官网上正确的的做法在《自定义事件 - 自定义组件的 v-model》,还需要在组件中加一个 model 属性。
在这里插入图片描述
咱们只要在 Location.vue 中照葫芦画瓢加上 model 属性,初始显示就正常了。
在这里插入图片描述
model 属性的详细说明在 https://cn.vuejs.org/v2/api/#model ,里面涉及的用法更加的绕。
https://segmentfault.com/a/1190000019337976 这篇倒是写得比较清楚,有时间可以翻一翻。

4、第三版($emit 自定义事件)

除了上面的 v-model 语法糖,Vue 官网的《组件基础 - 使用事件抛出一个值》中还推荐了使用 $emit 触发一个自定义事件、并且在 $emit 的第二个参数中传递数据的做法。
在这里插入图片描述
我们也可以类似地改造之前的代码 —— 记得先回退到第一版 —— 要是你有使用 git 的好习惯,这个事情就很容易做了 😀
Location.vue :
在这里插入图片描述
App.vue:
在这里插入图片描述
虽然代码比 v-model 多了一丢丢,但是效果是杠杠的,理解起来也很容易。

再回过头来看一下上面说的 v-model 语法糖,其实也是用了 $emit 的!
只不过,触发的事件不是自定义的,而是 input 这种。

5、第四版 ($emit + v-bind.sync)

你要是觉得上面第三版 $emit 的代码写得有点儿多的话,
Vue 还提供了 v-bind.sync 这种简化的写法,
来省去 App.vue 中的事件处理函数。
在这里插入图片描述
不过,这个也是有前提条件的,只有符合特定的写法才能生效。
1)Location.vue 中仍然要用 $emit 触发事件;
2) $emit 触发的事件的名称必须是 update:绑定属性名 这种风格的;
3) $emit 的第二个参数,必须是绑定属性的新的值。在这里插入图片描述

6、第五版(回调函数)

这种做法很少见于 “组件间数据传递” 的常规套路,但是在各种业务处理中却很常见。
比如说,Java 和 C# 中的排序回调,JavaScript 和 Python 的高阶函数等等。
它的出发点跟 $emit 刚好相反,
使用者不捕获组件中抛出的事件 —— 组件中直接调用使用者传递过来的回调函数。
在这里插入图片描述
在这里插入图片描述
其结果也是 OK 的 —— 这就是第一性原理。

7、思考题

1)将上面的控件变成 checkbox 的风格
在这里插入图片描述
2)保留控件的下拉框风格,但是在 App.vue 中同时显示代码和地名:
在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值