vue响应式原理解析

# Vue响应式原理解析

首先定义了四个核心的js文件

- 1. observer.js 观察者函数,用来设置data的get和set函数,并且把watcher存放在dep中

- 2. watcher.js 监听者函数,用来设置dep.target开启依赖收集的条件,和触发视图的更新函数

- 3. compile.js 编译者函数,用来编译模版和实例化 watcher 函数

- 4. index.js 入口文件

注意dep函数就是一个壳子,用来存放watcher和触发watcher更新的

首先从index.js开始,定义函数 SelfVue

```js

function SelfVue (options) {

Object.keys(this.data).forEach(function(key) {

self.proxyKeys(key);

//遍历data,给每一个key执行函数 proxyKeys,定义get、set

//这样就可以通过 this.name 来获取data中的name值了

//否则的话必须通过 this.data.name 才能获取

});

observe(this.data); //观察this.data,设置好各种监听的规则

new Compile(options.el, this);//在这里处理编译和调用watcher的函数

options.mounted.call(this); // 所有事情处理好后执行mounted函数

}

```

然后看 observe函数:

```js

defineReactive: function(data, key, val) {

var dep = new Dep();//初始化dep,dep用来存放watcher

Object.defineProperty(data, key, {

get: function getter () {

//只有在watcher中才会设置Dep.target,所以只有在watcher中才会去增加监听

if (Dep.target) {//Dep.target就是保存的Watcher自己

dep.addSub(Dep.target);

}

return val;

},

set: function setter (newVal) {

if (newVal === val) {

return;

}

val = newVal;

dep.notify();

}

});

}

```

然后看watcher

```js

function Watcher(vm, exp, cb) {

this.value = this.get(); // 将自己添加到订阅器的操作

/*

在这里执行this.get(),也就是调用了 this.vm.data[this.exp]

即调用了观察者中的get函数:

首先给Dep.target赋值,在观察者函数中,打开了

if (Dep.target) {

dep.addSub(Dep.target); //所以在这里可以向dep中添加watcher函数

}

*/

}

Watcher.prototype = {

get: function() {

Dep.target = this; // 缓存自己

var value = this.vm.data[this.exp] // 强制执行监听器里的get函数

Dep.target = null; // 添加到 dep 中之后,再释放自己

return value;

}

}

```

这样每次 new watcher的时候就会实例化watcher

然后就会调用this.value = this.get();

然后就会执行 this.vm.data[this.exp]

就会调用观察者函数中的 get方法,由于此时设置了dep.target

所以就会保存watcher到dep中

```js

new Watcher(this.vm, exp, function (value) {

self.updateText(node, value);

});

```

再来看compile.js中是何时触发监听watcher函数的,该文件做了三件事情:

```js

this.fragment = this.nodeToFragment(this.el); //获取挂载元素为代码片段

this.compileElement(this.fragment);//划分该代码片段的类型,执行编译

this.el.appendChild(this.fragment);//挂载该代码片段到html上

```

所以核心代码是compileElement函数:

```js

[].slice.call(childNodes).forEach(function(node) {

var reg = /\{\{(.*)\}\}/;

var text = node.textContent;

if (self.isElementNode(node)) { //v-model指令、v-on:click方法

self.compile(node);

} else if (self.isTextNode(node) && reg.test(text)) {//文本节点

self.compileText(node, reg.exec(text)[1]);

}

if (node.childNodes && node.childNodes.length) {//子节点继续循环遍历

self.compileElement(node);

}

});

```

循环遍历模板代码,按照:文本节点、v-model指令、v-on:click方法做不同的逻辑处理:

但是都会用到该函数

```js

new Watcher(this.vm, exp, function (value) {

self.updateText(node, value);

});

```

如上所述,实例化 Watcher的时候,就是给模板中用到的exp,向dep中增加watcher函数,

而watcher函数包括的方法:更新和get函数。

所以遍历完模板后,实例化 watcher,然后就会执行 watcher 中的get函数,实现监听功能。

```js

Watcher.prototype = {

update: function() {

var value = this.vm.data[this.exp];

var oldVal = this.value;

if (value !== oldVal) {

this.value = value;

this.cb.call(this.vm, value, oldVal);

}

},

get: function() {

Dep.target = this; // 缓存自己

var value = this.vm.data[this.exp] // 强制执行监听器里的get函数

Dep.target = null; // 释放自己

return value;

}

};

```

---

待数据发生变化时,会触发观察者函数中的 set 函数:

```js

set: function setter (newVal) {

if (newVal === val) {

return;

}

val = newVal;

dep.notify();

}

```

然后就会通知dep更新,这里注意的是,如果该值没有在模板中使用,this.sub就是空数组,所以这里通知函数中也不会更新视图:

```js

notify: function() {

//虽然上面data的所有值发生变化的时候会触发set和dep.notify();

//但是在这里只是会循环遍历每个之前监听到的watcher---this.subs

//所以,如果在html中没有用到的数据,即使在methods中使用到了

//在这里也不会触发视图更新

this.subs.forEach(function(sub) {

sub.update();

});

}

```

如果模板中使用了两次data中的title:

<h2>{{title}}</h2>

