资讯专栏INFORMATION COLUMN

Vue响应式原理-排除所有优化,只看核心

xumenger / 2046人阅读

摘要:响应式原理作为写业务的码农,几乎不必知道原理。所以找工作之前可以先复习下,只要是关于的,必定会问响应式原理。如果是,运行其方法,其方法中含有的值时,会触发收集当更新时,也会触发更新重新获取值如果是会触发的函数响应式原理计算属性值这是

Vue响应式原理

作为写业务的码农,几乎不必知道原理。但是当你去找工作的时候,可是需要造原子弹的,什么都得知道一些才行。所以找工作之前可以先复习下,只要是关于vue的,必定会问响应式原理。

核心:

//es5
Object.defineProperty(obj,key,{
    get() {
        // 获取obj[key]的时候触发
    },
    set(val) {
       // obj[key] = "xxx"时触发
    }
})

其实,只需要在修改data值的时候,需要触发一个回调方法,来更新与此值有关的数据,就完了。但是你面试的时候,那些大佬可不会觉得是这样的,需要把整个流程说明白才行。

简单的Vue响应式代码如下: Vue.js:
class Vue {
  constructor(opts) {
    this.opts = opts
    if (opts.data) this.initData(opts.data);
    if (opts.watch) this.initWatch(opts.watch);
    if (opts.computed) this.initComputed(opts.computed);
    if (opts.el) this.$mount(opts.el)
  }
  initData(data) {
    // 让data上的数据被get的时候能够搜集watcher,data变为观察者
    new Observable(data);
    this.data = data
    // 数据可在this上直接读取
    Object.keys(data).forEach(key => {
      this.proxy(key)
    })
  }

  initWatch(watch) {
    Object.keys(watch).forEach(key => {
      // 
      new Watcher(this, key, watch[key])
    })
  }
  initComputed(data) {
    Object.keys(data).forEach(key => {
      new Watcher(this, key)
      this.proxy(key, {
        get: data[key],
      })
    })

  }

  $mount(el) {
    el = document.querySelector(el)
    this.template = el.innerHTML

    this.el = el
    const fn = _ => {
      const nwTemp = this.parseHTML(this.template)
      this.el.innerHTML = nwTemp
      if(this.opts.mounted) {
        this.opts.mounted.call(this)
      }
    }
    new Watcher(this, fn)
  }
  parseHTML(template) {

    return template.replace(/{{(.*?)}}/g, (str, str1) => {
      return this[str1.trim()]
    })
  }


  // 将数据直接挂到this上,使用getterSetter代理,获取vm.data上的值
  proxy(key, getterSetter) {
    const vm = this
    getterSetter = getterSetter || {
      set(value) {
        vm.data[key] = value
      },
      get() {
        return vm.data[key]
      }
    }
    Object.defineProperty(vm, key, getterSetter)
  }
}
// 给data作为观察者用的
class Observable {
  constructor(obj) {

    Object.keys(obj).forEach(key => {
      this.defineReact(obj, key, obj[key])
    });
  }

  defineReact(obj, key, value) {
  
    const dep = new Dep()
    Object.defineProperty(obj, key, {
      get() {
        // 获取data里面信息的时候,能够搜集依赖,这些依赖都是watcher实例
        if (Dep.target) {
          dep.append(Dep.target)
        }
        return value
      },
      set(val) {
        value = val
        // 修改data里面数据的时候,去通知已搜集的依赖更新
        dep.notify()
      }
    })
  }

}

// 搜集与触发wacher
class Dep {

  constructor() {
    this.subs = []
  }
  append(watcher) {
    // 避免重复添加watcher
    // watcher 在update的时候,会重新获取值,此时不必再添加
    if(this.subs.includes(watcher)) return
    this.subs.push(watcher)
  }
  notify() {
    this.subs.forEach(watcher => {
      watcher.update()
    })
  }
}
Dep.target = null


class Watcher {
  // keyOrFn 为字符串或者function,opts.watch为字符串,computed,$mount中为function
  constructor(vm, keyOrFn, cb) {
    this.cb = cb
    if (typeof keyOrFn === "string") {
      this.getter = function () {
        return vm[keyOrFn] // 例:initWatch时,watch:{a(){}} ,a为data里的数据,此处获取vm.a会触发a的收集
      }
    } else {
      
      this.getter = keyOrFn // 如果为fn(computed中)值为此函数的返回值
    }

    this.value = this.get()
  }
  get() {
    Dep.target = this // 此watcher记录下来
    const value = this.getter(this.vm)
     // 运行getter,如果是watch,获取一次data里的值,完成收集。
     // 如果是computed,运行其方法,其方法中含有data的值时,会触发收集
    // 当更新时,也会触发getter
     Dep.target = null 
    return value
  }

  update() {
    // 更新
    const oldValue = this.value
    // 重新获取值
    this.value = this.get()
    if (this.cb) {
      //如果是watch,会触发watch的函数
      this.cb.call(this.vm, this.value, oldValue)
    }
  }
}
index.html
  

Vue响应式原理


msg: {{ msg }}

num: {{ num }}

num+1计算属性值:{{ add1 }}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104174.html

相关文章

  • 前端每周清单第 34 期:Vue 现状盘点与 3.0 展望,React 代码迁移与优化,图片优化详论

    摘要:工程实践立足实践,提示实际水平内联函数与性能很多关于性能优化的文章都会谈及内联函数,其也是常见的被诟病为拖慢性能表现的元凶之一不过本文却是打破砂锅问到底,论证了内联函数并不一定就会拖慢性能,过度的性能优化反而会有损于应用性能。 showImg(https://segmentfault.com/img/remote/1460000011481413?w=1240&h=825); 前端每周...

    CoderStudy 评论0 收藏0
  • 深入学习Vuex

    摘要:深入学习作为配合使用的数据状态管理库,针对解决兄弟组件或多层级组件共享数据状态的痛点问题来说,非常好用。至此,构造函数部分已经过了一遍了。 深入学习Vuex vuex作为配合vue使用的数据状态管理库,针对解决兄弟组件或多层级组件共享数据状态的痛点问题来说,非常好用。本文以使用者的角度,结合源码来学习vuex。其中也参考了许多前辈的文章,参见最后的Reference Vue加载Vuex...

    codercao 评论0 收藏0
  • 深入学习Vuex

    摘要:深入学习作为配合使用的数据状态管理库,针对解决兄弟组件或多层级组件共享数据状态的痛点问题来说,非常好用。至此,构造函数部分已经过了一遍了。 深入学习Vuex vuex作为配合vue使用的数据状态管理库,针对解决兄弟组件或多层级组件共享数据状态的痛点问题来说,非常好用。本文以使用者的角度,结合源码来学习vuex。其中也参考了许多前辈的文章,参见最后的Reference Vue加载Vuex...

    funnyZhang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<