• vue2 源码解析(四)data数据响应式


    代码分析

    文件:vue\src\core\instance\init.js

    1. export function initMixin (Vue: Class<Component>) {
    2. Vue.prototype._init = function (options?: Object) {
    3. ...
    4. // 初始化过程
    5. vm._self = vm
    6. initLifecycle(vm) // 初始化相关生命周期属性 $children、$root、$children、$refs
    7. initEvents(vm) // 自定义事件监听
    8. initRender(vm) // 插槽解析($slots)。 render(h)方法里的h:_c() 和 $createElement()
    9. callHook(vm, 'beforeCreate') // 生命周期钩子:beforeCreate
    10. // 初始化组件各种状态、响应式处理
    11. initInjections(vm) // resolve injections before data/props
    12. initState(vm) // props、methods、data、computed、watch
    13. initProvide(vm) // resolve provide after data/props
    14. callHook(vm, 'created')// 生命周期钩子:created
    15. }
    16. }

    initState(vm) 函数中执行,继续往下

    文件:vue\src\core\instance\state.js

    1. export function initState (vm: Component) {
    2. vm._watchers = []
    3. const opts = vm.$options
    4. // props、methods、data处理,优先级p》m》d
    5. if (opts.props) initProps(vm, opts.props)
    6. if (opts.methods) initMethods(vm, opts.methods)
    7. if (opts.data) {
    8. initData(vm)
    9. } else {
    10. observe(vm._data = {}, true /* asRootData */)
    11. }
    12. if (opts.computed) initComputed(vm, opts.computed)
    13. if (opts.watch && opts.watch !== nativeWatch) {
    14. initWatch(vm, opts.watch)
    15. }
    16. }

    找到重点函数:initData(vm)

    1. function initData (vm: Component) {
    2. let data = vm.$options.data
    3. // 判断data是函数还是其他
    4. data = vm._data = typeof data === 'function'
    5. ? getData(data, vm)
    6. : data || {}
    7. if (!isPlainObject(data)) {
    8. data = {}
    9. process.env.NODE_ENV !== 'production' && warn(
    10. 'data functions should return an object:\n' +
    11. 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
    12. vm
    13. )
    14. }
    15. // 边界判断
    16. // proxy data on instance
    17. const keys = Object.keys(data)
    18. const props = vm.$options.props
    19. const methods = vm.$options.methods
    20. let i = keys.length
    21. while (i--) {
    22. const key = keys[i]
    23. if (process.env.NODE_ENV !== 'production') {
    24. // 判断data是否与methods里的重名
    25. if (methods && hasOwn(methods, key)) {
    26. warn(
    27. `Method "${key}" has already been defined as a data property.`,
    28. vm
    29. )
    30. }
    31. }
    32. // 判断data是否与props里的重名
    33. if (props && hasOwn(props, key)) {
    34. process.env.NODE_ENV !== 'production' && warn(
    35. `The data property "${key}" is already declared as a prop. ` +
    36. `Use prop default value instead.`,
    37. vm
    38. )
    39. } else if (!isReserved(key)) {
    40. proxy(vm, `_data`, key)
    41. }
    42. }
    43. // observe data
    44. // 递归,对data 进行响应式处理
    45. observe(data, true /* asRootData */)
    46. }

    最后observe(data, true /* asRootData */) 函数对象data进行响应处理

    该函数文件:vue\src\core\observer\index.js

    1. export function observe (value: any, asRootData: ?boolean): Observer | void {
    2. // 不是对象 或者 虚拟dom实例 return
    3. if (!isObject(value) || value instanceof VNode) {
    4. return
    5. }
    6. // 1.响应式。2.动态属性的加入或删除,数组的加入或删除的变更通知。
    7. let ob: Observer | void
    8. // 判断是否有__ob__,如果是响应式对象,直接返回,不做重复处理
    9. if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    10. ob = value.__ob__
    11. } else if (
    12. shouldObserve &&
    13. !isServerRendering() &&
    14. (Array.isArray(value) || isPlainObject(value)) &&
    15. Object.isExtensible(value) &&
    16. !value._isVue
    17. ) {
    18. // 初始化传入需要响应式的对象
    19. ob = new Observer(value)
    20. }
    21. if (asRootData && ob) {
    22. ob.vmCount++
    23. }
    24. return ob
    25. }

    observe 函数将传入vlaue进行响应式处理 ob = new Observer(value)

    1. /**
    2. * Observer class that is attached to each observed
    3. * object. Once attached, the observer converts the target
    4. * object's property keys into getter/setters that
    5. * collect dependencies and dispatch updates.
    6. */
    7. export class Observer {
    8. value: any;
    9. dep: Dep;
    10. vmCount: number; // number of vms that have this object as root $data
    11. constructor (value: any) {
    12. this.value = value
    13. // 2. dep? 如果使用Vue.set/delete 添加或删除属性,负责通知更新
    14. this.dep = new Dep()
    15. this.vmCount = 0
    16. def(value, '__ob__', this)
    17. // 1. 分辨传入对象类型
    18. // 如果是数组
    19. if (Array.isArray(value)) {
    20. if (hasProto) {
    21. protoAugment(value, arrayMethods)
    22. } else {
    23. copyAugment(value, arrayMethods, arrayKeys)
    24. }
    25. this.observeArray(value)
    26. } else {
    27. // 是对象
    28. this.walk(value)
    29. }
    30. }
    31. /**
    32. * Walk through all properties and convert them into
    33. * getter/setters. This method should only be called when
    34. * value type is Object.
    35. */
    36. walk (obj: Object) {
    37. const keys = Object.keys(obj)
    38. for (let i = 0; i < keys.length; i++) {
    39. defineReactive(obj, keys[i])
    40. }
    41. }
    42. /**
    43. * Observe a list of Array items.
    44. */
    45. observeArray (items: Array) {
    46. for (let i = 0, l = items.length; i < l; i++) {
    47. observe(items[i])
    48. }
    49. }
    50. }

    1. 如果是对象走walk()

    1. export function defineReactive (
    2. obj: Object,
    3. key: string,
    4. val: any,
    5. customSetter?: ?Function,
    6. shallow?: boolean
    7. ) {
    8. // 每一个key对应一个dep
    9. const dep = new Dep()
    10. const property = Object.getOwnPropertyDescriptor(obj, key)
    11. if (property && property.configurable === false) {
    12. return
    13. }
    14. // cater for pre-defined getter/setters
    15. const getter = property && property.get
    16. const setter = property && property.set
    17. if ((!getter || setter) && arguments.length === 2) {
    18. val = obj[key]
    19. }
    20. // 递归处理
    21. let childOb = !shallow && observe(val)
    22. Object.defineProperty(obj, key, {
    23. enumerable: true,
    24. configurable: true,
    25. // 一个组件一个watcher
    26. get: function reactiveGetter () {
    27. const value = getter ? getter.call(obj) : val
    28. // 如果存在,说明此次调用触发者是一个Watcher实例
    29. if (Dep.target) {
    30. // 建立dep 和 Dep.target 之间依赖关系
    31. dep.depend()
    32. if (childOb) {
    33. // 建立ob 内部dep 和 Dep.target 之间依赖关系
    34. childOb.dep.depend()
    35. // 如果是数组,数组内部所有项都要做相同处理
    36. if (Array.isArray(value)) {
    37. dependArray(value)
    38. }
    39. }
    40. }
    41. return value
    42. },
    43. set: function reactiveSetter (newVal) {
    44. const value = getter ? getter.call(obj) : val
    45. /* eslint-disable no-self-compare */
    46. if (newVal === value || (newVal !== newVal && value !== value)) {
    47. return
    48. }
    49. /* eslint-enable no-self-compare */
    50. if (process.env.NODE_ENV !== 'production' && customSetter) {
    51. customSetter()
    52. }
    53. // #7981: for accessor properties without setter
    54. if (getter && !setter) return
    55. if (setter) {
    56. setter.call(obj, newVal)
    57. } else {
    58. val = newVal
    59. }
    60. childOb = !shallow && observe(newVal)
    61. dep.notify()
    62. }
    63. })
    64. }

    主要是进行依赖收集,实现n(Dep): n(Watcher)关系。

    1. /**
    2. * Collect dependencies on array elements when the array is touched, since
    3. * we cannot intercept array element access like property getters.
    4. */
    5. function dependArray (value: Array) {
    6. for (let e, i = 0, l = value.length; i < l; i++) {
    7. e = value[i]
    8. e && e.__ob__ && e.__ob__.dep.depend()
    9. if (Array.isArray(e)) {
    10. dependArray(e)
    11. }
    12. }
    13. }

    2. 如果是数组走observeArray()

    并会进行protoAugment(value, arrayMethods) 覆盖数组7个方法

    1. function protoAugment (target, src: Object) {
    2. /* eslint-disable no-proto */
    3. // 覆盖当前数组实例的原型
    4. // 只会影响当前数组实例本身
    5. target.__proto__ = src
    6. /* eslint-enable no-proto */
    7. }

    通过传入一个arrayMethods

    import { arrayMethods } from './array'

    找到该文件地址:vue\src\core\observer\array.js

    1. /*
    2. * not type checking this file because flow doesn't play well with
    3. * dynamically accessing methods on Array prototype
    4. */
    5. import { def } from '../util/index'
    6. const arrayProto = Array.prototype
    7. // 数组原型备份
    8. export const arrayMethods = Object.create(arrayProto)
    9. const methodsToPatch = [
    10. 'push',
    11. 'pop',
    12. 'shift',
    13. 'unshift',
    14. 'splice',
    15. 'sort',
    16. 'reverse'
    17. ]
    18. /**
    19. * Intercept mutating methods and emit events
    20. */
    21. methodsToPatch.forEach(function (method) {
    22. // cache original method
    23. const original = arrayProto[method]
    24. def(arrayMethods, method, function mutator (...args) {
    25. // 执行原始行为
    26. const result = original.apply(this, args)
    27. // 变更通知
    28. // 1. 获取ob 实例
    29. const ob = this.__ob__
    30. // 如果是新增元素的操作:比如push、unshift、splice 对该元素进行响应式observeArray
    31. let inserted
    32. switch (method) {
    33. case 'push':
    34. case 'unshift':
    35. inserted = args
    36. break
    37. case 'splice':
    38. inserted = args.slice(2)
    39. break
    40. }
    41. if (inserted) ob.observeArray(inserted)
    42. // notify change
    43. // 让内部的dep 通知更新
    44. ob.dep.notify()
    45. return result
    46. })
    47. })
  • 相关阅读:
    在C语言中,堆和栈是两种不同的内存分配机制
    k8s之service
    【Try Hack Me】内网专项---Wreath
    场景应用:用SpringBoot手写一个登录拦截器
    算法与数据结构【30天】集训营——线性表之链式表的表示与实现及顺表与链表比较(04)
    调用GPT接口使用3.5模型报错:Unrecognized request argument supplied: messages
    探究Spring原理
    学习windows系统让python脚本在后台运行的方法
    解决stable diffusion webui1.6 wd1.4 tagger加载失败的问题
    聊聊 mysql 事务?(一)
  • 原文地址:https://blog.csdn.net/qq_41538165/article/details/127893732