摘要:与状态同步非常困难通过添加观察者监测变化,如和。应用中状态的属性会被监测,当它们发生变化时,只有依赖了发生变化属性的元素会被重新渲染。
现代 js 框架存在的根本原因
然而通常人们(自以为)使用框架是因为:
它们支持组件化;
它们有强大的社区支持;
它们有很多(基于框架的)第三方库来解决问题;
它们有很多(很好的)第三方组件;
它们有浏览器扩展工具来帮助调试;
它们适合做单页应用。
Keeping the UI in sync with the state is hard (UI与状态同步非常困难)
通过(添加)观察者监测变化,如 Angular 和 Vue.js。应用中状态的属性会被监测,当它们发生变化时,只有依赖了(发生变化)属性的 DOM 元素会被重新渲染。
1.属性拦截器-初步数据劫持
Object.defineProperty()
let a = {} Object.defineProperty(a, "b", { enumerable: true, configurable: true, set (newValue){ console.log("set") value = newValue }, get (){ console.log("get") return value } }) value = a.b a.b = 1 console.log(a.b)
读a.b或者设置a.b时候触发get和set函数
configurable如果为false,那么不可以修改, 不可以删除.
writable给的说明是如果设置为false,不可以采用 数据运算符,进行赋值
2.想实现一个这样的功能
当我们试图修改 a 的值时:ins.a = 2,在控制台将会打印 "修改了 a’,
乍一看比较简单
考虑到复杂情况,
比如如何避免收集重复的依赖,如何深度观测,如何处理数组以及其他边界条件等等
const ins = new Vue({ data: { a: 1 } }) ins.$watch("a", () => { console.log("修改了 a") })
3.收集依赖, 起码需要一个”筐“
// dep 数组就是我们所谓的“筐” const dep = [] Object.defineProperty(data, "a", { set () { // 当属性被设置的时候,将“筐”里的依赖都执行一次 dep.forEach(fn => fn()) }, get () { // 当属性被获取的时候,把依赖放到“筐”里 dep.push(fn) } }) $watch("a", () => { console.log("设置了 a") })
4.$watch 函数是知道当前正在观测的是哪一个字段的
const data = { a: 1 } const dep = [] Object.defineProperty(data, "a", { set () { dep.forEach(fn => fn()) }, get () { // 此时 Target 变量中保存的就是依赖函数 dep.push(Target) } }) // Target 是全局变量 let Target = null function $watch (exp, fn) { // 将 Target 的值设置为 fn Target = fn // 读取字段值,触发 get 函数 data[exp] }
明白数据响应系统的整体思路,为接下来真正进入 Vue 源码做必要的铺垫
4.observer
observe工厂函数
const data = { a: 1 } const data = { a: 1, // __ob__ 是不可枚举的属性 __ob__: { value: data, // value 属性指向 data 数据对象本身,这是一个循环引用 dep: dep实例对象, // new Dep() vmCount: 0 } }
new Observer(data)
Observer构造函数-调用this.walk(value)-defineReactive(get和set)
get 调用 dep.depend() 在 get 函数中如何收集依赖
set 调用 dep.notify() 通知更新
5.dep
static target; // watcher id; //记录id不能重复收集 subs; //数组,sub收集所以的watcher
dep.notify()实际上是 watcher里的update() 渲染更新
dep.depend()实际上是 watcher里的update() 渲染更新 把watcher实例对象推入subs
6.watcher
vm, vue实例对象
expOrFn, 表达式
cb, 回调函数
当然还在vm上定义了很多其他的computer,watch之类的
收集依赖,要想收集,必须 new watcher()
get () { pushTarget(this) } addDep (){ dep.addSub(this) // 把watcher自己加入dep.subs数组 } update(){ queueWatcher() //排队渲染 }
6.总结
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/97375.html
摘要:执行的时候,会绑定上下文对象为组件实例于是中的就能取到组件实例本身,的代码块顶层作用域就绑定为了组件实例于是内部变量的访问,就会首先访问到组件实例上。其中的获取,就会先从组件实例上获取,相当于。 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得...
摘要:接下来要看看这个订阅者的具体实现了实现订阅者作为和之间通信的桥梁,主要做的事情是在自身实例化时往属性订阅器里面添加自己自身必须有一个方法待属性变动通知时,能调用自身的方法,并触发中绑定的回调,则功成身退。 本文能帮你做什么?1、了解vue的双向数据绑定原理以及核心代码模块2、缓解好奇心的同时了解如何实现双向绑定为了便于说明原理与实现,本文相关代码主要摘自vue源码, 并进行了简化改造,...
摘要:储存订阅器因为属性被监听,这一步会执行监听器里的方法这一步我们把也给弄了出来,到这一步我们已经实现了一个简单的双向绑定了,我们可以尝试把两者结合起来看下效果。总结本文主要是对双向绑定原理的学习与实现。 当今前端天下以 Angular、React、vue 三足鼎立的局面,你不选择一个阵营基本上无法立足于前端,甚至是两个或者三个阵营都要选择,大势所趋。 所以我们要时刻保持好奇心,拥抱变化,...
摘要:所以无需太过介怀是实现的单向或双向绑定。监听数据绑定更新函数的处理是在这个方法中,通过添加回调来接收数据变化的通知至此,一个简单的就完成了,完整代码。 本文能帮你做什么?1、了解vue的双向数据绑定原理以及核心代码模块2、缓解好奇心的同时了解如何实现双向绑定为了便于说明原理与实现,本文相关代码主要摘自vue源码, 并进行了简化改造,相对较简陋,并未考虑到数组的处理、数据的循环依赖等,也...
阅读 3479·2021-10-13 09:39
阅读 1461·2021-10-08 10:05
阅读 2263·2021-09-26 09:56
阅读 2282·2021-09-03 10:28
阅读 2679·2019-08-29 18:37
阅读 2037·2019-08-29 17:07
阅读 605·2019-08-29 16:23
阅读 2196·2019-08-29 11:24