博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
v-model 是怎么实现的?
阅读量:5977 次
发布时间:2019-06-20

本文共 3842 字,大约阅读时间需要 12 分钟。

表单v-model

在vue中,v-model无疑是最常用的API之一了,像 input、textarea、radio、checkbox、select等都可以使用v-model实现双向绑定。那么它具体是怎么实现的呢? 下面通过自己写的一个demo来具体分析一下?

directives

在vue的parse阶段也就是将AST树生成code的过程中会去收集元素上定义的指令

这个时候我们可以看到dir.name是model(这个model指的是指令名称model), 然后去拿到gen,实际上就是model绑定的函数,那么这个时候会执行到genDefaultModel(el, value, modifiers);其中el是指tag的AST对象, value是model, modifiers是undefined。
那么我们来看下genDefaultModel这个函数的实现
这个函数中首先执行了modifiers,它的不同主要影响event和valueExpression ,那么在我们的这个demo中很显然,event是input。valueExpression 是$event.target.value,然后接下来去执行genAssignmentCode生成code,我们来看下genAssignmentCode的实现
这里执行了parseModel,那么在我们的这个demo里,value就是model所以res.key==null那么就得到

model = $event.target.value复制代码

再回到genDefaultModel,接下来会执行两个非常重要的方法

addProp(el, 'value', ("(" + value + ")"));  addHandler(el, event, code, null, true);复制代码

第一个方法addProp实际上就是给我们的input标签添加一个prop,相当于动态绑定了value, 第二个方法addHandler就是给input标签添加一个input事件,并且在事件触发的时候动态修改model的值。如此,也就实现了双向绑定。

复制代码

最后再回到genDirectives,会根据指令生成一个render函数

组件v-model

let baseSpan = {  template: '
我是显示还是不显示呢' + '
', props: ['value'], name: 'baseSpan', data(){ return { isShow: this.value } }, methods: { changeValue() { this.isShow = true this.$emit('input', true) } }} var app = new Vue({ el: '#app', data: { visible: false }, components: { baseSpan }})Vue.component('base-span',baseSpan);复制代码

html代码如下

{
{visible}}
复制代码

父组件的visible数据关联到了子组件baseSpan v-model指令上,而子组件上定义了一个value的props,以及一个changeValue方法,方法内部通过派发一个input事件向父组件传递数据。 这是典型的父子组件通信方式,也是v-model生效的必要条件。当然这个value和input并不是写死的,我们可以根据实际需要在子组件的model配置中进行自定义,比如,我们派发的事件也可以是一个change。那么我们来看下源码是如何设计的?

if (el.component) {    genComponentModel(el, value, modifiers);    // component v-model doesn't need extra runtime    return false}复制代码

组件的v-model同样执行指令的model函数,然后命中上面的if语句,进而执行genComponentModel(el, value, modifiers)方法。

function genComponentModel (  el,  value,  modifiers) {  var ref = modifiers || {};  var number = ref.number;  var trim = ref.trim;  var baseValueExpression = '$$v';  var valueExpression = baseValueExpression;  if (trim) {    valueExpression =      "(typeof " + baseValueExpression + " === 'string'" +      "? " + baseValueExpression + ".trim()" +      ": " + baseValueExpression + ")";  }  if (number) {    valueExpression = "_n(" + valueExpression + ")";  }  var assignment = genAssignmentCode(value, valueExpression);  el.model = {    value: ("(" + value + ")"),    expression: ("\"" + value + "\""),    callback: ("function (" + baseValueExpression + ") {" + assignment + "}")  };}复制代码

针对我们这个例子而言会生成如下的el.model

当genDirectives执行完毕后,会执行

// component v-model  if (el.model) {    data += "model:{value:" + (el.model.value) + ",callback:" + (el.model.callback) + ",expression:" + (el.model.expression) + "},";  }复制代码

这样父组件的render函数会包含子组件model的配置项,那么在创建子组件vnode阶段,会执行createComponent函数。 然后执行如下的逻辑

// transform component v-model data into props & events  if (isDef(data.model)) {    transformModel(Ctor.options, data);  }复制代码

那么transformModel主要是将model绑定的数据visible添加到data.props中。然后在on中添加data.model.callback.

// transform component v-model info (value and callback) into// prop and event handler respectively.function transformModel (options, data) {  var prop = (options.model && options.model.prop) || 'value';  var event = (options.model && options.model.event) || 'input';(data.props || (data.props = {}))[prop] = data.model.value;  var on = data.on || (data.on = {});  if (isDef(on[event])) {    on[event] = [data.model.callback].concat(on[event]);  } else {    on[event] = data.model.callback;  }}复制代码

其实就相当于父组件将visible通过props传递给子组件的value。然后子组件改变数据的时候,通过派发事件通知父组件更新visible。通过demo演示就是:当我点击按钮的时候,isShow=true 子组件中的span显示。visible = true。

再次点击按钮 isShow = false。 visible = false

可见无论是表单元素的v-model还是组件的v-model。他们的本质就是一种语法糖。

心得体会

公司目前正在使用vue,闲暇之余自己写demo,通过单步调试的方法,一步一步的去理解vue源码。如有不正之处,还请不吝赐教。

转载地址:http://ggsox.baihongyu.com/

你可能感兴趣的文章
我的友情链接
查看>>
zabbix企业应用之监控mysql 5.6版本
查看>>
BGP选路原则与专有命令的研究
查看>>
关于java的引用、C++的指针、引用的深入分析
查看>>
CMD 修改Host文件 BAT
查看>>
linux用户管理的命令及手动添加用户
查看>>
Windows 7 家庭版如何启用Administrator账户
查看>>
android幻灯片效果实现-Gallery
查看>>
node中exports与module.exports的区别
查看>>
redis 安装及php扩展编译安装
查看>>
批量有效地修改package名
查看>>
android或ios app请求参数格式
查看>>
Camera Vision - video surveillance on C#
查看>>
如何理解网络连接中的"3次握手"?
查看>>
使用Dubbo服务出现java.io.IOException: invalid constant type: 18异常解决办法
查看>>
PYKit目录
查看>>
JSON使用总结
查看>>
php-redis中文帮助手册_系统相关_config_eval_evalSha_script...
查看>>
Tomcat Context配置
查看>>
CentOS6.5安装ntopng
查看>>