vue中一些方法的原理

1.$set

 用法:this.$set(Object, key, value)或 Vue.$set(Object, key, value)

   原理:当data数据中存在某个属性时,我们对他进行更改,页面会更新;但是如果data数据中没有某个属性值,我们对其更改,页面不会显示此属性;那是因为再vue初始化的时候,已经将data中定义的数据处理成响应式的了,所以数据的改变会触发页面的更新;但是新增的属性却不是响应式的,虽然打印可以看到对象确实多了此属性,但是由于不是响应式的,所以不会体现在页面上

##源码
位置:src/core/observer/index.js

 //set接收三个参数,target/key/val  target的类型为对象或者数组
export function set (target: Array<any> | Object, key: any, val: any): any {
  //当前环境为生产环境并且 target为null/undefined 或者 || target为string/number/symbol/boolean的一种时  抛出警告
  if (process.env.NODE_ENV !== 'production' &&
    (isUndef(target) || isPrimitive(target))
  ) {
    warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
  }
  //数组
  //判断是数组并且key为有效的数组索引
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    //将target数组的长度设置为target.length和key中的最大值
    target.length = Math.max(target.length, key)
    //做替换操作
    target.splice(key, 1, val)
    return val
  }
  //对象
  //key为数据中的一个属性并且不是Object原型上的属性
  if (key in target && !(key in Object.prototype)) {
    //已经存在说明是响应式的,直接更新
    target[key] = val
    return val
  }
  //之前不存在的情况
  //定义ob的值为target._ob_ 
  const ob = (target: any).__ob__
  //target对象是vue实例对象或者根数据对象,就会抛出错误
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== 'production' && warn(
      'Avoid adding reactive properties to a Vue instance or its root $data ' +
      'at runtime - declare it upfront in the data option.'
    )
    return val
  }
  //ob不存在,target不是响应式的
  if (!ob) {
    target[key] = val
    return val
  }
  //对新添加的key或者val做响应式----defineReactive主要实现Object.defineProperty()方法
  defineReactive(ob.value, key, val)
  //通知更新 --notify主要调用了update方法
  ob.dep.notify()
  return val
}

参考链接:https://www.cnblogs.com/heavenYJJ/p/9559439.html

总结:$set()修改数组主要是调用了splice();此方法主要是调用defineReactive()对新添加的key或者val做响应式;然后通过notify()方法来通知更新

2.$watch
用法:this.$watch(this.a.b,fn,{deep:true,immediate:true})
##源码
位置:src/core/instance/state.js

Vue.prototype.$watch = function (
    expOrFn: string | Function, //对象属性
    cb: any,                    //回调函数或者纯对象
    options?: Object            //可选配置对象
  ): Function {
    const vm: Component = this
    //如果回调cb是对象
    if (isPlainObject(cb)) {
      return createWatcher(vm, expOrFn, cb, options)
    }
    options = options || {}
    //表明这是一个用户的watcher,而不是组件的render watcher
    options.user = true 
    //创建watcher实例
    const watcher = new Watcher(vm, expOrFn, cb, options)
    //设置immediate立即执行一次cb
    if (options.immediate) {
      try {
        cb.call(vm, watcher.value)
      } catch (error) {
        handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)
      }
    }
    //返回取消监听的函数
    return function unwatchFn () {
      watcher.teardown()
    }
  }
}

所以他的重点在于获得Watcher实例

##源码
位置:src/core/observer/watcher.js

export default class Watcher {
  vm: Component;
  expression: string;
  cb: Function;
  id: number;
  deep: boolean;
  user: boolean;
  lazy: boolean;
  sync: boolean;
  dirty: boolean;
  active: boolean;
  deps: Array<Dep>;
  newDeps: Array<Dep>;
  depIds: SimpleSet;
  newDepIds: SimpleSet;
  before: ?Function;
  getter: Function;
  value: any;

