彻底理解前端依赖收集

作者: 前端大课堂 | 来源:发表于2019-01-19 22:58 被阅读1次

依赖收集是 Vue.js 和 Mobx.js 核心的之一,那么依赖追踪算法如何工作呢?本文将带读者自己动手实现一个依赖收集的库。

早起很多双向绑定的框架,Model和Dom同步更新,或者现在较多场景Model更新时,Dom自动更新。 

例子1: 

data = {

    label: '深圳欢迎你'

}; 

bind(dom, data);  //实现一个bind函数,让dom和data绑定起来

data.label = "北京欢迎你";  // dom 上显示的label就是"北京欢迎你" 

例子2: 

计算属性 sum 

data = {

    num: 3,

    price: 20,

    sum: 60    // sum = num * price    

}

那么每次data.num和data.price改变,自动更新data.sum

这两个例子用Object.defineProperty很好实现。但是耦合度很高。 

function defineReactive (obj, key, val) {

    Object.defineProperty(obj, key, {

            get () { return val },

            set (newValue) {

                // 例子1 这里要写dom更新的代码 

                // 例子2 这里要写sum更新数据的代码 

                val = newValue

            }

    })

}

也就是被依赖者要知道依赖者的存在。 那如果一个源数据被很多方依赖,那么在set部分的代码将越来越多。 

那现在 mobx 和 vuejs 的依赖收集是怎么做的呢?

我们先看个题目,计算某个同学的岁数? 

那么如何实现呢?首先,依赖于当前的年份,其次,依赖于这个同学的出生年份。 

let current = {

    year: 2018

}; 

let student = {

    born: 1995

}; 

我们需要两个函数,defineReactive(obj, key) 和 defineComputed(obj, key, computeFn, updateCallback) 来实现整个过程。 

let agedObj = {    

    age: 0   // 初始的岁数

}; 

defineReactive(current, 'year'); 

defineReactive(student, 'born'); 

defineComputed(agedObj, age, () => current.year - student.born, value => {

    console.log(`this student's aged is ${value}`); 

}); 

// 标记当前正在求值的 computed 函数

let Dep = null

// 定义 computed,需传入求值函数与 computed 更新时触发的回调

function defineComputed (obj, key, computeFn, updateCallback) {

    // 封装供 reactive 收集的更新回调,以触发 computed 的更新事件

    const onDependencyUpdated = function () {

        // 在此调用 computeFn 计算出的值用于触发 computed 的更新事件

        // 供后续可能的 watch 等模块使用

        const value = computeFn()

        updateCallback(value)

    }

    Object.defineProperty(obj, key, {

        get () {

            // 标记当前依赖,供 reactive 收集

            Dep = onDependencyUpdated

            // 调用求值函数,中途收集依赖

            const value = computeFn()

            // 完成求值后,清空标记

            Dep = null

            // 最终返回的 getter 结果

            return value

        },

         // 计算属性无法 set

         set () {}

    })

}

// 通过 getter 与 setter 定义出一个 reactive

function defineReactive (obj, key) {

     let val = obj[key];

     // 在此标记哪些 computed 依赖了该 reactive

     const deps = []

     Object.defineProperty(obj, key, {

         // 为 reactive 求值时,收集其依赖

        get () {

            if (Dep) deps.push(Dep)

             // 返回 val 值作为 getter 求值结果

             return val;

         },

         // 为 reactive 赋值时,更新所有依赖它的计算属性

         set (newValue) {

             // 在 setter 中更新值

             val = newValue

             // 更新值后触发所有 computed 依赖更新

             deps.forEach(changeFn => changeFn())

             }

        })

}

其实原理很简单,就是defineComputed(obj, key, computeFn, updateCallback) 在调用的时候,先把computeFn和updateCallback组成的一个新的函数赋给全局的Dep函数,因为computeFn在执行的时候回去调用依赖对象的字段的get方法。 get 方法在调用的时候,会把Dep函数作为依赖收集起来,在set的时候会把所有依赖函数调用更新。 

相关文章

  • 彻底理解前端依赖收集

    依赖收集是 Vue.js 和 Mobx.js 核心的之一,那么依赖追踪算法如何工作呢?本文将带读者自己动手实现一个...

  • Vue响应式-依赖收集

    依赖收集 为什么要收集依赖 咋样实现收集依赖 通过一个事件发布订阅模式设计模式,然而在这种设计模式去实现依赖收集,...

  • 前端开发面试知识点大纲

    史上最全 前端开发面试问题及答案整理 本文旨在加深对前端知识点的理解,资料来源于网络,由本人收集整理。 前端开发面...

  • iOS多线程的初步研究(四)-- NSTimer

    理解run loop后,才能彻底理解NSTimer的实现原理,也就是说NSTimer实际上依赖run loop实现...

  • Spring 如何解决循环依赖的问题

    转自:彻底理解SpringIOC、DI-这篇文章就够了 先看一个循环依赖问题 现象 循环依赖其实就是循环引用,也就...

  • Node.js 常用工具、依赖和插件

    本文收集了 Node.js 中常用的工具、依赖包和插件等,涵盖了 应用、后端框架、前端框架、UI 组件、工具库、功...

  • 彻底理解参数收集与参数分配

    之前在读python代码时,总会遇到函数里有**kwarg, *param的写法,一直不是很清楚究竟是什么意思。今...

  • mobx VS Redux

    mobx api 简单,样板代码少 redux 需要注意的挺多,mobx 需要对依赖收集理解 Redux 规范 s...

  • 前端收集

    前端收集 在前端路上摸索前行,在这里分享自己长期关注的前端开发相关的优秀网站、博客、以及活跃开发者。欢迎更新,以下...

  • MobX依赖收集

    看了一点点源码,看不懂。。。1.mobx-react observe 在装饰类组价,把component的rend...

网友评论

    本文标题:彻底理解前端依赖收集

    本文链接:https://www.haomeiwen.com/subject/zbsrdqtx.html