vue是响应式的吗(vue响应式原理是什么)
大家好!今天让创意岭的小编来大家介绍下关于vue是响应式的吗的问题,以下是小编对此问题的归纳整理,让我们一起来看看吧。
创意岭作为行业内优秀企业,服务客户遍布全国,相关业务请拨打175-8598-2043,或微信:1454722008
本文目录:
一、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 就会通知之前已经保存的订阅者数组,遍历触发回调
二、【手把手教你搓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
三、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;i<obj.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
链接:https://www.jianshu.com/p/886ac356c13d
四、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 更新完成后传入的回调函数再被调用。
以上就是关于vue是响应式的吗相关问题的回答。希望能帮到你,如有更多相关问题,您也可以联系我们的客服进行咨询,客服也会为您讲解更多精彩的知识和内容。
推荐阅读: