v-model是 vue.js 中用于在表单表单元素上创建双向数据绑定,它的本质只是一个语法糖,在单向数据绑定的基础上,增加了监听用户输入事件并更新数据的功能;

对,它本质上只是一个语法糖,但到底是一个什么样的语法糖呢……?

正好最近我也踩到相关的坑了,就从最简单的input和textarea元素入手,分析一下v-model这个指令到底做了什么吧

请先确认您已阅读过官方文档中关于v-model的部分

没有任何修饰符的 v-model

v-model 会根据绑定元素的类型,监听不同的输入事件,在 input 和 textarea 上,它默认监听的就是 input 事件;

简单点说,如果有这样一段模板:

    <input v-model="name" type="text"/>

那么 v-model 的行为,就比较类似:

    <input :value="name" @input="name = $event.target.value" type="text"/>

关于$event变量,官方文档有介绍:

有时也需要在内联语句处理器中访问原生 DOM 事件。可以用特殊变量 $event 把它传入方法

虽然上面的例子描述了 v-model 最基础的行为,但二者的行为也不是完全一样,这次我踩到的坑也就在这里了……

官方文档中表单控件绑定章节有这么一条提示:

对于要求 IME (如中文、 日语、 韩语等) 的语言,你会发现v-model不会在 ime 构成中得到更新。如果你也想实现更新,请使用input事件。

IME 的全称是 Input Method Editor 也就是我们常说的输入法

所谓 IME 构成,是指我们在输入文字时,处于未确认状态的文字,以微软的中文输入法举例:



图中带下划线的部分就属于 IME 构成,这些东西同样会触发input事件,但不会触发v-model更新数据……

本着踩进坑里了就不能白踩的精神,阅读了一下 vue.js 的源码:

function genDefaultModel (
el: ASTElement,
value: string,
modifiers: ?ASTModifiers
): ?boolean {
const type = el.attrsMap.type
const { lazy, number, trim } = modifiers || {}
const needCompositionGuard = !lazy && type !== 'range'
const event = lazy
? 'change'
: type === 'range'
? RANGE_TOKEN
: 'input' let valueExpression = '$event.target.value'
if (trim) {
valueExpression = `$event.target.value.trim()`
}
if (number) {
valueExpression = `_n(${valueExpression})`
} let code = genAssignmentCode(value, valueExpression)
if (needCompositionGuard) {
code = `if($event.target.composing)return;${code}`
} addProp(el, 'value', `(${value})`)
addHandler(el, event, code, null, true)
if (trim || number || type === 'number') {
addHandler(el, 'blur', '$forceUpdate()')
}
}

可以看到,v-model在编译的时候自动添加了一层判断,根据event.target.composing判断此次input事件是否是 IME 构成触发的……然而不知为什么,我没有在 MDN 上查到这个属性……

所以 v-model 的实际行为和这样的模板是一致的:

    <input :value="name" @input="if($event.target.composing)return;name=$event.target.value" type="text"/>

当然也有例外,比如type="range"的 input 元素就不会加这层判断,再怎么说滑块也是不会和 IME 构成有关的;

添加了 trim 修饰符的 v-model

trim修饰符的作用就是去除输入内容两侧的空格,这个没什么问题……

v-model.trim的行为类似于:

    <input :value="name" @input="if($event.target.composing)return;name=$event.target.value.trim()" type="text"/>

添加了 lazy 修饰符的 v-model

lazy修饰符按照官方的描述是将默认监听的 input 事件改为监听 change 事件:

在默认情况下, v-model 在 input 事件中同步输入框的值与数据 (除了 上述 IME 部分),但你可以添加一个修饰符 lazy ,从而转变为在 change 事件中同步

而 change 事件一般是在输入元素失去焦点时才会触发,所以也不用考虑 IME 构成什么的了,v-model.lazy 的行为就类似于:

    <input :value="name" @change="name=$event.target.value" type="text"/>

添加了 number 修饰符的 v-model

官方文档说这个修饰符可以自动把输入内容转为数字,然而具体是怎么转的数字并没有说……

如果想自动将用户的输入值转为 Number 类型(如果原值的转换结果为 NaN 则返回原值),可以添加一个修饰符 number 给 v-model 来处理输入值

从之前的源码来看,vue.js使用了一个_n函数来处理输入内容,也就是说v-model.number的行为可以这样模仿:

    <input type="text" :value="name" type="number" @input="if($event.target.composing)return;name =_n($event.target.value)">

……然而这个_n是什么……

既然是能直接用的函数,那肯定能在 runtime 里找到,稍微翻了一下发现这个_n实际上是一个toNumber的工具函数,而toNumber函数是下面这个样子的:

/**
* Convert a input value to a number for persistence.
* If the conversion fails, return original string.
*/
export function toNumber (val: string): number | string {
const n = parseFloat(val)
return isNaN(n) ? val : n
}

可见,它是使用parseFloat函数来转的数字,再使用isNaN判断转换结果,如果结果是NaN,那么就返回原字符串,否则返回转为数字后的结果;

所以如果想完整模拟number修饰符,只在内联语句中写就不太现实了……姑且尝试了一下,类似下面这个样子:

    <input type="text" :value="name" type="number" @input="if($event.target.composing)return;name=isNaN(parseFloat($event.target.value))?$event.target.value:parseFloat($event.target.value)">

好吧,我承认这个修饰符很好用了……(:з)∠)

弄懂这个修饰符到底是什么之后,应该也就不会弄错了,不过反正都写到这里了,顺便就根据别人问过我的问题,整理一下number修饰符几个常见的理解误区吧:

number 修饰符不能限制输入内容

