Vue 源码解读-数据响应系统

栏目: JavaScript · 发布时间: 7年前

内容简介:作者:呆恋小喵我的后花园:我的 github:

data 对象初始化

  • 通过 vm.$options.data 函数获取 data 对象

  • 校验 data 对象是否为 纯对象

  • 检验 data 对象与 props 对象 冲突键

  • 检验 methods 对象与 data 对象 冲突键

  • 在 Vue 实例添加 代理 访问 data 对象的同名属性

  • 调用 observe 函数开启 响应式

数据响应系统

  • 避免收集 重复 依赖

  • 深度 观测

  • 处理 边界 条件

observe 工厂函数

export function observe (value: any, asRootData: ?boolean): Observer | void {
    if (!isObject(value) || value instanceof VNode) {
        return
    }
    let ob: Observer | void
    // 避免重复观测
    if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
        ob = value.__ob__
    } else if (
        // 是否开启观测
        shouldObserve &&
        // 是否服务端渲染
        !isServerRendering() &&
        // 待观测对象是否为数组或纯对象
        (Array.isArray(value) || isPlainObject(value)) &&
        // 待观测对象是否可扩展
        Object.isExtensible(value) &&
        // 避免观测 Vue 实例
        !value._isVue
    ) {
        ob = new Observer(value)
    }
    if (asRootData && ob) {
        ob.vmCount++
    }
    return ob
}

observer 构造函数

export class Observer {
    // 待观测对象
    value: any;
    // 待观测对象依赖收集“筐”
    dep: Dep;
    vmCount: number;
    constructor (value: any) {
        this.value = value
        this.dep = new Dep()
        this.vmCount = 0
        // 为待观测对象定义不可枚举 __ob__ 属性
        def(value, '__ob__', this)
        // 处理数组
        if (Array.isArray(value)) {
            const augment = hasProto
                ? protoAugment
                : copyAugment
            // 调用变异函数时执行依赖
            augment(value, arrayMethods, arrayKeys)
            // 开启深度观测
            this.observeArray(value)
        // 处理纯对象
        } else {
            this.walk(value)
        }
    }
    walk (obj: Object) {
        const keys = Object.keys(obj)
        for (let i = 0; i < keys.length; i++) {
            defineReactive(obj, keys[i])
        }
    }
    observeArray (items: Array<any>) {
        for (let i = 0, l = items.length; i < l; i++) {
            observe(items[i])
        }
    }
}

defineReactive 函数

get 函数

  • 返回 属性值

  • 收集 依赖

set 函数

  • 设置 属性值

  • 触发 依赖

export function defineReactive (
    obj: Object,
    key: string,
    val: any,
    customSetter?: ?Function,
    shallow?: boolean
) {
    // 待观测对象依赖收集“筐”
    const dep = new Dep()
    // 属性描述对象
    const property = Object.getOwnPropertyDescriptor(obj, key)
    // 属性描述对象是否可配置
    if (property && property.configurable === false) {
        return
    }
    // 缓存原 setter、getter
    const getter = property && property.get
    const setter = property && property.set
    if ((!getter || setter) && arguments.length === 2) {
        val = obj[key]
    }
    // 开启深度观测
    let childOb = !shallow && observe(val)
    // 重定义 setter、getter
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
            // 保证原 getter 正常运作且返回属性值
            const value = getter ? getter.call(obj) : val
            // 依赖是否存在
            if (Dep.target) {
                // 收集依赖至 dep “筐”,属性值被修改时执行依赖
                dep.depend()
                if (childOb) {
                    // 收集依赖至 childOb.dep “筐”,添加新属性时执行依赖
                    childOb.dep.depend()
                    if (Array.isArray(value)) {
                        // 因数组索引无 get,逐个收集每一数组元素依赖
                        dependArray(value)
                    }
                }
            }
            return value
        },
        set: function reactiveSetter (newVal) {
            // 获取原属性值
            const value = getter ? getter.call(obj) : val
            // 属性值是否更新
            if (newVal === value || (newVal !== newVal && value !== value)) {
                return
            }
            // 非生产环境打印辅助信息
            if (process.env.NODE_ENV !== 'production' && customSetter) {
                customSetter()
            }
            // 保证原 setter 正常运作且设置属性值
            if (setter) {
                setter.call(obj, newVal)
            } else {
                val = newVal
            }
            // 开启深度观测
            childOb = !shallow && observe(newVal)
            // 执行依赖
            dep.notify()
        }
    })
}

数组变异函数拦截

import { def } from '../util/index'
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
    'push',
    'pop',
    'shift',
    'unshift',
    'splice',
    'sort',
    'reverse'
]
methodsToPatch.forEach(function (method) {
    // 缓存原变异函数
    const original = arrayProto[method]
    // 定义与变异函数同名的拦截函数
    def(arrayMethods, method, function mutator (...args) {
        // 调用原变异函数
        const result = original.apply(this, args)
        const ob = this.__ob__
        let inserted
        switch (method) {
            case 'push':
            case 'unshift':
                inserted = args
                break
            case 'splice':
                inserted = args.slice(2)
                break
        }
        // 观测新增元素
        if (inserted) ob.observeArray(inserted)
        // 执行依赖
        ob.dep.notify()
        return result
    })
})

作者:呆恋小喵

我的后花园: https://sunmengyuan.github.io/garden/

我的 github: https://github.com/sunmengyuan

原文链接: https://sunmengyuan.github.io/garden/2019/02/01/vue-observer.html


以上所述就是小编给大家介绍的《Vue 源码解读-数据响应系统》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

C++ Primer 中文版(第 5 版)

C++ Primer 中文版(第 5 版)

[美] Stanley B. Lippman、[美] Josée Lajoie、[美] Barbara E. Moo / 王刚、杨巨峰 / 电子工业出版社 / 2013-9-1 / CNY 128.00

这本久负盛名的 C++经典教程,时隔八年之久,终迎来史无前例的重大升级。除令全球无数程序员从中受益,甚至为之迷醉的——C++ 大师 Stanley B. Lippman 的丰富实践经验,C++标准委员会原负责人 Josée Lajoie 对C++标准的深入理解,以及C++ 先驱 Barbara E. Moo 在 C++教学方面的真知灼见外,更是基于全新的 C++11标准进行了全面而彻底的内容更新。......一起来看看 《C++ Primer 中文版(第 5 版)》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

MD5 加密
MD5 加密

MD5 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具