图解 Vue 响应式原理

Posted 前端日志

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图解 Vue 响应式原理相关的知识,希望对你有一定的参考价值。

最近部门分享,有同学提到了 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 响应式原理的主要内容,如果未能解决你的问题,请参考以下文章

    Vue.js响应式原理

    前端攻城狮该了解的 Vue.2x 响应式原理

    Vue响应式原理

    Vue响应式原理

    Vue源码之响应式原理(个人向)

    深入浅出 Vue 响应式原理!

    (c)2006-2024 SYSTEM All Rights Reserved IT常识