zoukankan      html  css  js  c++  java
  • 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)
            }
          }
        }
      }
     
  • 相关阅读:
    C语言超大数据相加计算整理
    pc端页面加载更多条信息(loading)
    web前端学习路线
    linux启动流程
    Computer Science: http://www.cs.odu.edu/~cs779/
    编程网站
    sublime-text 键绑定
    monokai-background
    .vimrc
    vim 正则表达式查找ip
  • 原文地址:https://www.cnblogs.com/znLam/p/13153779.html
Copyright © 2011-2022 走看看