1. 前言
Vue的双向绑定一直是其核心,在这里我们将通过Vue源码来了解双向绑定的原理。
vue版本:2.6.11
vue仓库:https://github.com/vuejs/vue
vue文档:https://cn.vuejs.org/
2. Observer 观察者模式
简单来讲,观察者模式(Observer Pattern)适用于对象间存在一对多关系。比如,当一个对象被修改时,会自动通知依赖它的对象(事件)。
Vue的双向绑定就是采用此模式进行的。当某个对象的值被更改时,依赖(引用)此值的对象或事件都会收到通知。
观察者模式的更多信息可参考:https://www.runoob.com/design-pattern/observer-pattern.html
Vue代码中创建了 Observer类、Dep类、Watcher类来构建观察者模式。
2.1 Observer类
说明:深度遍历data内的成员,添加观察者模式,具体为以下操作:
①当成员类型为Object或Array时,在根节点创建一个 __ob__ 成员,指向一个初始化的Observer类的实例。
②当成员不是Object和Array时,封装其set和get属性,在2个属性的内部增加对Dep支持,用来支持观察者模式。当进行get操作时触发depend(),进行set操作时触发notify()。
2.2 Dep类
说明:在每一个需要进行绑定(单项或双向)的js对象(非Object和Array),Vue都在其对象内创建了一个Dep类的实例,此Dep实例用于管理当前js对象与订阅者的关系。
当进行get操作时触发Dep的depend(),其内部逻辑为此
进行set操作时触发Dep的notify(),在内部遍历订阅者集合,调用每个订阅者的update()方法。
主要成员:
subs[]:管理一个订阅者集合。
addSub():添加订阅者;
removeSub():移除订阅者;
notify():遍历订阅者数组,调用每个订阅者的update()方法。
depend():添加dep与sub(订阅者)的互相依赖。sub(订阅者)的dep集合添加此dep,dep实例的sub集合添加相关订阅者。
2.3 Watcher类
说明:Watcher类,即订阅者,在Vue中扮演的角色是当监听到data的值变更时,进行相关工作,比如修改HTML代码、getter、watch api等等。
3. 什么是data
在Vue官方教程对data解释如下:data为Vue 实例的数据对象。Vue 将会递归将 data 的 property 转换为 getter/setter,从而让 data 的 property 能够响应数据变化。
对象必须是纯粹的对象 (含有零个或多个的 key/value 对);浏览器 API 创建的原生对象和原型链上的 property 会被忽略。大概来说,data 应该只能是数据。
首先看个简单例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <body> <div id= "app" > <p>姓名:<input v-model= "userName" /></p> <p>年龄:<input v-model= "age" /></p> <span>{{userName}}</span> </div> <script> var VM = new Vue({ data: { userName: '' , age: '' , likes: [ '语文' , '数学' ] }, el: '#app' }) </script> </body> |
4. data是如何初始化的
通过Vue源码,data在Vue初始化时经过了以下几个步骤:
4.1 initData(vm)
说明:这一步中,Vue会遍历data的成员,分别绑定到Vue实例的_data成员和根节点上。
其中根节点data成员的set和get属性都是与_data相关。
示例:
4.2 observe(data)
同 #2.1 Observer类 的解释:深度遍历data内的成员,添加观察者模式,具体为以下操作:
①当成员类型为Object或Array时,在根节点创建一个 __ob__ 成员,指向一个初始化的Observer类的实例。
②当成员不是Object和Array时,封装其set和get属性,在2个属性的内部增加对Dep支持,用来支持观察者模式。当进行get操作时触发depend(),进行set操作时触发notify()。
5. HTML是如何跟data联动的
Vue源码对HTML处理的步骤如下:
5.1 $mount(el)
说明:获取初始化Vue对象的el成员指定的HTML。
5.2 parse(html)
说明:将HTML代码转换为AST对象。AST即抽象语法树,这里不做深入讲解,感兴趣的可自行搜索。
在这一步中,会得到HTML元素内的属性,包括Vue中的指令。
5.3 generate(ast)
说明:深度遍历AST对象,生成render方法。
注意:此时render为string类型。(这里方便展示进行了代码格式化)
5.4 callHook(vm, 'beforeMount')
说明:触发beforeMount事件的回调。
5.5 new Watcher
说明:创建一个Watcher类的实例,并赋值给Vue实例的_watcher成员。
Watcher类,即监听者类,在Vue中扮演的角色是当监听到data的值变更时,进行相关工作,比如修改HTML代码、getter、watch api等等。
5.6 vm._render()
说明:调用实例的render()转换为VNode。
这一块比较复杂,其最终结果是把AST tree → 转换为VNode tree。
5.7 vm._update()
说明:新生成的VNode替换旧的VNode,替换算法就是diff算法。而在初始化时,原始html代码会转换为一个空VNode
关于diff算法的了解可看此篇文章:
Vue的diff算法解析:https://www.infoq.cn/article/uDLCPKH4iQb0cR5wGY7f
5.8 callHook(vm, 'mounted')
说明:触发mounted事件的回调。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 单线程的Redis速度为什么快?
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库