vue中v-model实现原理,几种实现思路

v-model双向绑定

1、普通版实现

  • 通过props接收value,作为input输入框的property (dom属性:加了.prop修饰符)
  • 绑定输入框input事件,触发input事件,通知父组件更新数据
// input.vue
<template>
  <input type="text" :value.prop="value" @input="notify" />
</template>
<script>
export default {
  props: { value: String },// 必须用 "value" 这个prop
  methods: {
    notify(e) {
      // 输入框的值在e.target.value
      this.$emit("input", e.target.value);// 必须触发 "input" 事件
  },
};
</script>

// ---------------------------
// 假如不想使用 "value" 这个prop和 "input" 这个事件名怎么办?
// 可以指定model
<template>
  <input type="text" :value.prop="book" @input="notify" />
</template>
<script>
export default {
  model: { prop: "book", event: "change" },// 指定prop为"book",指定事件为"change"
  props: { book: String },// 接收book
  methods: {
    notify(e) {
      this.$emit("change", e.target.value); // 触发"change"事件
    },
  },
};
</script>

// ---------------------------
// father.vue
<template>
  <the-input v-model="book"></the-input> // 使用v-model,绑定book
</template>
<script>
import theInput from "./input.vue";
export default {
  components: { theInput },
  data() {
    return {
      book: "",
    };
  },
};
</script>

可以发现prop必须是"value",触发的事件必须是"input",这是因为v-model默认绑定这俩键名
通过设定model可以指定新的prop与事件名称
子组件编写完成后,在父组件引入,这时候就可以使用v-model了

2、通过watch实现

  • 用上面的方法使用v-model是比较简洁清爽的,但是子组件只能被动的接收prop中的value,没有自身的状态
  • 通过watch就能让组件拥有自己的状态
  • 先讲实现思路
  • 子组件通过props接收value,赋值给自身内部的internalValue
  • 监听value变化实时更新internalValue ;
  • 另外绑定input事件,通知父组件更新value,
// input.vue
<template>
  <input type="text" :value.prop="internalValue" @input="notify" />
</template>
<script>
export default {
  props: { value: String },
  data() {
    return {
      internalValue: this.value, // 内部value
    };
  },
  watch: {
    value(val) {
      this.internalValue = val; // 实时更新数据
    },
  },
  methods: {
    notify(e) {
      this.$emit("input", e.target.value); // 更新父组件数据
    },
  },
};
</script>

// ---------------------------
// father.vue 
<template>
  <the-input v-model="book"></the-input> // 使用v-model,绑定book
</template>
<script>
import theInput from "./input.vue";
export default {
  components: { theInput },
  data() {
    return {
      book: "",
    };
  },
};
</script>

如何实现一个能够自动过滤首尾空格的v-model?
只需要在watch里边赋值的时候对传过来的值进行修剪操作就好了

 watch: {
    value(val) {
      this.internalValue = val.trim(); // 实时更新数据,并且裁剪字符串两边的空格
    },
  },
  • 每次输入框输入的时候,会触发input事件,这时候更新父组件的value
  • 子组件watch到value变化,于是尝试更新internalValue
  • 更新internalValue的同时,顺便修剪了一下首尾的空格
  • 这时候输入框的value就是修剪之后的值了

.sync修饰符双向绑定

// input.vue
<template>
  <input type="text" :value.prop="book" @input="notify" />
</template>
<script>
export default {
  props: { book: String },
  methods: {
    notify(e) {
      this.$emit("update:book", e.target.value); // 更新父组件数据
    },
  },
};
</script>

// ---------------------------
// father.vue 
<template>
  <the-input :book.sync="book"></the-input>
</template>
<script>
import theInput from "./input.vue";
export default {
  components: { theInput },
  data() {
    return {
      book: "",
    };
  },
};
</script>
  • sync修饰符和v-model很像,都是接收一个prop、触发一个update事件
  • 区别在于v-model必须接收特定的prop(value)和触发特定的事件(input)
  • 而sync修饰符可以接收任意prop,在更新的时候触发对应的update事件就好了
  • 所以可以把sync修饰符理解为灵活版本的v-model

render函数形式

<script>
export default {
  render(h) {
    return h("input", {
      domProps: { value: this.value },
      on: { input: this.notify },
    });
  },
  props: { value: String },
  methods: {
    notify(e) {
      this.$emit("input", e.target.value); // 更新父组件数据
    },
  },
};
</script>

template模板最终会编译成render函数,所以直接用render写法是一种更高效的方式
相比较而言render写起来会更难阅读一些,具体用法参考vue官方文档

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值