本文作者:qiaoqingyi

vue2响应式原理源码(vue20响应式原理)

qiaoqingyi 2023-04-09 761

本篇文章给大家谈谈vue2响应式原理源码,以及vue20响应式原理对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

vue.js响应式原理

vue2响应式原理主要通过 Object.fefineProperty

当把一个普通的js对象传入 Vue 实例作为 data 选项,Vue将遍历此对象所有的属性,并使用 Object.fefineProperty 把这些属性全部转为 getter/setter 。 Object.defineProperty 是ES5中一个无法 shim 的特性,这也是Vue 不支持IE8 以及更低版本浏览器的原因。

vue3响应式原理主要通过 Proxy 代理对象

虚拟dom就是用普通的js对象来描述 DOM 对象

真实dom成员复杂,虚拟dom可以用简洁的方式来表示实现真实dom,创建虚拟dom开销小。

虚拟dom库

使用模块

Snabbdom 核心

patch 整体过程分析

vue数组响应式原理

vue2中Object.defineProperty响应式只对对象有效,对数组无效,所以对数组做额外处理。我们知道,会改变数组本身的方法只有7个:sort, push, pop, slice, splice, shift, unshift,所以可以通过重写这些方法来达到数组响应式

解决方案:

1. 找到数组原型

2. 覆盖那些能够修改数组的更新方法,让他们在修改数组同时,还可以通知更新

3. 将得到的新的原型设置到数组实例原型上

4. 对数组内部元素实现响应式

// 实现数组响应式// 1. 替换数组原型中7个方法constoriginalProto=Array.prototype// 克隆体原数组原型constarrayProto=Object.create(originalProto)// 可修改数组的7个方法 , 'sort'constchangeMethods=['push','pop','shift','unshift','slice','splice','sort']//  2. 在克隆的原型上,覆盖那些能够修改数组的更新方法,让他们在修改数组同时,还可以通知更新changeMethods.forEach(method={arrayProto[method]=function(){// 进行原始操作originalProto[method].apply(this,arguments)// 覆盖操作:增加更新通知console.log(`数组正在执行${method}方法`);}})// 对象响应化functiondefineReactive(obj,key,value){Object.defineProperty(obj,key,{get(){console.log('获取'+key);returnvalue},set(newVal){if(newVal!==value){// console.log(newVal);// console.log(JSON.stringify(obj[key]));console.log(`正在改变${key}值:从${obj[key]}变为${newVal}`)value=newVal}}})}functionobserver(obj){// 不是对象或者为null,不做响应式,结束if(typeofobj!=='object'||obj===null)return;// 如果是数组,修改其实例的原型if(Array.isArray(obj)){// 3. 将得到的新的原型设置到数组实例原型上obj.__proto__=arrayProto// 4. 对数组内的元素,同样进行响应化for(leti=0;iobj.length;i++){// console.log(obj[i]);observer(obj[i])}// 如果是对象}else{Object.keys(obj).forEach(key={console.log(obj,key,obj[key]);defineReactive(obj,key,obj[key])})}}obj=[{a:1},2,7,5,3]observer(obj)obj.push(4)// 数组正在执行push方法obj.pop()// 数组正在执行pop方法obj[0].a=2// 获取a    // 正在改变a值:从1变为2obj.sort()// 数组正在执行sort方法console.log(obj);// [ 2, 3, 5, 7, { a: [Getter/Setter] } ]console.log(obj[4].a);// 获取a  // 2

链接

vue2响应式原理源码(vue20响应式原理)

vue2数据响应式原理

vue2响应式原理由 Observer 类, Dep 类和 Watcher 类互相调用实现, Observer 类是把一个普通的object类变成每一层都能相应的类, Dep 类的作用是添加,移除,通知和收集订阅者, Watcher 类是订阅者,主要功能是把当数据改变的时候,去调用回调函数,修改dom节点

那么是怎么实现响应式的呢,首先是一个函数,要先转换为可响应的,那就需要用到 Observer 类

这个 observe 函数就是对 Observer 类做多了一层封装

而 Observer 类是通过 Object.defineProperty 来监控数据的获取和改变的

关键在于 defineReactive 方法,这个方法是对 Object.defineProperty 做了一层封装,并且对对象的每一层做递归调用,实现了每一层都有响应监控

但是是怎么知道现在要保存哪一个 Watcher 实例到订阅者数组里面的呢?其实就是用了这个 Dep.target , Dep.target 相当于 window.target ,全局只有一个,全局也能访问

