searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Vue响应式原理介绍

2023-05-26 07:01:05
74
0

Vue.js 是当前流行的一款渐进式 JavaScript 框架,其主要特点之一是响应式编程。Vue.js 的响应式系统让数据的变化驱动了页面的变化,降低了前端编程的复杂度。在本文中,我们将介绍 Vue.js 的响应式原理。

Vue.js 的响应式系统

当一个 Vue.js 应用启动时,Vue.js 会将所有的数据转换为响应式数据,这些响应式数据会与模板中的 DOM 元素建立关联,当发生数据更新时,Vue.js 会自动更新模板。这种机制被称为“响应式系统”,Vue.js 中使用一个 Watcher 来观察每一个响应式数据,当数据改变时触发更新。

响应式数据的创建

Vue.js 中采用了 Object.defineProperty 方法来实现响应式数据。Object.defineProperty 方法可以在一个对象上定义一个新属性或修改一个已有属性,包含以下几个参数:

  • obj:要在其上定义属性的对象;

  • prop:要定义或修改的属性的名称;

  • descriptor:将被定义或修改的属性的描述符。

Vue.js 将每一个响应式数据对象添加一个标识,并在其中存储了关于该对象的观察者 Watcher 信息。Vue.js 通过该方法来监听数据的变化并触发更新。

以下代码展示了如何使用 Object.defineProperty 来创建响应式数据:

var obj = {}
Object.defineProperty(obj, 'prop', {
 get: function () {
   return value
},
 set: function (newValue) {
   value = newValue
}
})

依赖收集

依赖收集是 Vue.js 最重要的特性之一。当 Vue.js 遇到了一个取值操作(如 obj.prop),它会通过一个全局定义的变量(称为 Dep)将该取值操作与当前激活的 Watcher 进行关联。这个 Watcher 就是数据的依赖。

当响应式数据发生改变时,该数据的 Set 方法会通知所有的 Watcher 更新,这些 Watcher 就会根据自身的依赖重新计算其所对应的组件,然后更新视图。

下面是一个代码示例:

function defineReactive (obj, key, val) {
 Object.defineProperty(obj, key, {
   get: function () {
     if (Dep.target) {
       Dep.target.addDep(dep)
    }
     return val
  },
   set: function (newVal) {
     if (val !== newVal) {
       val = newVal
       dep.notify()
    }
  }
})
}

其中,addDep 方法用于将当前 Watcher 加入到依赖中。

Watcher

Watcher 是 Vue.js 实现响应式编程的核心。它的作用是存储被观察的数据以及依赖,当被观察的数据被修改时,Watcher 会通知所有的依赖重新计算。

下面是一个 Watcher 的基础实现:

function Watcher (vm, expOrFn, cb) {
 this.cb = cb
 this.vm = vm
 this.expOrFn = expOrFn
 this.depIds = []
 if (typeof expOrFn === 'function') {
   this.getter = expOrFn
} else {
   this.getter = this.parseGetter(expOrFn.trim())
}
 this.value = this.get()
}

Watcher.prototype = {
 update: function () {
   this.run()
},
 run: function () {
   var value = this.get()
   var oldValue = this.value
   if (value !== oldValue) {
     this.value = value
     this.cb.call(this.vm, value, oldValue)
  }
},
 get: function () {
   Dep.target = this
   var value = this.getter.call(this.vm, this.vm)
   Dep.target = null
   return value
},
 addDep: function (dep) {
   var id = dep.id
   if (this.depIds.indexOf(id) === -1) {
     this.depIds.push(id)
     dep.addSub(this)
  }
},
 parseGetter: function (exp) {
   if (/[^\w.$]/.test(exp)) return

   var exps = exp.split('.')

   return function (obj) {
     for (var i = 0; i < exps.length; i++) {
       obj = obj[exps[i]]
    }
     return obj
  }
}
}

响应式系统的局限性

虽然 Vue.js 的响应式系统可以很好地工作,但是也有一些局限性。比如:

  • 数组变化时 Vue.js 无法识别以下操作:直接通过数组下标设置元素,Array.prototype.push(),Array.prototype.pop(),Array.prototype.shift(),Array.prototype.unshift(),Array.prototype.splice(),Array.prototype.sort(),Array.prototype.reverse()

Vue3的响应式

以上主要是Vue2.x的响应式原理,下面是Vue3.x的响应式原理。

Vue3.x引入了一个新的响应式API,即Proxy API,来代替Vue2.x中使用的Object.defineProperty实现。使用Proxy API可以使得Vue3.x在性能上更加优越,并且可以更灵活地处理动态添加和删除的属性,以及能实现对Map、Set等JavaScript数据结构的响应式处理。

Vue3.x中,响应式系统的核心依然是劫持核心对象的getter方法,来监听属性值的变化,但是与Vue2.x不一样的是,Vue3.x使用了Proxy API,将一个对象包装在一个代理层之中,从而在代理层进行拦截和监听。

