写文章不容易,点个赞呗兄弟 专一 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工做原理,源码版助于了解内部详情,让咱们一块儿学习吧 研究基于 Vue版本 【2.5.17】bash
若是你以为排版难看,请点击 下面连接 或者 拉到 下面关注公众号也能够吧dom
说到 Vue,感受第一印象就是双向绑定,因此v-model键值是Vue印象的半壁江山啊,这么重要的东西,你好歹要知道是怎么实现的吧学习
咱们今天就来说解双向绑定的工做原理,你应该知道,双向绑定就是经过绑定 dom 事件来实现的,但是,怎么绑定的事件,绑定什么事件?ui
首先,双向绑定,我我的认为应该分为 初始化绑定 和 双向更新 两part。this
初始化绑定,就是初始化时给表单元素绑定值,绑定事件,为双向更新作准备spa
双向更新,就是任意一边变化,同时能让另外一个边更新3d
双向更新那是后话,只有一开始时成功执行绑定操做才会有以后 双向更新这个东西,因此,今天按顺序来了解两个part,从四个问题开始双向绑定
一、v-model 怎么给表单绑定数据code
二、v-model 绑定什么事件
三、v-model 怎么绑定事件
四、v-model 如何进行双向更新
TIP
v-model 还能够用在 自定义组件上,可是很明显,此次咱们先不讲这一块,而是先将正常的表单使用
咱们先以 input text 类型讲解,对于其余的表单元素,流程都差很少,只是中间涉及的内容不一样。因此就先讲个例子,而后具体在源码版所有一块儿说
一、怎么赋值?v-model 绑定的数据赋值给表单元素的 value 属性
二、怎么绑定事件?解析不一样表单元素,配置相应的事件名和事件回调,在插入dom以前,addEventListener 绑定上事件
三、怎么双绑?外部变化,触发事件回调,event.target.value 赋值给model绑定的数据;内部变化,修改表单元素属性 value
看完结论,有点懵?咱们来看看具体的内容,结果导向来进行学习
下面的讲解如下面这个为例
获取值流程
首先,上面例子解析后的渲染函数是下面这样(已简化,只保留表单值相关)
(function(){
with(this){
return _c('div',[
_c('input',
domProps:{"value":name}
)
])
}
})
复制代码
一、这个渲染函数是没有执行的 匿名函数。执行的时候,会绑定上下文对象为 组件实例
二、因而 with(this) 中的 this 就能取到 组件实例自己,with 的代码块 顶层做用域 就绑定为了 组件实例
三、因而 with 内部变量的访问,就会首先访问到 组件实例上。其中 name 的 获取,就会先从 组件实例上获取,至关于 vm.name。赋值完成后,domProps 就是下面这样
{ domProps:{value:111} }
复制代码
四、上面的 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,会绑定不一样的 事件
change 事件
select,checkbox,radio
input 事件
这是默认事件,当不是上面三种表单元素时,会解析成 input 事件
好比 text、number 等 input 元素和 textarea
TIP
实际上关于 input 和 textarea 绑定的事件远不止 input 一个事件,可是涉及内容太多,打算放在源码版说明,这里先简单默认是 input 事件
上面例子解析成下面的渲染函数(已简化,只保留事件相关)
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 绑定了 name ,name 会收集到 本组件的 watcher
a. 下面渲染函数执行时,会绑定自己组件实例为上下文对象
b. name 访问的是 组件实例的 name
c. name 此时便收集到了 当前正在渲染的组件实例,当前渲染的实例是本身,因而收集到了自身的 watcher
(function(){
with(this){
return _c('div',[_c('input',
domProps:{"value":(name)})
])
}
})
复制代码
若是你不懂 收集 watcher 什么意思,建议看下个人另外一篇文章
【Vue原理】响应式原理 - 白话版
二、内部修改 name 变化,通知收集器内的 watcher 更新,因此本组件会更新,上面的渲染函数从新执行,便 从新从实例读取 name
三、从新给 input dom 的 value 赋值,因而 页面就更新了
怎么赋值?那是回到第一个问题了,兄弟
外部变化
手动改变表单,事件触发,执行事件回调(下面这个),更新组件数据 name
function($event) {
name = $event.target.value
}
复制代码
更新内部数据流程
一、当事件触发的时候,会把 表单的值 赋值给 name
二、name 是从 组件实例上访问的
三、因此此次赋值会 直接改变组件实例的 name
回调怎么赋值给组件实例的name
一开始不懂,因此不理解,也没查到,写了个例子,大概理解了意思
一、由于事件回调 在 with 里面声明
二、因而事件回调的 做用域链最顶层 就加上了一层 with 绑定的做用域
三、就算事件回调不在 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 三要素
一、绑定属性
二、绑定事件
三、属性+事件组合完成双向更新