学习笔记:Vue源码学习(一)——响应式

声明:以下内容为个人学习vue源码中记录的笔记,仅适合我本人,其他人请谨慎阅读,读了你可能也看不懂(狗头


目标

  • 熟悉vue源码架构

  • 了解vue初始化过程

  • 了解vue响应式原理

准备

  • git clone https://github.com/vuejs/vue.git

  • npm i

  • npm i rollup -g

  • package.json => scripts => dev 加上 sourcemap

    - "dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
    + "dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",
    
  • npm run dev

  • 创建测试html,引入dist/vue.js

断点调试

new Vue()

然后F11逐步深入调试

在这里插入图片描述

src\core\instance\index.js

  • this._init(options)
  • initMixin(Vue)
  • stateMixin(Vue)
  • eventsMixin(Vue)
  • lifecycleMixin(Vue)
  • renderMixin(Vue)

初始化流程

vm.mount()

src/core/instance/lifecycle.js

mountComponent //挂载组件

updateComponent/new Watcher

_update // patch解析

/src/core/instance/render.js

_render

总结一下

new Vue() => this._init() => vm.mount() => mountComponent() => updateComponent/new Watch => _render() => -update()

响应式流程

其中stateMixin里面是数据响应式相关的东西,我们重点学习的内容,

src\core\instance\state.js

在initData中就是初始化data中方法,观察其实很简单,总结一下就做了这几件事:

  • 判断data是函数还是对象
  • 判断有没有和props或method中的名字冲突
  • 最后把data传给obsever

所以重点学习的内容在obsever中

~小插曲:

在state.js中发现了一个函数,解决了我的一个疑惑

  • 为什么this.xxx可以直接访问this.data.xxx

const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop
}

export function proxy (target: Object, sourceKey: string, key: string) {
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

// proxy(vm, `_data`, key)

原来是通过Object.defineProperty加了一层代理

src\core\observer\index.js

核心函数就是defineRective

/**
 * Define a reactive property on an Object.
 */
// 定义属性拦截
// Vue.util.defineReactive = defineReactive
export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  // 小管家:每个key一个
  const dep = new Dep()

  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }

  // cater for pre-defined getter/setters
  const getter = property && property.get
  const setter = property && property.set
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]
  }

  // 如果val是对象还会递归它
  let childOb = !shallow && observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      // 依赖收集
      if (Dep.target) {
        dep.depend()
        if (childOb) {
          // 如果存在对象嵌套,则存在子ob实例,需要建立大管家和当前watcher之间的关系
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()
      }
      // #7981: for accessor properties without setter
      if (getter && !setter) return
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal)
      dep.notify()
    }
  })
}

写个简版便于理解

function observe(){
    /* 一些其他操作 */
  return new Observe()
}

function defineReactive(obj, key, val) {
  Object.defineProperty(obj, key, {
    get(){
      /* ...依赖搜集... */
      return val
    },
    set(newVal) {
      if(newVal === val) return
      val = newVal
      /* ...更新通知... */
    }
  })
}

class Observe {
  constructor(value) {
    /*  ... 一些针对数组的判断及处理... */
    this.walk(value)
  }

  walk(obj) {
    const keys = Object.keys(obj)
    for(let i = 0;i<keys.length; i++) {
      defineReactive(obj, keys[i], obj[keys[i]])
    }
  }
}

视图中出现每个key都需要一个Watcher来维护,一个key可能在出现多次,每个key创建一个dep,

所以一个dep可以管理多个watcher

在这里插入图片描述

总结

先上一个刚画的简图

在这里插入图片描述

1,初始化的时候会遍历使用observer遍历data,拦截他们的get,set

2,每个key对应一个Dep,get里面使用dep.addDep收集依赖,set里dep.notify通知更新

3,如果将来data里面的数据变化,会通过对应的dep,通知所有的watcher执行更新函数

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值