图解 Vue 响应式原理
Posted 前端日志
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图解 Vue 响应式原理相关的知识,希望对你有一定的参考价值。
最近部门分享,有同学提到了 Vue 响应式原理,大家在讨论时,发现一些同学对这一知识理解还不够深入,不能形成一个闭环,为了帮助大家理解这个问题,我重新过了一下 Vue 源码,并整理了多张流程图,便于大家理解。
本文 Vue 源码版本:2.6.11,为了便于理解,均有所删减。
本文将从以下两个方面进行探索:
从 Vue 初始化,到首次渲染生成 DOM 的流程。
从 Vue 数据修改,到页面更新 DOM 的流程。
先从最简单的一段 Vue 代码开始:
<template>
<div>
message
</div>
</template>
<script>
new Vue(
data()
return
message: "hello world",
;
,
);
</script>
这段代码很简单,最终会在页面上打印一个 hello world,它是如何实现的呢?
我们从源头:new Vue 的地方开始分析。
和 Dep 的关联, 初始化 Watcher 的时候,打上 Dep.target 标识,然后调用 get 方法进行页面渲染。加上上文的 Data,目前 Data、Dep、Watcher 三者的关系如下:我们再拉通串一下整个流程:Vue 通过 defineProperty
完成了 Data 中所有数据的代理,当数据触发 get 查询时,会将当前的 Watcher 对象加入到依赖收集池 Dep 中,当数据 Data 变化时,会触发 set 通知所有使用到这个 Data 的 Watcher 对象去 update 视图。
目前的整体流程如下:
上图的流程中 Data 和 Dep 都是 Vue 初始化时创建的,但现在我们并不知道 Wacher 是从哪里创建的,带着这个问题,我们接着往下探索。
上文中,我们分析了初始化 Vue 过程中处理数据的部分,接下来,我们分析一下数据渲染的部分。
其实 new Vue 执行到最后,会调用 mount 方法,将 Vue 实例渲染成 dom 。
方法完成页面更新。整体的流程如下:
好了,探索到这里,Vue 的响应式原理,已经被我们分析透彻了,如果你还没有明白,不妨再细品一下上图。
本来探索到上面的流程图就结束了,但好奇的我又想到了一个问题
vue数组响应式原理
参考技术A
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])// 如果是对象elseObject.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
以上是关于图解 Vue 响应式原理的主要内容,如果未能解决你的问题,请参考以下文章