  constructor (
    vm: Component,              //组件实例对象
    expOrFn: string | Function, //观察的属性
    cb: Function,               //回调
    options?: ?Object,          //可配置对象
    isRenderWatcher?: boolean   //标识观察者是否是渲染函数的观察者
  ) {
    this.vm = vm
    if (isRenderWatcher) {
      vm._watcher = this
    }
    vm._watchers.push(this)
    // options
    if (options) {
      this.deep = !!options.deep
      this.user = !!options.user
      this.lazy = !!options.lazy
      this.sync = !!options.sync
      this.before = options.before
    } else {
      this.deep = this.user = this.lazy = this.sync = false
    }
    this.cb = cb
    this.id = ++uid // uid for batching
    this.active = true
    this.dirty = this.lazy // for lazy watchers
    this.deps = []
    this.newDeps = []
    this.depIds = new Set()
    this.newDepIds = new Set()
    this.expression = process.env.NODE_ENV !== 'production'
      ? expOrFn.toString()
      : ''
    // parse expression for getter
    if (typeof expOrFn === 'function') {
      this.getter = expOrFn
    } else {
      this.getter = parsePath(expOrFn)
      if (!this.getter) {
        this.getter = noop
        process.env.NODE_ENV !== 'production' && warn(
          `Failed watching path: "${expOrFn}" ` +
          'Watcher only accepts simple dot-delimited paths. ' +
          'For full control, use a function instead.',
          vm
        )
      }
    }
    this.value = this.lazy
      ? undefined
      : this.get()
  }

  /**
   * Evaluate the getter, and re-collect dependencies.
   */
  get () {
    pushTarget(this) //将当前Watcher的实例指定到Dep.target上
    /*
      src/core/observer/dep.js
      pushTarget(target){
        Dep.targer = target
      }
    */
    //然后立即调render函数,render函数访问相关的key,
    let value
    const vm = this.vm
    try {
      value = this.getter.call(vm, vm)
    } catch (e) {
      if (this.user) {
        handleError(e, vm, `getter for watcher "${this.expression}"`)
      } else {
        throw e
      }
    } finally {
      // "touch" every property so they are all tracked as
      // dependencies for deep watching
      if (this.deep) {
        //深层遍历,递归读取被观察属性的所有子属性的值
        traverse(value)
      }
      popTarget()
      this.cleanupDeps()
    }
    return value
  }

  /**
   * Add a dependency to this directive.
   */
   //记录自己都订阅过哪些Dep
  addDep (dep: Dep) {
    const id = dep.id
    //去重
    if (!this.newDepIds.has(id)) {
      this.newDepIds.add(id)
      this.newDeps.push(dep)
      if (!this.depIds.has(id)) {
        //把自己订阅到dep
        dep.addSub(this)
      }
    }
  }

  /**
   * Clean up for dependency collection.
   */
  cleanupDeps () {
    let i = this.deps.length
    while (i--) {
      const dep = this.deps[i]
      if (!this.newDepIds.has(dep.id)) {
        dep.removeSub(this)
      }
    }
    let tmp = this.depIds
    this.depIds = this.newDepIds
    this.newDepIds = tmp
    this.newDepIds.clear()
    tmp = this.deps
    this.deps = this.newDeps
    this.newDeps = tmp
    this.newDeps.length = 0
  }

  /**
   * Subscriber interface.
   * Will be called when a dependency changes.
   */
  update () {
    /* istanbul ignore else */
    if (this.lazy) {
      this.dirty = true
    } else if (this.sync) {
      //指定同步更新
      this.run()
    } else {
      //异步更新队列
      queueWatcher(this)
    }
  }

  /**
   * Scheduler job interface.
   * Will be called by the scheduler.
   */
  run () {
    if (this.active) {
      const value = this.get()
      //对比新值value和旧值this.value是否相等
      if (
        value !== this.value ||
        // Deep watchers and watchers on Object/Arrays should fire even
        // when the value is the same, because the value may
        // have mutated.
        isObject(value) ||
        this.deep
      ) {
        // set new value
        const oldValue = this.value
        this.value = value
        if (this.user) { //通过watch选项或者$watch定义的
          try {
            //执行回调
            this.cb.call(this.vm, value, oldValue)
          } catch (e) {
            handleError(e, this.vm, `callback for watcher "${this.expression}"`)
          }
        } else {
          this.cb.call(this.vm, value, oldValue)
        }
      }
    }
  }
 
原文地址:https://www.cnblogs.com/znLam/p/13153779.html