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. Myeclipse删除default包

    MyEclipse删除default包(不显示default包) 很多时候,我们一不留神会出现下方这种情况,这个default包真难看 这里直接提供解决方案 之后点击这个Filter,然后去掉Empt ...

  2. PHPCMS 概念初学

    CMS是一个内容管理系统,主要是用来做企业站,也就是说我们在网上见到绝大部分的企业站都是用这个做的,有极少数的是用源生代码写的 如何安装? 1.从网站搜索下载安装包并解压 2.点击文件夹后显示一下两个 ...

  3. 前端开发面试题总结之——HTML

    ______________________________________________________________________________________________ 相关知识点 ...

  4. 杭电2000——ASCII码排序

    /* ASCII码排序 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Tota ...

  5. ubuntu16.04下安装配置深度学习环境(一、cuda7.5的安装)

    1.下载所需要的软件 cuda7.5下载(点击下载链接),cudnn4.0下载 2.安装NVIDIA驱动. 一般有两种方法:1)一种方法是利用"软件和更新"来安装,依次选择 系统设 ...

  6. 最新虚拟机SAP ECC6.0 EHP7带示例数据IDES版+BW740

    学习SAP,需要虚拟机,请联系QQ:858-048-581 版本:ECC6.0 EHP7.虚拟机操作系统:windows server 2008 64bit..数据库:SQL2008 R2.系统已经做 ...

  7. 【openstack N版】——创建云主机

    一.启动实例 1.1 已准备服务介绍 MySql:为各个服务提供数据存储. RabbitMQ:为各个服务之间通信提供交通枢纽. keystone:为各个服务之间通信提供认证和服务注册. Glance: ...

  8. 关于Java空指针的控制(转)

    1)在已经的String(字符串)调用 equal()和 equalsingnoreCase()而不是未知的对象 通常在已经的非空字符串在调用equals().因为equal()方法是对称的,调用a. ...

  9. Javaweb之Servlet入门

    1. 什么是Servlet? Java Servlet 是运行在 Web 服务器或应用服务器上的程序:他是浏览器(HTTP客户端)请求和HTTP服务器上资源(访问数据库)之间的中间层. 2. 什么是S ...

  10. CentOs下安装PHP环境的步骤

    前言 在CentOs环境下安装php开发环境,需要首先安装一些源文件,然后使用yum命令直接安装即可,在Fedora 20 源中已经有了PHP的源,直接可以使用以下命令安装即可: # yum inst ...