这个修饰符的作用仅仅是把用户输入的内容尝试转换一下,如果转换成功了,那就是成功了,如果没成功,它也不会多管闲事的不让你输入进去;

就算输入的不是真实的数字,也可能会被转换

因为使用的是parseFloat函数嘛……所以只要输入字符串的第一个字符是数字,最后就会被转成数字,比如这样

number 修饰符只会尝试转换,不会尝试计算

它只是一个简单的修饰符……请不要尝试让它做太多的事情……就算你输入一个1+1,更新数据时也不会变成2的……当然,也不会原封不动的把"1+1"这个字符串放进去,因为刚才说过了嘛……只要第一个字符是数字,parseFloat这个函数就能把输入的字符串给你转成数字……←A←

以上就是今天要写的内容啦,如果再有什么新发现,我可能会再补充~

总的来说……vue.js 写的还是很精致的,也相当好用,源码也非常清晰,偶尔读读原理,想必也会对使用这个框架有或多或少的帮助吧

起码我觉得自己以后是不会再踩进 v-model 的坑了……

【下次抽时间也研究一下 select 和 checkbox 的 v-model 行为好了……

如果文章中有什么错误或者疏漏,请告诉我,我会尽快修改!

关于本文有任何问题也欢迎评论~

转载请注明来源

vue.js中,input和textarea上的v-model指令到底做了什么?的更多相关文章

  1. 从vue.js的源码分析,input和textarea上的v-model指令到底做了什么

    v-model是 vue.js 中用于在表单表单元素上创建双向数据绑定,它的本质只是一个语法糖,在单向数据绑定的基础上,增加了监听用户输入事件并更新数据的功能:对,它本质上只是一个语法糖,但到底是一个 ...

  2. vue.js 中双向绑定的实现---初级

    1. 1 我们看到的变量,其实都不是独立的,它们都是windows对象上的属性 <!DOCTYPE html> <html lang="en"> <h ...

  3. 浅析Vue.js 中的条件渲染指令

    1 应用于单个元素 Vue.js 中的条件渲染指令可以根据表达式的值,来决定在 DOM 中是渲染还是销毁元素或组件. html: <div id="app"> < ...

  4. vue.js中的slot

    vue.js 中的 slot 一.slot 的作用 调用组件的时候,对于数据,我们会用props将数据从父组件传至子组件.但是,如果从父组件到子组件,单纯是页面局部渲染的改变,slot会更合适. 二. ...

  5. vue.js 中 data, prop, computed, method,watch 介绍

    vue.js 中 data, prop, computed, method,watch 介绍 data, prop, computed, method 的区别 类型 加载顺序 加载时间 写法 作用 备 ...

  6. vue.js中ref及$refs的使用及讲解

    关于ref和$refs的用法及讲解,vue.js中文社区( https://cn.vuejs.org/v2/api/#ref )是这么讲解的: ref 被用来给元素或子组件注册引用信息,引用信息将会注 ...

  7. 实例分析Vue.js中 computed和methods不同机制

    在vue.js中,有methods和computed两种方式来动态当作方法来用的 1.首先最明显的不同 就是调用的时候,methods要加上() 2.我们可以使用 methods 来替代 comput ...

  8. Vue.js中使用select选择下拉框

    在Vue.js中使用select选择下拉框有两种方法: 第一种: Add.html: <select v-model="sysNotice.noticeType" id=&q ...

  9. Vue.js中css的作用域

    Vue.js中的css的作用域问题: 如果在vue组件下的style中定义样式,效果会作用于整个html页面,如果只想本组件的css样式只作用于本组件的话,在<style>标签里添加sco ...

随机推荐

  1. 从数据库提取数据通过jstl显示在jsp页面上

    从数据库提取数据通过jstl显示在jsp页面上 1.ConnectDB.java连接数据库,把数据转换成list public class ConnectDB { private final stat ...

  2. Odd Numbers of Divisors

    给出一个正奇数K,两个正整数low,high. 有多少整数属于[low, high],且包含K个因子. 数据 C(0 < C < 1e5),测试样例数. (1 < K < 10 ...

  3. android学习3——长宽的单位问题dp,px,dpi

    android设备的单位px,pt,dp,sp 分辨率 先通俗说下分辨率的概念.可以把屏幕想想成一个个正方形格子组成的.如果横向有1280个格子,竖向有720个格子.那么分辨率就是1280*720.这 ...

  4. map与hash_map使用与对比

    #include <iostream> #include <functional> #include <map> #include <ext/hash_map ...

  5. windows下读取utf-8文件

    #include <stdio.h> #include <tchar.h> #include <memory> int main() { FILE* fp1 = f ...

  6. 3D Touch开发全面教程之Peek and Pop - 预览和弹出

    ## 3D Touch开发全面教程之Peek and Pop - 预览和弹出 --- ### 了解3D Touch 在iPhone 6s和iPhone 6s Plus中Apple引入了3D Touch ...

  7. 初识html5——试试博文编辑器

    1.html5简介 HTML5 将成为 HTML.XHTML 以及 HTML DOM 的新标准. HTML 的上一个版本诞生于 1999 年.自从那以后,Web 世界已经经历了巨变. HTML5 仍处 ...

  8. 【转】Docker —— 从入门到实践

    http://yeasy.gitbooks.io/docker_practice/content/index.html Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud ...

  9. Redis实战与 Session缓存

    C#操作Redis的库有很多,比如C# Redis Client就很好用, 在NuGet上搜索 ServiceStack.Redis  安装到项目中,将会添加以下引用 ServiceStack.Red ...

  10. Hibernate框架双向多对多关联映射关系

    建立双向多对多关联关系    Project.java (项目表)                private Integer proid;                private Strin ...