首先得先讲一讲 Watcher 类,我们先回到上面的index.js,对象要让 Watcher 类进行监听,而 Watcher 有3个参数,第一个是监听的对象,第二个是监听的属性,比如 a.b.c.d ,第三个是属性改变后触发的回调函数

先来讲一下 parsePath ,这个在工具类里,作用是访问 a.b.c.d 这种链式属性

首先是触发了 Watcher 的 get() 方法,把当前实例保存在了 Dep.target 里面

然后在调用 parsePath 获取属性值的过程中,会挨个访问响应对象的属性,就会触发相应的 getter ,我们回到 defineReactive.js ,可以发现这时候相应属性的 getter 就会把 Dep.target 也就是相应的 Watcher 的实例保存在了 Dep 类的订阅者数组里面

最后,在改变属性的时候,相应属性的 setter 就会通知之前已经保存的订阅者数组,遍历触发回调

vue2响应式原理总结

vue组件实例化时,会对data属性深度遍历(遇到数组或者对象)为每一个属性添加数据劫持。数据劫持就是使用Object.defineProperty(de fai in pro pu tei)方法添加get/set方法。

在这个过程中会实例化一个Dep类。

1.在get拦截器里触发dep实例的depend方法,进行依赖收集,实质是在dep的实例属性sub数组中添加依赖这个属性的watcher实例。

2.在set拦截器里触发dep实例的notify方法,对收集到的所有依赖派发更新,(watcher的update方法)

vue组件实例化时会实例化一个渲染watcher,渲染watcher实例化过程会做两件事情。

1.创建vnode,在这个过程中,访问了data属性,触发了get方法,完成了依赖收集。

2.触发了子组件的实例化,子组件实例化又会重复上述数据劫持的过程。

这个过程就是对组件树的深度遍历。

结合组件生命周期来看整个过程,父组件会先触发created钩子,子组件后触发created钩子。而子组件mouted钩子会先执行,父组件的mouted钩子后执行。

分步骤记忆

1、实现页面不刷新的原理

2、页面视图刷新的原理

实现页面不刷新

1.hash

2.history

3.abstract:支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式。

1.hash(哈希模式),#符号后边是浏览器行为,在改变的时候不对页面进行刷新(重新请求URL)(监听hashChange事件)

2.history模式,H5新增了pushState,replaceState连个新API,可以修改历史记录却不会使浏览器刷新页面。

视图更新原理

其原理就是vue的响应式更新dom的原理,m = v

m是数据,也就是在vue-router install时在根组件(root vue component)添加了_route属性,在匹配到对应路由后更新了_route属性值,继而触发了该属性值的渲染watcher,在继而触发dom更新。

两种模式的不同

1.部署时,history模式需要服务端处理所有可能的路径(例如配置nginx的配置文件),防止出现404。哈希模式则不需要。

2.URL表示不同。

v-model指令就是 v-bind:value 和 @input 的语法糖。

它即可以支持原生表单元素,也可以支持自定义组件

在自定义组件中其实际是这样的:

它的实现通过自定义render函数, 缓存了 vnode

Vue 在更新 DOM 时是异步执行的,只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。

如果同一个 watcher 被多次触发,只会被推入到队列中一次。在缓冲时会去除重复数据避免不必要的计算和 DOM 操作。

$nextTick(cb) 目的是在DOM 更新完成后传入的回调函数再被调用。

vue2中computed原理

要理解这个,首先要理解vue2的数据响应式原理,因为computed这个API的实现是建立在数据响应式的基础之上的。

这里附上vue响应式原理的地址: vue2数据响应式原理

在vue的watcher实例中配置了lazy,dirty,value属性,就是用来配合实现computed的API。vue在初始化computed时,会给每一个computed属性配置一个watcher,并配置lazy的值为true。在new Watcher时,还会设置dirty为true。由于lazy为true,这个时候并不会执行computed配置的get方法,也就是说不会去计算出这个计算属性的值出来,那么什么时候才计算呢?就是在执行render函数时,会去取计算属性的值,这个时候,会执行计算属性的getter。在getter里面,会先判断dirty的值(为true,则表示当前计算属性的值为脏值,已经过期了,需要重新计算出来;为false则表示当前的值是最新的,不需要重新计算,直接取缓存里面的值就行了,也就是value字段的值)。如果为true,则调用watcher.evaluate方法计算最新的值出来,然后将dirty设为false,把最新的值赋值给value。在计算最新的值时,也就是执行我们在computed对象中配置的相应的get函数。根据之前讲的响应式原理,会先将当前的watcher挂到dep的静态属性target上,然后执行get,在这个过程中,会使用到data中的属性,然后进行依赖收集,将computed的watcher存到data数据对应的dep中去。完了之后,将watcher从targetStack中弹出,这是dep的静态属性target的值又变成了render函数的watcher。以上步骤执行完后,会判断当前dep的target是否还有值,有的话,会执行watcher.depend()。这一步是为了让依赖的data对应的dep中不仅是收集到computed的wathcer,还要收集render函数的watcher,因为当我们依赖的data改变时,不仅要通知到computed的watcher,还要通知render函数的watcher,达到重渲染的目的。然后会把最新的value值返回,这时,render函数里面终于拿到了计算属性的最新值了。假如后面继续用到了这个计算属性,那么在执行计算属性的getter时,跟之前一样,会先判断dirty值,这个时候,dirty的值为false,则直接返回value值,也就是之前说的取缓存的值,不会再次运行一遍计算属性的get函数了。

