v-model 摸爬滚打原理详解

v-model 是 Vue 框架提供的众多指令中的一个,其主要做用是能够实如今表单 <input><textarea><select> 标签元素上建立双向数据绑定。javascript

那双向数据绑定又是什么呢?在讲解 v-model 指令以前,咱们就先来讲说双向数据绑定吧!html

双向数据绑定

废话很少说,上来就直接亮一段 v-model 示例的代码吧!vue

// 定义 v-model 示例组件
Vue.component('bindData', {
  template:` <div> <p>this is bindData component!</p> <button @click="handleChange">change input value</button> <input type="text" v-model="inputValue" /> <p>{{inputValue}}</p> </div> `,
  data() {
    return {
      inputValue: 'hello'
    }
  },
  methods:{
    // 点击按钮改变 input 的值
    handleChange() {
      this.inputValue = `I'm changed`;
    }
  }
});

const app=new Vue({
  el: '#app',
  template: ` <div> <bindData /> </div> `
});
复制代码

我相信上面的一段简单的示例代码对于参与过基于 Vue 技术栈项目开发的同窗来讲都是常规操做了,若是你尚未参与过基于 Vue 技术栈项目的开发或者对 Vue 不怎么熟悉,我介意你先去查看官网的文档,尝试着作一两个项目,而后回过头来看我们的这篇文章,你确定会对 v-model 有新的认识!java

上面这段示例代码经过在 input 标签上绑定 v-model 实现一个很神奇的功能:react

  • 首先在 input 标签上经过 v-model 指令绑定 data_(若是你还不知道data 这个 Vue 示例的内置属性,你能够点击_这里_) _的 inputValue 属性;api

  • 由于 $data 的 inputValue 属性有一个 hello 的初始值,因此当组件 mounted 后,input 显示的值就是 hello,同时 input 标签下的 p 标签里面显示的也是 helloapp

  • 当组件显示后,向 input 中输入值,发现 input 标签下的 p 标签里面显示的是_ _input 标签当前展现的值框架


* 最后点击 input 标签上面的 button 标签,发现 input 标签的值和 p 标签里面显示的内容同时变化了,并且_显示内容一致,都显示成了 I'm changed_。

v-model

上面详细的列出了几点操做和展现的内容,回头看看这就是双向数据绑定呀!此时此刻,若是你还不是很了解 v-model 的实现原理,能够先静下心来根据本身如今已经知道的知识来好好想一想或者假设一下 v-model 内部是怎样操做的(不要怕想错 💪)。ide

几分钟过去了。。。[斜眼笑]函数

接触 v-model

如今咱们一块儿来分析一下吧:

  • 一、从 inputValue 属性入手吧,由于 input 标签、v-model、button 标签、p 标签的惟一联系就是这个 inputValue 属性了,不信你就看看示例代码吧!

  • 二、由于 input 属性是定义在 $data 上的,那就是响应式数据_(若是你还不知道响应式数据是啥,你能够点击_这里,根据响应式数据的特性:**当数据值被修改时,会驱动视图的变化,显示变化后的数据,**那么对应的绑定了 inputValue 属性的 p 标签显示的内容就会变化

  • 三、当咱们往 input 输入值时,p 标签显示的内容也相应的变化了,根据响应式数据的特性,这里确定是触发了 inputValue 值的变化,而且将 input 此时的值赋值给了 inputValue,才致使了 p 标签内容的变化;

  • 四、当点击 button 时,input 和 p 标签显示的值都变化了且显示的值同样,而在 button 绑定的 click 处理函数中,咱们直接将新值赋值给了 inputValue。p 标签是显示的展现了 inputValue 属性的值,因此理所固然的变化了,那 input 呢?

  • 五、据咱们所知,input 上面是有一个叫 value 的属性,咱们能够根据 value 属性给 input 设置显示值。那点击 button 后,咱们给 inputValue 设置了新的值,input 的显示值也变化了,那确定是有个给 input 的 value 属性设置值的操做;

  • 六、在第 3 点和第 5 点分别暴露出了在 input 上有两个隐式的操做:给 inputValue 设置和给 input 的 value 属性设置。可是在 input 标签上没有显示的绑定操做,只有一个 v-model 指令。

❓这里咱们就假设这两个隐式的操做就是 v-model 封装的。

懵逼改造 v-model

根据上面 6 点的分析或者根据咱们现有的知识,咱们稍微改写一下代码:

// 定义 v-model 示例组件改写
Vue.component('bindData1', {
  template:` <div> <p>this is bindData1 component!</p> <button @click="handleChange">change input value</button> <input type="text" :value="inputValue" @change="handleInputChange" /> <p>input 中的值为:{{inputValue}}</p> </div> `,
  data() {
    return {
      inputValue: 'hello'
    }
  },
  methods:{
    // 处理 input 输入 change 事件
    handleInputChange(e) {
      this.inputValue = e.target.value;
    },
    
    // 点击按钮改变 input 的值
    handleChange() {
      this.inputValue = `I'm changed`;
    }
  }
});

const app=new Vue({
  el: '#app',
  template: ` <div> <bindData1 /> </div> `
});
复制代码

改造后的代码能够实现和改造以前同样的双向数据绑定的效果,可是改造以前的示例示例代码,改造以后的代码主要修改了两个地方:

  • 一、将 input 标签上的 v-model 指令去掉了,换成了用 v-bind:value(缩写 :value) 指令来绑定 inputValue 属性,而且加上了一个 v-on:change(缩写 ) 事件;

  • 二、添加了一个 input change 处理函数,函数逻辑是将当前 input 标签的值赋值给 $data 的 inputValue 属性

深刻 v-model

看到这里,你是否是在犯嘀咕:这两中不一样的代码均可以实现一样的双向数据绑定的效果,确定不是巧合,这两种处理方式确定存在某种联系。

不错,上面两个示例基本是等效的实现。改造后的示例是改造前的复杂实现方式,也就是说 v-model 只是一种封装或者语法糖,负责监听用户的输入事件以更新数据,并对一些极端场景进行特殊处理。

v-model 会忽略全部表单元素的 value、checked、selected 特性的初始值而老是将 Vue 实例的数据做为数据来源。你应该经过 JavaScript 在组件的 data 选项中声明初始值。

v-model 在不一样的 HTML 标签上使用会监控不一样的属性和抛出不一样的事件:

  • text 和 textarea 元素使用 value 属性和 input 事件;

  • checkbox 和 radio 使用 checked 属性和 change 事件;

  • select 字段将 value 做为 prop 并将 change 做为事件。

看到这里是否是恍然大悟了!下面是一个 select 的示例,关于更多的示例能够上 Vue 官网查看

Feb-26-2019 12-57-55.gif

自定义组件 v-model

前文的内容只是讲解了 v-model 在原生表单标签上面的使用,在平时的开发中使用第三方 UI 库提供的组件时使用 v-model 的频率也很好,那自定义组件上面的 v-model 又是怎么实现的呢?

在自定义的组件上 v-model 默认会利用名为 value 的 prop 和名为 input 的事件实现,可是对于不一样的表单元素 value 属性会用于不一样的目的(正如咱们上面提到的),好比单选框、复选框表现为 checked。为了区别这些不一样的表现特性 Vue 给组件提供了 model 配置属性。model 是一个对象:提供 prop 属性指定组件 value 特性,event 指定值变化时触发的事件。

说了这么多,来一段示例代码吧!

// 自定义组件 checkbox
Vue.component('checkbox', {
  template: ` <input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change', $event.target.checked)" > `,
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean
  },
});

