广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >Vue响应式原理的示例详解
  • 849
分享到

Vue响应式原理的示例详解

2024-04-02 19:04:59 849人浏览 薄情痞子
摘要

Vue 最独特的特性之一,是非侵入式的响应系统。数据模型仅仅是普通的 javascript 对象。而当你修改它们时,视图会进行更新。聊到 Vue 响应式实现原理,众多开发者都知道实现

Vue 最独特的特性之一,是非侵入式的响应系统。数据模型仅仅是普通的 javascript 对象。而当你修改它们时,视图会进行更新。聊到 Vue 响应式实现原理,众多开发者都知道实现的关键在于利用 Object.defineProperty , 但具体又是如何实现的呢,今天我们来一探究竟。

为了通俗易懂,我们还是从一个小的示例开始:

<body>
  <div id="app">
    {{ message }}
  </div>
  <script>
    var app = new Vue({
      el: '#app',
      data: {
        message: 'Hello Vue!'
      }
    })
</script>
</body>

我们已经成功创建了第一个 Vue 应用!看起来这跟渲染一个字符串模板非常类似,但是 Vue 在背后做了大量工作。现在数据和 DOM 已经被建立了关联,所有东西都是响应式的。我们要怎么确认呢?打开你的浏览器的 JavaScript 控制台 (就在这个页面打开),并修改 app.message的值,你将看到上例相应地更新。修改数据便会自动更新,Vue 是如何做到的呢?

通过 Vue 构造函数创建一个实例时,会有执行一个初始化的操作:

function Vue (options) {
    this._init(options);
}

这个 _init初始化函数内部会初始化生命周期、事件、渲染函数、状态等等:

initLifecycle(vm);
      initEvents(vm);
      initRender(vm);
      callHook(vm, 'beforeCreate');
      initInjections(vm);
      initState(vm);
      initProvide(vm);
      callHook(vm, 'created');

因为本文的主题是响应式原理,因此我们只关注 initState(vm) 即可。它的关键调用步骤如下:

function initState (vm) {
  initData(vm);
}

function initData(vm) {
  // data就是我们创建 Vue实例传入的 {message: 'Hello Vue!'}
  observe(data, true );
}

function observe (value, asRootData) {
  ob = new Observer(value);
}

var Observer = function Observer (value) {
  this.walk(value);
}

Observer.prototype.walk = function walk (obj) {
  var keys = Object.keys(obj);
  for (var i = 0; i < keys.length; i++) {
    // 实现响应式关键函数
    defineReactive$$1(obj, keys[i]);
  }
};
}

我们来总结一下上面 initState(vm)流程。初始化状态的时候会对应用的数据进行检测,即创建一个 Observer 实例,其构造函数内部会执行原型上的 walk方法。walk方法的主要作用便是 遍历数据的所有属性,并把每个属性转换成响应式,而这转换的工作主要由 defineReactive$$1 函数完成。

function defineReactive$$1(obj, key, val) {
  var dep = new Dep();
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      var value = getter ? getter.call(obj) : val;
      if (Dep.target) {
        dep.depend();
        if (childOb) {
          childOb.dep.depend();
          if (Array.isArray(value)) {
            dependArray(value);
          }
        }
      }
      return value
    },
    set: function reactiveSetter(newVal) {
      var value = getter ? getter.call(obj) : val;
      
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      
      if (customSetter) {
        customSetter();
      }
      // #7981: for accessor properties without setter
      if (getter && !setter) { return }
      if (setter) {
        setter.call(obj, newVal);
      } else {
        val = newVal;
      }
      childOb = !shallow && observe(newVal);
      dep.notify();
    }
  });
}

defineReactive$$1函数内部使用Object.defineProperty 来监测数据的变化。每当从 obj 的 key 中读取数据时,get 函数被触发;每当往 obj 的 key 中设置数据时,set 函数被触发。我们说修改数据触发 set 函数,那么 set 函数是如何更新视图的呢?拿本文开头示例分析:

<div id="app">
    {{ message }}
</div>

该模板使用了数据 message, 当 message 的值发生改变的时候,应用中所有使用到 message 的视图都能触发更新。在 Vue 的内部实现中,先是收集依赖,即把用到数据 message 的地方收集起来,然后等数据发生改变的时候,把之前收集的依赖全部触发一遍就可以了。也就是说我们在上述的 get 函数中收集依赖,在 set 函数中触发视图更新。那接下来的重点就是分析 get 函数和 set 函数了。先看 get 函数,其关键调用如下:

get: function reactiveGetter () {
        if (Dep.target) {
          dep.depend();
        }
 }
 
Dep.prototype.depend = function depend () {
    if (Dep.target) {
      Dep.target.aDDDep(this);
    }
 };
 