下面是Vue3.x中响应式系统的实现代码:

const mutableHandlers = {
 get: function(target, key) {
   const res = Reflect.get(target, key)
   track(target, key) // 响应式收集依赖
   return isObject(res) ? reactive(res) : res
},
 set: function(target, key, val) {
   const oldVal = Reflect.get(target, key)
   val = isObject(val) ? reactive(val) : val
   const result = Reflect.set(target, key, val)
   if (!oldVal) {
     // 新添加的属性
     trigger(target, 'add', key, val)
     trigger(target, 'set', key, val)
  } else if (val !== oldVal) {
     // 已有的属性
     trigger(target, 'set', key, val, oldVal)
  }
   return result
},
 deleteProperty: function(target, key) {
   const result = Reflect.deleteProperty(target, key)
   if (result) {
     trigger(target, 'delete', key)
  }
   return result
}
}

function reactive(obj) {
 if (!isObject(obj)) {
   return obj
}
 return new Proxy(obj, mutableHandlers)
}

在这个实现中,mutableHandlers是一个对象,其中的get和set方法是拦截属性访问和设置的方法。当使用reactive函数劫持一个对象时,会返回该对象的代理对象,该代理对象可以处理这些操作,从而使得可以在这些操作中收集依赖和触发更新。

在创建代理对象的过程中,对对象进行递归处理,将对象中的所有属性都转换为响应式属性。当访问代理对象中的一个属性值时,该值会被代理,并且如果该值是一个对象,则会递归创建它的代理对象。

除了使用Proxy API来实现响应式系统之外,Vue3.x的响应式系统还引入了新的API,比如refcomputed。这些API可以帮助我们更加方便地处理响应式数据,并且可以减少开发的工作量、减少代码的复杂度。

总结

本文主要介绍了Vue2.x和Vue3.x响应式系统的实现原理。Vue3.x采用了新的Proxy API替代了Object.defineProperty API,提升了性能,解决了某些场景下响应式更新不正确的问题,并且可以处理动态属性和Map、Set等数据结构的响应式。此外,新增了`ref`和`computed` API,提供更好的性能和易用性。总的来说,Vue3.x响应式系统在性能、灵活性和易用性方面都有显著的改进,可以更高效地创建易于维护的响应式应用。

0条评论
0 / 1000
张****亮
9文章数
0粉丝数
张****亮
9 文章 | 0 粉丝
原创

Vue响应式原理介绍

2023-05-26 07:01:05
74
0

Vue.js 是当前流行的一款渐进式 JavaScript 框架,其主要特点之一是响应式编程。Vue.js 的响应式系统让数据的变化驱动了页面的变化,降低了前端编程的复杂度。在本文中,我们将介绍 Vue.js 的响应式原理。

Vue.js 的响应式系统

当一个 Vue.js 应用启动时,Vue.js 会将所有的数据转换为响应式数据,这些响应式数据会与模板中的 DOM 元素建立关联,当发生数据更新时,Vue.js 会自动更新模板。这种机制被称为“响应式系统”,Vue.js 中使用一个 Watcher 来观察每一个响应式数据,当数据改变时触发更新。

响应式数据的创建

Vue.js 中采用了 Object.defineProperty 方法来实现响应式数据。Object.defineProperty 方法可以在一个对象上定义一个新属性或修改一个已有属性,包含以下几个参数:

  • obj:要在其上定义属性的对象;

  • prop:要定义或修改的属性的名称;

  • descriptor:将被定义或修改的属性的描述符。

Vue.js 将每一个响应式数据对象添加一个标识,并在其中存储了关于该对象的观察者 Watcher 信息。Vue.js 通过该方法来监听数据的变化并触发更新。

以下代码展示了如何使用 Object.defineProperty 来创建响应式数据:

var obj = {}
Object.defineProperty(obj, 'prop', {
 get: function () {
   return value
},
 set: function (newValue) {
   value = newValue
}
})

依赖收集

依赖收集是 Vue.js 最重要的特性之一。当 Vue.js 遇到了一个取值操作(如 obj.prop),它会通过一个全局定义的变量(称为 Dep)将该取值操作与当前激活的 Watcher 进行关联。这个 Watcher 就是数据的依赖。

当响应式数据发生改变时,该数据的 Set 方法会通知所有的 Watcher 更新,这些 Watcher 就会根据自身的依赖重新计算其所对应的组件,然后更新视图。

下面是一个代码示例:

function defineReactive (obj, key, val) {
 Object.defineProperty(obj, key, {
   get: function () {
     if (Dep.target) {
       Dep.target.addDep(dep)
    }
     return val
  },
   set: function (newVal) {
     if (val !== newVal) {
       val = newVal
       dep.notify()
    }
  }
})
}