当某个时候,计算属性依赖的data数据变了,则会先后触发computed的watcher,还有render函数的watcher。在将vue的数据响应式原理时,我们知道,数据改变时,是会触发watcher的update方法。在这个方法里面,首先判断lazy是否为true,这就是针对计算属性设计的。我们可以看看源码片段:

如果为true,则只做了一件非常简单的事,就是把dirty设为了true,然后就没了。这个时候会去执行render函数的watcher.update。也就是把render函数的执行交给了nextTick去管理,这也是之前讲过的。在执行render函数时,又使用到了这个计算属性,那么,则会执行这个计算属性的getter,判断dirty是否为true,由于刚刚在执行computed的wathcer.update时,把dirty设为了true,这个时候肯定会执行watcher.evaluate重新去计算最新的值,也就是执行我们配置在computed里面的get函数。接下来的其实跟前面所讲的都是一样的了。

这里附上关于computed初始化的源码片段:

以上就是个人对于computed原理的理解了。

【手把手教你搓Vue响应式原理】(一)初识Vue响应式

在讲这个之前,首先要明白一点,这个所谓的响应式,其实本身就是对 MVVM 的理解。

MVVM 其实就是所谓的 Modal View ViewModal 。

简单理解,就是你的 data 中的数据,和 template 模板中的界面,本身就是两个东西。

但是, Vue 给你做了一层中间的 ViewModal ,让视图上的改变能反映到 data 中, data 中的改变能反映到视图上。

在这个反映过程中,ViewModal就是视图和数据的一个桥梁。

同样是让 a + 1 。

在 Vue 中,这个桥梁是你看不见的,因为 Vue 都帮你完成了视图和数据的变化传递。

而 React 就是侵入式的,因为要显式地声明 setState ,通过它,来设置变量的同时,设置视图的改变。

所以,所谓的侵入式,其实就是对于桥梁的侵入。

所以, Vue 的神奇之处就在于,不需要我们手动地显示调用 setState ,也就是这个桥梁, Vue 已经帮我们桥接上了。

要让 data 改变的同时,视图也发生改变,所以,问题的所在,就是我们需要监听,什么时候,这个变量发生了变量。

然而, ES5 中,就有那么一个特性,可以做到对于数据的劫持(监听)。

它就是 Object.defineProperty 。

Object.defineProperty( obj, prop, descriptor ) 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象,与此同时,它可以对 对象的一些额外底层的属性进行设置 。例如可以设置 writable , enumerable , configurable 等属性。

后面的额外属性设置,才是我们使用它的重点。

但是,我们使用的不是上面的几个属性,最主要的还是它的 get set ,可以对属性值的获取和设置操作进行拦截。

get主要是可以对值的获取进行拦截,,它必须要传入一个 return ,并且, 该函数的返回值会被用作属性的值 。我们可以来看一个例子:

由于设置了 get ,所以,输出 a.name 的时候直接会被拦截,走 get() 中的 return 所以,此时, a.name 的值应该是 你已经被拦截了!。

set主要是可以对值的设置进行拦截,该方法会接受一个参数,那就是 被赋予的新值 。我们可以来看一个例子:

由于设置了 set ,所以,设置值的时候会被拦截,走 set() 中的方法。

所以, Vue 能自动获取data中的改变,反映到视图的原因,就是有对于变量的获取和设置的劫持,当变量发生改变的同时, Vue 能在第一时间知道,并且对视图做出相应的改变操作。

而这把钥匙就是 Object.defineProperty 。

【尚硅谷】Vue源码解析之数据响应式原理

Object.defineProperty() - MDN

vue2响应式原理源码的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于vue20响应式原理、vue2响应式原理源码的信息别忘了在本站进行查找喔。

阅读
分享