【Vue原理】VModel - 白话版

↑点击上方 “悲伤日记” 持续关注我 

专注 Vue 源码分享,为了方便大家理解,分为了白话版和 源码版,白话版让大家可以轻松理解工作原理,源码版让大家更清楚内部操作和 Vue的美,喜欢我就关注我,好吧兄弟,不会让你失望的

说到 Vue,感觉第一印象就是双向绑定,所以v-model键值是Vue印象的半壁江山啊,这么重要的东西,你好歹要知道是怎么实现的吧

我们今天就来讲解双向绑定的工作原理,你应该知道,双向绑定就是通过绑定 dom 事件来实现的,可是,怎么绑定的事件,绑定什么事件?

首先,双向绑定,我个人认为应该分为 初始化绑定双向更新 两part。

初始化绑定,就是初始化时给表单元素绑定值,绑定事件,为双向更新做准备

双向更新,就是任意一边变化,同时能让另一个边更新

双向更新那是后话,只有一开始时成功执行绑定操作才会有之后 双向更新这个东西,所以,今天按顺序来了解两个part,从四个问题开始

1、v-model 怎么给表单绑定数据

2、v-model 绑定什么事件

3、v-model 怎么绑定事件

4、v-model 如何进行双向更新

TIP

v-model 还可以用在 自定义组件上,但是很明显,这次我们先不讲这一块,而是先将正常的表单使用

先看结论

我们先以 input text 类型讲解,对于其他的表单元素,流程都差不多,只是中间涉及的内容不同。所以就先讲个例子,然后具体在源码版全部一起说

1怎么赋值?v-model 绑定的数据赋值给表单元素的 value 属性

2怎么绑定事件?解析不同表单元素,配置相应的事件名和事件回调,在插入dom之前,addEventListener 绑定上事件

3怎么双绑?外部变化,触发事件回调,event.target.value 赋值给model绑定的数据;内部变化,修改表单元素属性 value

看完结论,有点懵?我们来看看具体的内容,结果导向来进行学习

下面的讲解以下面这个为例

v-model 怎么给表单绑定数据

获取值流程

首先,上面例子解析后的渲染函数是下面这样(已简化,只保留表单值相关)

(function(){    
    with(this){  
        return _c('div',[
            _c('input',
                domProps:{"value":name}
            )
        ])
    }})

1这个渲染函数是没有执行的 匿名函数。执行的时候,会绑定上下文对象为 组件实例

2于是 with(this) 中的 this 就能取到 组件实例本身,with 的代码块 顶层作用域 就绑定为了 组件实例

3于是 with 内部变量的访问,就会首先访问到 组件实例上。其中 name 的 获取,就会先从 组件实例上获取,相当于 vm.name。赋值完成后,domProps 就是下面这样

{ domProps:{value:111} }

4上面的 value 是 v-model 解析成的原生属性,保存在属于该节点 input 的  domProps 对象存储器中

绑定值流程

创建dom input 之后,插入dom input 之前,遍历该 input 的  domProps ,逐个添加给 input dom

简化后的代码像下面这样进行赋值

for(var i in domProps){    input[i]=domProps[i]}

其中给节点赋值就是这样

input.value = 111

v-model 绑定什么事件

不同的表单元素使用v-model,会绑定不同的 事件

change 事件

select,checkbox,radio

input 事件

这是默认事件,当不是上面三种表单元素时,会解析成 input 事件

比如 text、number 等 input 元素和 textarea 

TIP

实际上关于 input 和 textarea 绑定的事件远不止 input 一个事件,但是涉及内容太多,打算放在源码版说明,这里先简单默认是 input 事件

v-model 怎么绑定事件

上面例子解析成下面的渲染函数 (已简化,只保留事件相关)

with(this) {    
    return _c('div', [
        _c('input', {        
            on: {            
              "input": function($event) {
                  name = $event.target.value              }            }        }
    )])
}

解析事件流程

1解析不同表单元素,配置不同的事件

2拼装 事件回调函数,不同表单元素,回调不一样

3把 事件名和拼装回调 配套 保存给相应的表单元素的 on 事件存储器

什么时候绑定事件

生成 input dom 之后,插入input dom 之前

怎么绑

使用之前保存的 事件名和 事件回调,给 input dom 像下面这样绑定上事件

input.addEventListener("input",function($event) {        name = $event.target.value})

v-model 如何进行双向更新

双向,指的是 外部和内部

外部变化:用户手动改变表单值,输入或者选择

内部变化:从内部修改绑定值

内部变化

1v-model 绑定了 name ,name 会收集到 本组件的 watcher

a. 下面渲染函数执行时,会绑定本身组件实例为上下文对象 

b. name 访问的是 组件实例的 name 

c. name 此时便收集到了 当前正在渲染的组件实例,当前渲染的实例是自己,于是收集到了自身的 watcher

(function(){    
  with(this){  
    return _c('div',[_c('input',
        domProps:{"value":(name)})
    ])
  }})

如果你不懂 收集 watcher 什么意思,建议看下我的另一篇文章

【Vue原理】响应式原理 - 白话版

2内部修改 name 变化,通知收集器内的 watcher 更新,所以本组件会更新,上面的渲染函数重新执行,便 重新从实例读取 name

3重新给 input dom 的 value 赋值,于是 页面就更新了

怎么赋值?那是回到第一个问题了,兄弟

外部变化

手动改变表单,事件触发,执行事件回调(下面这个),更新组件数据 name

function($event) {        name = $event.target.value}

更新内部数据流程

1当事件触发的时候,会把 表单的值 赋值给 name

2name 是从 组件实例上访问的

3所以这次赋值会 直接改变组件实例的 name

回调怎么赋值给组件实例的name?

一开始不懂,所以不理解,也没查到,写了个例子,大概理解了意思

1因为事件回调 在 with 里面声明

2于是事件回调的 作用域链最顶层 就加上了一层 with 绑定的作用域

3就算事件回调不在 with 中执行,事件回调中的 变量访问,也会先访问之前 with 绑定过的作用域,因为作用域链的最顶层

with举栗子

var name=22
var a={name:"a"}
with(a){    
    var fn=function(){        
        console.log(name)
    }}fn()

你认为 fn 执行的时候,会打印出什么呢?行了,给你看结果了

好吧,再一次深刻认识到一个知识点,with 绑定作用域原来这么强,离开with执行,还是先访问他的作用域,脱离不出魔爪啊,强盗作用域

回来总结!

于是当事件回调执行的时候,会 直接赋值 给 组件实例的name,这样便通过外部改变了内部数据

TIP

外部变化,本来可能会存在一种情况

a、手动修改表单后, 回调内会更新组件的值

b、组件的值更新之后,会通知组件更新,组件更新时,便又会重新把input 赋值一遍

非常多余的一步操作,所以这里,Vue做一个判断,判断旧值和 新值是否相等,不等才更新,关于旧值,会保存在 dom 的 _value 属性

总结

v-model 三要素

1、绑定属性

2、绑定事件

3、属性+事件组合完成双向更新

最后

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值