vue-源码分析

diff

Vue中的diff算法

响应式

抽象语法树

源码分析

vue3新特性

vue3新特性总结

vue2

那么Vue中它是如何做到响应式的呢?

想要完成此过程,我们需要做如下事情:

1)侦测对象数据的变化。
2)收集视图依赖了哪些数据。
3)数据变化时,自动通知和数据相关联的视图页面,并对视图进行更新。

在这里插入图片描述
数据双向绑定及响应式

数据在初始化的时候就将数据绑定成响应式,就是通过object.definePropertyo的set 和get方法,get 收集依赖,set 触发界面更新

function Vue(options) { //获取数据
  this._data = options.data;
  observe(this._data);
}

function observe(value) { //遍历data中的每一项
  //先不考虑value为数组的复杂状况
  if (!value || typeof value !== 'object') return;
  Object.keys(value).forEach(function(propertyName) {
    defineReactive(value, propertyName, value[propertyName])
  })
}

function defineReactive(obj, key, val) { //监听每一个data的数据变化
  var dep = new Dep();//实例化一个订阅者
  console.log('defineReactive==parameter', obj, key, val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    set: function(newVal) {
      console.log('newVal', newVal);
      if (val == newVal) return;
      dep.notify(); //若是新修改的值和原来的值不一样就从新渲染页面
      
    },
    get: function() {
      dep.addSub(Dep.target); //把watcher实例添加到依赖中
      return val;
      // 收集依赖
    }
  })
}

依赖收集器Dep: Dep的主要做用是存放Watcher观察者对象,Dep用于依赖收集和派发更新,它收集所有的观察者,当有数据变动的时候,它会把消息通知到所有的观察者,同时它也调用Watcher实列中的update方法,用于派发更新。

Dep有个subs的数组,用于收集观察者

addSub方法:用于将所有的观察者添加到subs数组中

Dep → Watcher这一步,还需要通知变化,所以需要一个notify方法,内部遍历每一个watcher观察者,去更新发生变化的部分

观察者Watcher:
观察者主要是通过比较新值和旧值的变化,然后决定是否调用回调函数callback更新视图

function Dep() {
 //添加观察者watcher
  this.subs = [];
  this.addSub = function(sub) {
    this.subs.push(sub);
    console.log("sub==", sub);
  }
  //通知Watcher更新视图
  this.notify = function() {
    this.subs.forEach(function(s) {
      s.update()
    });
  }
}
Dep.target = null;

2.观察者Watcher:主要做用是监听数据变化更新视图
function Watcher() {
  Dep.target = this; 
  //在new 一个Watcher 对象时将该对象赋值给Dep.target,在get 中会用到
  this.update = function() {
    console.log('updata view===')
  }
}

vue在observe中注册并调用get,在get中,将Watcher实例经过addSub方法添加到dep的subs数组中,这完成了依赖添加的过程;
当数据变化时,调用Object.defineProperty中的set方法,判断数据是否发生改变,若是改变了,就调用dep中的notify方法通知Watcher,Watcher知道数据变化更新数据.
在这里插入图片描述
能够看到array的原理和object相似,可是由于大多数状况下都不是set,而是一些操做数组的方法(push、pull、shift…),因此咱们加了一层拦截器去通知Dep。双向绑定

Object.defineProperty 可以做到对数组的监听,它是支持数组的,但是为什么没用它呢?

如果你知道数组的长度,理论上是可以预先给所有的索引设置 getter/setter 的。但是一来很多场景下你不知道数组的长度,二来,如果是很大的数组,预先加 getter/setter 性能负担较大。总而言之就是理论上 vue 是可以这样做,但是出于性能考虑没这样做,而是用了一种数组变异办法来触发视图更新。

import { def } from '../util/index'

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)

/**
 * Intercept mutating methods and emit events
 */
;[
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]
.forEach(function (method) {
  // cache original method
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (...args) {
    // 调用数组真正的方法
    const result = original.apply(this, args)
    // __ob__代表数据是否被observe了
    const ob = this.__ob__
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    // inserted表示有数据插入 对新数据进行observe
    if (inserted) ob.observeArray(inserted)
    // notify change
    ob.dep.notify()
    return result
  })
})

Vue2 中的变化侦测实现对 Object 及 Array 分别进行了不同的处理,Objcet 使用了Object.defineProperty API ,Array 使用了拦截器对 Array 原型上的能够改变数据的方法进行拦截。虽然也实现了数据的变化侦测,但存在很多局限 ,比如对象新增属性无法被侦测,以及通过数组下边修改数组内容,也因此在 Vue2 中经常会使用到 $set 这个方法对数据修改,以保证依赖更新。

Vue3 中使用了 es6 的 Proxy API 对数据代理,没有像 Vue2 中对原数据进行修改,只是加了代理包装,因此首先性能上会有所改善。其次解决了 Vue2 中变化侦测的局限性,可以不使用 $set 新增的对象属性及通过下标修改数组都能被侦测到。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值