其中,addDep 方法用于将当前 Watcher 加入到依赖中。

Watcher

Watcher 是 Vue.js 实现响应式编程的核心。它的作用是存储被观察的数据以及依赖,当被观察的数据被修改时,Watcher 会通知所有的依赖重新计算。

下面是一个 Watcher 的基础实现:

function Watcher (vm, expOrFn, cb) {
 this.cb = cb
 this.vm = vm
 this.expOrFn = expOrFn
 this.depIds = []
 if (typeof expOrFn === 'function') {
   this.getter = expOrFn
} else {
   this.getter = this.parseGetter(expOrFn.trim())
}
 this.value = this.get()
}

Watcher.prototype = {
 update: function () {
   this.run()
},
 run: function () {
   var value = this.get()
   var oldValue = this.value
   if (value !== oldValue) {
     this.value = value
     this.cb.call(this.vm, value, oldValue)
  }
},
 get: function () {
   Dep.target = this
   var value = this.getter.call(this.vm, this.vm)
   Dep.target = null
   return value
},
 addDep: function (dep) {
   var id = dep.id
   if (this.depIds.indexOf(id) === -1) {
     this.depIds.push(id)
     dep.addSub(this)
  }
},
 parseGetter: function (exp) {
   if (/[^\w.$]/.test(exp)) return

   var exps = exp.split('.')

   return function (obj) {
     for (var i = 0; i < exps.length; i++) {
       obj = obj[exps[i]]
    }
     return obj
  }
}
}

响应式系统的局限性

虽然 Vue.js 的响应式系统可以很好地工作,但是也有一些局限性。比如:

  • 数组变化时 Vue.js 无法识别以下操作:直接通过数组下标设置元素,Array.prototype.push(),Array.prototype.pop(),Array.prototype.shift(),Array.prototype.unshift(),Array.prototype.splice(),Array.prototype.sort(),Array.prototype.reverse()

Vue3的响应式

以上主要是Vue2.x的响应式原理,下面是Vue3.x的响应式原理。

Vue3.x引入了一个新的响应式API,即Proxy API,来代替Vue2.x中使用的Object.defineProperty实现。使用Proxy API可以使得Vue3.x在性能上更加优越,并且可以更灵活地处理动态添加和删除的属性,以及能实现对Map、Set等JavaScript数据结构的响应式处理。

Vue3.x中,响应式系统的核心依然是劫持核心对象的getter方法,来监听属性值的变化,但是与Vue2.x不一样的是,Vue3.x使用了Proxy API,将一个对象包装在一个代理层之中,从而在代理层进行拦截和监听。

下面是Vue3.x中响应式系统的实现代码:

const mutableHandlers = {
 get: function(target, key) {
   const res = Reflect.get(target, key)
   track(target, key) // 响应式收集依赖
   return isObject(res) ? reactive(res) : res
},
 set: function(target, key, val) {
   const oldVal = Reflect.get(target, key)
   val = isObject(val) ? reactive(val) : val
   const result = Reflect.set(target, key, val)
   if (!oldVal) {
     // 新添加的属性
     trigger(target, 'add', key, val)
     trigger(target, 'set', key, val)
  } else if (val !== oldVal) {
     // 已有的属性
     trigger(target, 'set', key, val, oldVal)
  }
   return result
},
 deleteProperty: function(target, key) {
   const result = Reflect.deleteProperty(target, key)
   if (result) {
     trigger(target, 'delete', key)
  }
   return result
}
}

function reactive(obj) {
 if (!isObject(obj)) {
   return obj
}
 return new Proxy(obj, mutableHandlers)
}

在这个实现中,mutableHandlers是一个对象,其中的get和set方法是拦截属性访问和设置的方法。当使用reactive函数劫持一个对象时,会返回该对象的代理对象,该代理对象可以处理这些操作,从而使得可以在这些操作中收集依赖和触发更新。

在创建代理对象的过程中,对对象进行递归处理,将对象中的所有属性都转换为响应式属性。当访问代理对象中的一个属性值时,该值会被代理,并且如果该值是一个对象,则会递归创建它的代理对象。

除了使用Proxy API来实现响应式系统之外,Vue3.x的响应式系统还引入了新的API,比如refcomputed。这些API可以帮助我们更加方便地处理响应式数据,并且可以减少开发的工作量、减少代码的复杂度。

总结

本文主要介绍了Vue2.x和Vue3.x响应式系统的实现原理。Vue3.x采用了新的Proxy API替代了Object.defineProperty API,提升了性能,解决了某些场景下响应式更新不正确的问题,并且可以处理动态属性和Map、Set等数据结构的响应式。此外,新增了`ref`和`computed` API,提供更好的性能和易用性。总的来说,Vue3.x响应式系统在性能、灵活性和易用性方面都有显著的改进,可以更高效地创建易于维护的响应式应用。

文章来自个人专栏
flutter
9 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0