<h1>{{title}}</h1>

则对data循环后,针对title变量,有两个watcher,存在针对该data值:title的this.sub数组中。

所以如果没有在模板中使用到的data,比如age变量,

在set函数中,由于模板中没有用到-->则不会执行new Watcher-->则不会赋值给dep.target-->则不会给 dep中收集依赖,保存watcher;

在get函数中,由于模板中没有用到,对应的dep.sub数组中就是空数组。所以即使set函数通知了dep.notify函数,也会应为是空数组,导致不会执行循环,也无法触发watcher的更新视图函数

原文地址:https://www.cnblogs.com/xiaozhumaopao/p/12038895.html

时间: 2024-10-06 14:11:44

vue响应式原理解析的相关文章

深度解析 Vue 响应式原理

深度解析 Vue 响应式原理 该文章内容节选自团队的开源项目 InterviewMap.项目目前内容包含了 JS.网络.浏览器相关.性能优化.安全.框架.Git.数据结构.算法等内容,无论是基础还是进阶,亦或是源码解读,你都能在本图谱中得到满意的答案,希望这个面试图谱能够帮助到大家更好的准备面试. Vue 初始化 在 Vue 的初始化中,会先对 props 和 data 进行初始化 Vue.prototype._init = function(options?: Object) { // ...

深入Vue响应式原理

深入Vue.js响应式原理 一.创建一个Vue应用 new Vue({ data() { return { name: 'yjh', }; }, router, store, render: h => h(App), }).$mount('#app'); 二.实例化一个Vue应用到底发生了什么? this._init() callHook(vm, 'beforeCreate') observe(vm._data) vm._data = vm.$options.data() proxy(vm, _

Vue响应式原理

前面的话 Vue最显著的特性之一便是不太引人注意的响应式系统(reactivity system).模型层(model)只是普通JS对象,修改它则更新视图(view).这会让状态管理变得非常简单且直观,不过理解它的工作原理以避免一些常见的问题也是很重要的本文将详细介绍Vue响应式系统的底层细节 追踪变化 把一个普通JS对象传给Vue实例的data选项,Vue将遍历此对象所有的属性,并使用Object.defineProperty把这些属性全部转为getter/setter.Object.defi

深入探讨vue响应式原理

现在是时候深入一下了!Vue 最独特的特性之一,是其非侵入性的响应式系统.数据模型仅仅是普通的 JavaScript 对象.而当你修改它们时,视图会进行更新.这使得状态管理非常简单直接,不过理解其工作原理同样重要,这样你可以避开一些常见的问题.在这个章节,我们将研究一下 Vue 响应式系统的底层的细节. 当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 get

vue.js响应式原理解析与实现

从很久之前就已经接触过了angularjs了,当时就已经了解到,angularjs是通过脏检查来实现数据监测以及页面更新渲染.之后,再接触了vue.js,当时也一度很好奇vue.js是如何监测数据更新并且重新渲染页面.今天,就我们就来一步步解析vue.js响应式的原理,并且来实现一个简单的demo. 首先,先让我们来了解一些基础知识. 基础知识 Object.defineProperty es5新增了Object.defineProperty这个api,它可以允许我们为对象的属性来设定gette

Vue响应式原理深入解析

Vue最明显的特性之一便是响应式系统,其数据模型即是普通的 JavaScript 对象.而当你读取或写入它们时,视图便会进行响应操作. 响应式data: <div id = "exp">{{ message }}</div> const vm = new Vue({ el: '#exp', data: { message: 'This is A' } }) vm.message = 'This is B' // 响应式 vm._message = 'This i

vue 响应式原理

Vue 采用声明式编程替代过去的类 Jquery 的命令式编程,并且能够侦测数据的变化,更新视图.这使得我们可以只关注数据本身,而不用手动处理数据到视图的渲染,避免了繁琐的 DOM 操作,提高了开发效率.不过理解其工作原理同样重要,这样可以回避一些常见的问题,下面我们来介绍一下 Vue 是如何侦测数据并响应视图的. Object.defineProperty Vue 数据响应核心就是使用了 Object.defineProperty 方法( IE9 + ) . var obj = {}; Obj

关于Vue响应式原理

Vue最独特的特性之一,是其非侵入性的响应式系统.数据模型仅仅是普通的javascript对象.而当你修改他们时,视图会进行更新.这使得状态管理非常简单直接,不过理解其工作原理同样重要. 当你把一个普通的javascript对象传入Vue实例作为data选项,Vue将遍历此对象所有属性,并使用Object.defineProperty 把这些属性全部转为getter/setter.Object.defineProperty 是es5中一个无法shim的特性,这也就是Vue不支持IE8以及更低版本

vue响应式原理整理

vue是数据响应性,这是很酷的一个地方.本文只为理清逻辑.详细请看官方文档 https://cn.vuejs.org/v2/guide/reactivity.html vue的data在处理数据时候,会遍历data内对象的所有属性,并使用Object.defineProperty将属性转为getter/setter. 这里的getter/setter对用户是不可见的,但是方便vue对数据进行内部跟踪,来维护数据. 用Object.defineProperty这是一个ES5无法支持特性,所有vue