Watcher.prototype.addDep = function addDep (dep) {
  dep.addSub(this);
}

 Dep.prototype.addSub = function addSub (sub) {
    this.subs.push(sub);
 };
 其中 Dep 构造函数如下:
 var Dep = function Dep () {
   this.id = uid++;
   this.subs = [];
 };

上述代码中Dep.target的值是一个Watcher实例,稍后我们再分析它是何时被赋值的。我们用一句话总结 get 函数所做的工作:把当前 Watcher 实例(也就是Dep.target)添加到 Dep 实例的 subs 数组中。在继续分析 get 函数前,我们需要弄清楚 Dep.target 的值何时被赋值为 Watcher 实例,这里我们需要从 mountComponent这个函数开始分析:

function mountComponent (vm, el, hydrating) {
  updateComponent = function () {
    vm._update(vm._render(), hydrating);
  };
  new Watcher(vm, updateComponent, noop, xxx);
}
// Wather构造函数下
var Watcher = function Watcher (vm, expOrFn, cb) {
  if (typeof expOrFn === 'function') {
    this.getter = expOrFn;
  } else {
    this.getter = parsePath(expOrFn);
  }
   this.value = this.get();
}

Watcher.prototype.get = function get () {
   pushTarget(this);
   value = this.getter.call(vm, vm);
}

function pushTarget (target) {
    targetStack.push(target);
    Dep.target = target;
}

由上述代码我们知道mountComponent函数会创建一个 Watcher 实例,在其构造函数中最终会调用 pushTarget函数,把当前 Watcher 实例赋值给 Dep.target。另外我们注意到,创建 Watcher 实例这个动作是发生在函数mountComponent内部,也就是说 Watcher 实例是组件级别的粒度,而不是说任何用到数据的地方都新建一个 Watcher 实例。现在我们再来看看 set 函数的主要调用过程:

set: function reactiveSetter (newVal) {
  dep.notify();
}

Dep.prototype.notify = function notify () {
   var subs = this.subs.slice();
   for (var i = 0, l = subs.length; i < l; i++) {
      subs[i].update();
    }
}

Watcher.prototype.update = function update () {
  queueWatcher(this);
}

 Watcher.prototype.update = function update () {
   // queue是一个全局数组
   queue.push(watcher);
   nextTick(flushSchedulerQueue);
 }
 
 // flushSchedulerQueue是一个全局函数
 function flushSchedulerQueue () {
    for (index = 0; index < queue.length; index++) {
      watcher = queue[index];
      watcher.run();
    }
 }
 
Watcher.prototype.run = function run () {
   var value = this.get();
}

set 函数内容有点长,但上述代码都是精简过的,应该不难理解。当改变应用数据的时候,触发 set 函数执行。它会调用 Dep 实例的 notify()方法,而 notify 方法又会把当前 Dep 实例收集的所有 Watcher 实例的 update 方法调用一遍,以达到更新所有用到该数据的视图部分。我们继续看 Watcher 实例的 update 方法做了什么。update 方法会把当前的 watcher 添加到数组 queue 中,然后把 queue 中每个 watcher 的 run 方法执行一遍。run 方法内部会执行 Wather 原型上的 get 方法,后续的调用在前文分析 mountComponent 函数中都有描述,在此就不再赘述。总结来说,最终 update 方法会触发 updateComponent函数:

updateComponent = function () {
  vm._update(vm._render(), hydrating);
};

Vue.prototype._update = function (vnode, hydrating) {
  vm.$el = vm.__patch__(prevVnode, vnode);
}

这里我们注意到 _update 函数的第一个参数是 vnode 。vnode 顾名思义是虚拟节点的意思,它是一个普通对象,该对象的属性上保存了生成 DOM 节点所需要数据。说到虚拟节点你是不是很容易就联想到虚拟 DOM 了呢,没错 Vue 中也使用了虚拟 DOM。前文说到 Wather 是和组件相关的,组件内部的更新就用虚拟 DOM 进行对比和渲染。_update 函数内部调用了 patch 函数,通过该函数对比新旧两个 vnode 之间的不同,然后根据对比结果找出需要更新的节点进行更新。

注:本文分析示例基于 Vue v2.6.14 版本。

到此这篇关于Vue响应式原理的示例详解的文章就介绍到这了,更多相关Vue响应式原理内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Vue响应式原理的示例详解