// 使用自定义组件 
Vue.component('useCheckbox', {
  template: ` <checkbox v-model="checkStatus" @change="handleChange"></checkbox> `,
  data() {
  	return {
    	checkStatus: false
    }
  },
  methods: {
  	handleChange(checked) {
    	// do something
    }
  }
});

复制代码

示例代码中的 checkStatus 的值将会传入这个名为 checked 的 prop。同时当 checkbox 触发一个 change 事件并附带一个新的值的时候,这个 checkStatus 的属性将会被更新。

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

v-model 修饰符

在原生的表单元素和自定义的组件中使用 v-model 咱们都讲解完了,同时咱们也讲到了 v-model 的实现原理,如今你们应该对 v-model 很了解了吧!

接下来,咱们就说说那些使用在 v-model 上的修饰符吧。

.lazy

在上面的内容中,咱们说起到 v-model 实现了双向数据绑定,双向数据绑定的特性是:当 input 标签显示的值实时变化时,也会实时的触发 input 标签上的 input 事件,在每次 input 事件触发后将输入框的值与数据实时进行同步。 在一些特殊的需求和场景下,你可能但愿数据同步不是实时同步而是在触发 change 事件的时候进行数据同步,那么你能够添加 lazy 修饰符进行处理,使用的示例以下:

<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg" >
复制代码

.number

在表单中处理输入验证必须是数字时,好比跟钱💰相关的操做,你这是可能就但愿能够将用户输入的值自动转换成数字类型,那你能够在 v-model 上加上 number 修饰符进行处理。

<!-- 将输入的值自动转换成数字类型 -->
<input v-model.number="age" type="number">
复制代码

⚠️ number 修饰符的处理原则是:使用 parseFloat() 函数对输入的值进行处理,若是输入的值是 parseFloat() 函数不能解析的,如以非数字开头的字符串,就会返回原始值。

.trim

对于用户在表单标签中输入的字符串,最后咱们都想去除首尾的空白字符,那么这个 trim 修饰符是很是有用的。

<!-- 去除输入值的首尾空白字符 -->
<input v-model.trim="msg">
复制代码

总结

v-model 是一个很是实用的指令,它的使用能够省去咱们不少的业务逻辑代码,使代码更加清晰、更好维护。固然 v-model 的实现原理仍是很是容易理解和消化的,再者尤大为了让咱们的开发更加方便,在 v-model 提供了很实用的修饰符简化咱们的操做,修饰符的使用也很简单。

v-model 的实现原理咱们算是搞清楚了,那下节咱们就说一下 Vue 源码究竟是怎么实现 v-model 的,下次再见吧👋!

相关文章
相关标签/搜索