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

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

内容简介:作者:呆恋小喵我的后花园:我的 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 源码解读-数据响应系统》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

Collective Intelligence实战

Collective Intelligence实战

阿拉克 / 2010-9 / 58.00元

《Collective Intelligence实战》内容简介:在互联网上,利用用户的集体智慧是成功的关键。集体智慧是一种新兴的编程技术,可让您从人们访问web和与web交互的过程中找到有价值的模式、发现这些访问者之间的关系和确定他们的个人偏好及习惯等。《collective Intelligence实战》首先介绍了集体智慧的原则和构建更具交互性网站的思想,然后通过示例开发了一个直接可用的基于Ja......一起来看看 《Collective Intelligence实战》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具