本文链接: https://www.lsjlt.com/news/141495.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Vue响应式原理的示例详解
    Vue 最独特的特性之一,是非侵入式的响应系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。聊到 Vue 响应式实现原理,众多开发者都知道实现...
    99+
    2022-11-13
  • 详解VUE响应式原理
    目录1、响应式原理基础2、核心对象:Dep与Watcher3、收集依赖与更新依赖3.1 收集依赖3.2 更新依赖4、源码调试4.1 测试的页面代码1、对象说明2、Dep与Watche...
    99+
    2022-11-12
  • vue-next响应式原理的示例分析
    这篇文章给大家分享的是有关vue-next响应式原理的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。预备知识无论是阅读这篇文章,还是阅读 vue-next 响应式模块的源...
    99+
    2022-10-19
  • Vue中响应式原理的示例分析
    这篇文章主要介绍了Vue中响应式原理的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Vue 嘴显著的特性之一便是响应式系统(reac...
    99+
    2022-10-19
  • Vue响应式系统的原理详解
    目录vue响应式系统的基本原理1.回顾一下Object.defineProperty的用法2.实战1:使用 Object.defineProperty 对 person的age属性 ...
    99+
    2022-11-12
  • VUE响应式原理的实现详解
    目录总结前言 相信vue学习者都会发现,vue使用起来上手非常方便,例如双向绑定机制,让我们实现视图、数据层的快速同步,但双向绑定机制实现的核心数据响应的原理是怎么样的呢,接下来让我...
    99+
    2022-11-13
  • 一文详解Vue中响应式原理
    废话不多说,直接进入正题,响应式在日常开发中的应用是很常见的,这里举个简单的例子:let a=3 let b=a*10 console.log(b)//30 a=4 console.log(b)//40这时候我们想让b=4*10,这样显然是...
    99+
    2023-05-14
    前端 Vue.js 设计模式
  • Vue响应式原理Observer、Dep、Watcher的示例分析
    小编给大家分享一下Vue响应式原理Observer、Dep、Watcher的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧...
    99+
    2022-10-19
  • Vuejs响应式原理的示例分析
    小编给大家分享一下Vuejs响应式原理的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!响应式原理> vuejs中的...
    99+
    2022-10-19
  • vue数据响应式原理中数组的示例分析
    这篇文章主要介绍vue数据响应式原理中数组的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!src/core/observer/index.jssrc/core/observer/array.js arrayM...
    99+
    2023-06-29
  • 详解vue数据响应式原理之数组
    目录src/core/observer/index.jssrc/core/observer/array.js arrayMethods总结src/core/observer/inde...
    99+
    2022-11-13
  • 详解Vue3的响应式原理解析
    目录Vue2响应式原理回顾Vue3响应式原理剖析嵌套对象响应式避免重复代理总结 Vue2响应式原理回顾 // 1.对象响应化:遍历每个key,定义getter、setter //...
    99+
    2022-11-12
  • Vue模拟响应式原理底层代码实现的示例
    目录1.Vue.js功能:2.Observer.js功能(数据劫持):3.Compiler.js功能:4.Dep.js功能:5.Watcher.js功能:整体分析Vue的基本结构如下...
    99+
    2022-11-12
  • 详解vue3 响应式的实现原理
    目录核心设计思想Vue.js 2.x 响应式Vue.js 3.x 响应式依赖收集:get 函数派发通知:set 函数总结源码参考核心设计思想 除了组件化,Vue.js 另一个核心设计...
    99+
    2022-11-13
  • Vue组合式API--setup中定义响应式数据的示例详解
    目录一、Options API(选项式API)的弊端1.1 什么是选项式API1.2 选项式API的弊端二、Composition API(组合式API)概述2.1 Composit...
    99+
    2022-11-13
  • 一文详解Vue3响应式原理
    目录回顾 vue2.x 的响应式vue3的响应式回顾 vue2.x 的响应式 实现原理: 对象类型:通过object.defineProperty()对属性的读取、修改进行拦截(数据...
    99+
    2022-11-13
  • Vue响应式原理及双向数据绑定示例分析
    目录前言响应式原理双向数据绑定前言 之前公司招人,面试了一些的前端同学,因为公司使用的前端技术是Vue,所以免不了问到其响应式原理和Vue的双向数据绑定。但是这边面试到的80%的同学...
    99+
    2022-11-13
  • vue3响应式Object代理对象的读取示例详解
    目录正文读取属性xx in objfor ... in正文 从这一章开始,作者将更新深入的讲解响应式,尤其是vue3响应式的具体的实现。其实在前面一章,如果你仔细阅读,你是可以实现一...
    99+
    2022-11-13
    vue响应式Object读取 vue Object
  • 图解Vue 响应式流程及原理
    目录阅读本文能够帮助你什么?一、组件化流程1. 整个new Vue阶段做了什么?2. 普通dom元素如何渲染到页面?3. 组件如何渲染到页面?4. Vue组件化简化流程二、响应式流程...
    99+
    2022-11-13
  • 一文详解Vue2/Vue3的响应式原理
    this.$delete(this.student, 'name');// 删除student对象属性name this.$set(this.student, 'age', '21');// ...
    99+
    2023-05-14
    Vue.js
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作