1. 首页
  2. Vuejs

Vue3源码学习(问题总结)

本篇文章会一直更新,直到把vue源码看完,有啥问题就可以在评论中写上一起研究。

小黄鸭调试法

最近发现了一个及其有用的终极调试方法,就是小黄鸭调试法。 小黄鸭调试法: 传说中的大师都是随身携带小黄鸭的,遇到bug就向小黄鸭解释每一行代码的用处,可能讲到一半就灵光乍现,bug灰飞烟灭。
本来只看vue源码的时候感觉遇到了很多问题,把这些问题记录下来,然后带着问题学习源码就感觉更加清晰。

差异

vue2中使用了Object.defineProperty进行数据劫持,对于数组进行单独处理,修改数组原型上的方法


if (Array.isArray) { this.observeArray(value) } else { this.walk(value) }

Vue3使用了Proxy进行数据劫持,优异于definePropertyProxy能够对数组修改进行监听。在Vue2中如果使用list[index]=5这种方式是无法监听到的,Proxy解决了这种问题,不仅如此proxy除了提供setget还提供了hasdeleteProperty等等。
Vue2中,对于我们在data中所定义的数据,在Vue初始化的时候,会遍历data中所有的数据进行响应式。

JS中文网 – 前端进阶资源教程 www.javascriptC.com
一个致力于帮助开发者用代码改变世界为使命的平台,每天都可以在这里找到技术世界的头条内容


function defineReactive(obj, key, val) { /* 对象的子对象递归进行observe并返回子节点的Observer对象 */ let childOb = observe(val) }

data中绑定的数据足够大的时候,有些数据可能没有用到,就能造成不必要的性能损失。在Vue3中,就避免了这种问题,在Vue初始化的时候只会对data这一层加上proxy,当获取其中数据的时候,才会为对应的数据添加响应式


instance.data = reactive(data); function reactive(target, handler) { observed = new Proxy(target, handler) } function createGetter() { return function get(target, key) { let res = Reflect.get(target, key) return isObject(res) ? reactive(res) : res; } }

例如


data: { status: { change: true, } }

只有当使用status.change的时候,才会创建对应的proxy

proxy的一些问题

多次触发set


arr = [] arrProxy = new Proxy(arr, { get: (target, prop) => { console.log(prop, 'get'); return Reflect.get(target, prop) }, set: (target, prop, value) => { console.log(prop, 'set') Reflect.set(target, prop, value) return true } }) arrProxy.push(1)

Vue3源码学习(问题总结)

把这段代码在chrome中执行,发现他会触发两次setget操作。把set中的console看成render函数,那么这个操作就会触发多次render,这显然是不合理的。在vue中主要是用下面方式进行解决:


function set(target, key) { const hadKey = hasOwn(target, key); const oldValue = target[key]; if (!hadKey) { console.log('trigger') } else if (val !== oldValue) { console.log('trigger') } }

可以看到,现在只会执行一次trigger,因为设置length是后执行的,但是这时候数组的长度已经更改了,所以后面的程序不会执行了。

只能监听到第一层


obj = { first: { inner: 3 } } proxyObj = new Proxy(obj, { get: (target, prop) => { console.log(prop, 'get'); return Reflect.get(target, prop) }, set: (target, prop, value) => { console.log(prop, 'set') Reflect.set(target, prop, value) return true } }) proxyObj.first.inner // first get proxObj.first.inner = 4 // first get

当获取inner属性值或者设置的时候,只会触发到第一层,所以子对象需要我们自己去实现


function get(target, key) { const res = Reflect.get(target, key, receiver); return isObject(res) ? reactive(res) : res; }

WeakMap

WeakMapes6中新加的一种数据类型,它和Map有何区别


const m = new Map(); m.set('key', 'value') const n = new WeakMap(); let obj = {}; n.set(obj, 'value') obj = null; n.has(obj);

Map能够使用字符串来作为key,但是WeakMap只能让对象来作为keyWeakMap最大的优势是能够避免内存泄漏,例如在上面例子中是了,使用了obj作为WeakMapkey,当把obj置为null的时候,这时候WeakMap是无法获取到对应的value值,意思是这个数据已经被垃圾回收器回收掉。

effect的作用

effect.spec.ts


let dummy const counter = reactive({ num: 0 }) effect(() => (dummy = counter.num)) expect(dummy).toBe(0) counter.num = 7; expect(dummy).toBe(7)

从上面可以看出effect收到一个回调函数,并且立即执行。当触发counterset操作的时候,在执行一遍effect中的函数,下面是effect的实现:


function effect(fn, options) { // ... const effect = createReactiveEffect(fn, options); if (!options.effect) { effect(); } return effect; }

effect就是对回调函数进行一次包装,并在包装的过程中进行一些操作,实际就是依赖收集,最后调用run方法


function run(effect, fn, args) { // ... try { effectStack.push(effect); return fn(...args) } finally { effectStack.pop(); } }

这里就像vue2中的watcher实现,effectStack是一个全局变量,当发布者data中的数据触发get操作的时候进行依赖收集,当触发set操作的时候,通知所有的订阅者更新。

Log

  1. 12-4 算是大概看完了响应式的部分
    > 作者:B_Cornelius
    > 链接:https://juejin.im/post/6844904013368934413

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「IT平头哥联盟」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:Vue3源码学习(问题总结)

链接:https://www.javascriptc.com/4079.html

« 尤雨溪:Vue3的设计过程
Vue3源码解读(三)-patch»
Flutter 中文教程资源

相关推荐

QR code