略微解读Vue源码(持续更新)

137 阅读1分钟

「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战

对于MVVM的理解

  • 传统的MVC指的是,用户操作会请求服务端路由,路由会调用对应的控制器来处理,控制器会获取数据。将结果返回给前端,页面重新渲染
  • MVVM:传统的前端将数据手动渲染,MVVM模式不需要用户手动操作DOM元素,将数据绑定到viewModel层上,自动渲染到页面,视图会通知viewModel层更新数据。Viewmodel是MVVM中的关键。

响应式数据原理

Watcher实际上是连接Vue组件和 Dep的桥梁。每一个 Watcher对应一个Vue Component

src/instance/state.js

function initData(vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    // 执行函数返回data return 出来变量
    : data || {}
  // 判断是否为函数,如果data有则使用data,没有data创建空对象
  if (!isPlainObject(data)) { // 判断是否为真实对象
    // export function isPlainObject (obj: any): boolean {
    //   return _toString.call(obj) === '[object Object]'
    // }
    data = {} // 避免报错
    process.env.NODE_ENV !== 'production' && warn(
      'data functions should return an object:\n' +
      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
      vm
    ) // 抛出警告,在生产环境下
  }
  // proxy data on instance
  const keys = Object.keys(data)
  // 取出其中的键
  const props = vm.$options.props
  // 组件的接收参数
  const methods = vm.$options.methods
  // 当前组件的方法
  let i = keys.length
  while (i--) {
    const key = keys[i]
    if (process.env.NODE_ENV !== 'production') { 
      // 如果不在生产环境
      if (methods && hasOwn(methods, key)) { 
        // hasOwn = Object.prototype.hasOwnProperty
        // methods 中是否有key
        // 避免和data中的键重复
        warn(
          `Method "${key}" has already been defined as a data property.`,
          vm
        )
      }
    }
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== 'production' && warn(
        `The data property "${key}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else if (!isReserved(key)) { // 判断键的ascll编码
      proxy(vm, `_data`, key) // 符合进行监听
    }
  }
  // observe data
  observe(data, true /* asRootData */)
}

src/core/observer/dep.js

Dep.target = null
const targetStack = [] // 记录栈

export function pushTarget (target: ?Watcher) {
  targetStack.push(target) 
  Dep.target = target
 // 进入栈复赋值操作
}

export function popTarget () {
  targetStack.pop()
  Dep.target = targetStack[targetStack.length - 1] 
  // 出栈将,将移除后的栈顶,将其赋值
}

src/core/objserver/index.js

export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that have this object as root $data

  constructor (value: any) {
    this.value = value
    this.dep = new Dep()
    this.vmCount = 0
    def(value, '__ob__', this)
    if (Array.isArray(value)) { // 判断是否为数组
      if (hasProto) { 
        // 这是一个原型链
        // export const hasProto = '__proto__' in {}
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      this.observeArray(value)
    } else {
      // 不是数组则为对象
      this.walk(value)
    }
  }

  /**
   * Walk through all properties and convert them into
   * getter/setters. This method should only be called when
   * value type is Object.
   */
  walk (obj: Object) {
    const keys = Object.keys(obj)
    // 进行对象的处理
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }

  /**
   * Observe a list of Array items.
   */
  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}


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
  }

  // 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]
  }

  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) {
          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()
    }
  })
}

src/core/utils/lang.js

export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
  // 监听对象内容
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    writable: true,
    configurable: true
  })
}