zoukankan      html  css  js  c++  java
  • 《深入浅出vue.js》阅读笔记之(object)变化侦测

    1、什么是变化侦测?

    通常,在运行时应用内部的状态会不断发生变化,此时需要不停地重新渲染页面,这时如何确定状态中发生了什么变化?

    变化侦测就是用来解决这个问题的,它分为两种类型,一种是“推”(push),另一种是拉(pull)。

    angular和react的变化侦测属于“拉”,即当状态发生变化时,它不知道那个状态变了,只知道状态可能变了,然后会发送一个信号通知框架,框架内部收到信号后,就会进行对比找出哪些dom需要重新渲染,在angular中称为脏检查,在react中则使用虚拟dom。

    而vue框架的变化侦测属于“推”。当状态发生变化时,vue立刻就知道了,而且在一定程度上知道哪些状态变了。因此,它知道的信息更多,也就可以进行更细粒度的更新。

    所谓细粒度的更新,也就是说,假如一个状态绑定了好几个依赖,每个依赖表示一个具体的dom节点,那么当这个状态发生变化时,向这个状态的所有依赖发送通知进行dom更新操作,也可以看出“拉”的粒度是最粗的。

    2、如何追踪变化?

    两种方法,Object.defineProperty和es6的Proxy。由于es6在浏览器中的支持度并不理想,所以vue使用的是第一种。

    知道了Object.defineProperty可以侦测对象的变化,那就可以写出以下的代码:

    function defineReactive (data, key, val) {
        Object.defineProperty(data, key, {
            enumerable: true,
            configurable: true,
            get: function () {
                return val
            },
            set: function (newval) {
                if (val === newval) {
                    return
                }
                val = newval
            }
        })
    }

    该函数作用是定义一个响应式数据,也就是在这个函数中进行追踪变化,当从data的key中读取数据时,get函数被触发;当从data的key中设置数据时,set函数被触发。

    3、如何收集依赖?

    我们之所以要观察数据,目的是当数据的属性发生变化时,可以通知那些曾经使用了该数据的地方。

    例如:

    <template>
        <h1>{{name}}</h1>
    </template>

    该模版中使用了name,当它发生变化时,要向使用了它的地方发送通知。

    实现的方法是,把用到数据name的地方收集起来,然后属性发生变化时,把之前收集好的依赖循环触发一遍。也就是在getter中收集依赖,setter中触发依赖。

    4、依赖收集在哪里?

    首先想到的是每个key都有一个数组,用来存储当前key的依赖。假设依赖是一个函数,保存在window.target上,现在可以把上面的defineReactive函数改造下:

    function defineReactive (data, key, val) {
        let dep = [] //new
        Object.defineProperty(data, key, {
            enumerable: true,
            configurable: true,
            get: function () {
                dep.push(window.target)
                return val
            },
            set: function (newval) {
                if (val === newval) {
                    return
                }
                // new
                for (let i=0;i<dep.length;i++) {
                    dep[i](newval, val)
                }
                val = newval
            }
        })
    }

    这里新增了数组dep,用来存储被收集的依赖,然后在set被触发时,循环dep以触发收集到的依赖。

    这里可以专门封装一个帮助我们管理依赖的类,用来收集、删除和发送通知的类:

    export default class dep {
        constructor () {
            this.subs = []
        }
    
        addSub (sub) {
            this.subs.push(sub)
        }
    
        removeSub (sub) {
            remove(this.sub, sub)
        }
    
        depend () {
            if (window.target) {
                this.addSub(window.target)
            }
        }
    
        notify () {
            const subs = this.subs.slice()
            for (let i=0,l = subs.length;i<1;i++) {
                subs[i].update()
            }
        }
    }
    
    function remove (arr, item) {
        if (arr.length) {
            const index = arr.indexOf(item)
            if (index > -1) {
                return arr.splice(index, 1)
            }
        }
    }

    再改造下之前的函数:

    function defineReactive (data, key, val) {
        let dep = new dep() //update
        Object.defineProperty(data, key, {
            enumerable: true,
            configurable: true,
            get: function () {
                dep.depend()
                return val
            },
            set: function (newval) {
                if (val === newval) {
                    return
                }
                val = newval
                // new
                dep.notify()
            }
        })
    }

    这样就清晰很多了

  • 相关阅读:
    取得元素节点的默认display值
    mass Framework emitter模块 v2
    memset函数详细说明
    八大排序算法总结
    电脑很卡,怎么办?这里帮你解决
    Android APK反编译详解(附图)
    java环境变量配置
    如何使用U盘安装操作系统 安装GHOST XP, xp纯净版
    c# WinForm开发 DataGridView控件的各种操作总结(单元格操作,属性设置)
    Js apply 方法 详解
  • 原文地址:https://www.cnblogs.com/pjl43/p/10632587.html
Copyright © 2011-2022 走看看