search
HomeWeb Front-endVue.jsAn article to talk about the principle of Vue responsive implementation

An article to talk about the principle of Vue responsive implementation

#vue is an easy-to-use framework with many convenient functions integrated inside it. The most distinctive function is its responsive system hidden at the bottom. Component states are all responsive JavaScript objects. When you change them, the view updates, making state management easier and more intuitive. So, how is the Vue responsive system implemented? This article is also based on the understanding and imitation implementation after reading the Vue source code, so follow the author's ideas and let's explore Vue from the shallower to the deeper! [Related recommendations: vuejs video tutorial]

The Vue source code version of this article: 2.6.14. In order to facilitate understanding, the code is simplified.

How Vue implements data responsiveness

When you pass an ordinary JavaScript object into a Vue instance as the data option, Vue will traverse the object All properties, and use Object.defineProperty to convert all these properties into getters/setters, and then run around the getters/setters.

A summary of Vue's responsive system in one sentence is: Observer mode Object.defineProperty intercepts getter/setter

MDN ObjdefineProperty

Observer Pattern

What is Object.defineProperty?

Object.defineProperty() method will directly define a new property on an object, or modify an existing property of an object. and returns this object.

Simply put, the property defined in this way will trigger get when executing obj.xxx, and set will be triggered when executing obj.xxx = xxx. This is the key to responsiveness.

Object.defineProperty is a feature in ES5 that cannot be shimmed (cannot be implemented through polyfill), which is why Vue does not support IE8 and lower browsers.

Basic implementation of responsive system

Now, let’s implement a simple one based on Object.defineProperty Responsive update system as an "appetizer"

let data = {};
// 使用一个中间变量保存 value
let value = "hello";
// 用一个集合保存数据的响应更新函数
let fnSet = new Set();
// 在 data 上定义 text 属性
Object.defineProperty(data, "text", {
  enumerable: true,
  configurable: true,
  set(newValue) {
    value = newValue;
    // 数据变化
    fnSet.forEach((fn) => fn());
  },
  get() {
    fnSet.add(fn);
    return value;
  },
});

// 将 data.text 渲染到页面上
function fn() {
  document.body.innerText = data.text;
}
// 执行函数,触发读取 get
fn();

// 一秒后改变数据,触发 set 更新
setTimeout(() => {
  data.text = "world";
}, 1000);

Next we run this code in the browser and will get the desired effect

Through the above code, I think you are interested in responsive You already have a certain understanding of how the system works. In order to make this "appetizer" easy to digest, this simple reactive system still has many shortcomings. For example: data and response update functions are strongly coupled together through hard coding, only achieve a one-to-one situation, and not modular enough. Wait... So next, let's complete it one by one.

Design a complete responsive system

To design a complete responsive system, we need to first understand a prerequisite knowledge, what is the observer pattern?

What is the observer pattern?

It is a behavioral design pattern that allows you to define a subscription mechanism that can notify multiple other objects that "observe" the object when an object event occurs.

Objects that have some status worthy of attention are usually called targets. Because it needs to notify other objects when its own status changes, we also call it a publisher. . All other objects that wish to follow changes in the publisher's state are called subscribers. In addition, the publisher interacts directly with all subscribers only through the interface, and they all must have the same interface.

An article to talk about the principle of Vue responsive implementation

For example?:

You (i.e. the subscriber in the application) are interested in the weekly magazine of a certain bookstore, you give the boss (i.e. the application The publisher in ) left a phone number and asked the boss to call you as soon as a new weekly was available. Others who were interested in this weekly also left phone numbers for the boss. When the new weekly arrives, the boss calls one by one to notify readers to pick it up.

If a reader accidentally leaves a QQ number instead of a phone number, the old version will not be able to get through when calling, and the reader will not receive notifications. This is what we said above, it must have the same interface.

After understanding the observer pattern, we began to design a responsive system.

Abstract observer (subscriber) class Watcher

在上面的例子中,数据和响应更新函数是通过硬编码强耦合在一起的。而实际开发过程中,更新函数不一定叫fn,更有可能是一个匿名函数。所以我们需要抽像一个观察者(订阅者)类Watcher来保存并执行更新函数,同时向外提供一个update更新接口。

// Watcher 观察者可能有 n 个,我们为了区分它们,保证唯一性,增加一个 uid
let watcherId = 0;
// 当前活跃的 Watcher
let activeWatcher = null;

class Watcher {
  constructor(cb) {
    this.uid = watcherId++;
    // 更新函数
    this.cb = cb;
    // 保存 watcher 订阅的所有数据
    this.deps = [];
    // 初始化时执行更新函数
    this.get();
  }
  // 求值函数
  get() {
    // 调用更新函数时,将 activeWatcher 指向当前 watcher
    activeWatcher = this;
    this.cb();
    // 调用完重置
    activeWatcher = null;
  }
  // 数据更新时,调用该函数重新求值
  update() {
    this.get();
  }
}

抽象被观察者(发布者)类Dep

我们再想一想,实际开发过程中,data 中肯定不止一个数据,而且每个数据,都有不同的订阅者,所以说我们还需要抽象一个被观察者(发布者)Dep类来保存数据对应的观察者(Watcher),以及数据变化时通知观察者更新。

class Dep {
  constructor() {
    // 保存所有该依赖项的订阅者
    this.subs = [];
  }
  addSubs() {
    // 将 activeWatcher 作为订阅者,放到 subs 中
    // 防止重复订阅
    if(this.subs.indexOf(activeWatcher) === -1){
      this.subs.push(activeWatcher);
    }
  }
  notify() {
    // 先保存旧的依赖,便于下面遍历通知更新
    const deps = this.subs.slice()
    // 每次更新前,清除上一次收集的依赖,下次执行时,重新收集
    this.subs.length = 0;
    deps.forEach((watcher) => {
      watcher.update();
    });
  }
}

抽象 Observer

现在,WatcherDep只是两个独立的模块,我们怎么把它们关联起来呢?

答案就是Object.defineProperty,在数据被读取,触发get方法,Dep 将当前触发 get 的 Watcher 当做订阅者放到 subs中,Watcher 就与 Dep建立关系;在数据被修改,触发set方法,Dep就遍历 subs 中的订阅者,通知Watcher更新。

下面我们就来完善将数据转换为getter/setter的处理。

上面基础的响应式系统实现中,我们只定义了一个响应式数据,当 data 中有其他property时我们就处理不了了。所以,我们需要抽象一个 Observer类来完成对 data数据的遍历,并调用defineReactive转换为 getter/setter,最终完成响应式绑定。

为了简化,我们只处理data中单层数据。

class Observer {
  constructor(value) {
    this.value = value;
    this.walk(value);
  }
  // 遍历 keys,转换为 getter/setter
  walk(obj) {
    const keys = Object.keys(obj);
    for (let i = 0; i <p>这里我们通过参数 value 的闭包,来保存最新的数据,避免新增其他变量</p><pre class="brush:php;toolbar:false">function defineReactive(target, key, value) {
  // 每一个数据都是一个被观察者
  const dep = new Dep();
  Object.defineProperty(target, key, {
    enumerable: true,
    configurable: true,
    // 执行 data.xxx 时 get 触发,进行依赖收集,watcher 订阅 dep
    get() {
      if (activeWatcher) {
        // 订阅
        dep.addSubs(activeWatcher);
      }
      return value;
    },
    // 执行 data.xxx = xxx 时 set 触发,遍历订阅了该 dep 的 watchers,
    // 调用 watcher.updata 更新
    set(newValue) {
      // 如果前后值相等,没必要跟新
      if (value === newVal) {
        return;
      }
      value = newValue;
      // 派发更新
      dep.notify();
    },
  });
}

至此,响应式系统就大功告成了!!

测试

我们通过下面代码测试一下:

let data = {
  name: "张三",
  age: 18,
  address: "成都",
};
// 模拟 render
const render1 = () => {
  console.warn("-------------watcher1--------------");
  console.log("The name value is", data.name);
  console.log("The age value is", data.age);
  console.log("The address value is", data.address);
};
const render2 = () => {
  console.warn("-------------watcher2--------------");
  console.log("The name value is", data.name);
  console.log("The age value is", data.age);
};
// 先将 data 转换成响应式
new Observer(data);
// 实例观察者
new Watcher(render1);
new Watcher(render2);

在浏览器中运行这段代码,和我们期望的一样,两个render都执行了,并且在控制台上打印了结果。

An article to talk about the principle of Vue responsive implementation

我们尝试修改 data.name = '李四 23333333',测试两个 render 都会重新执行:

An article to talk about the principle of Vue responsive implementation

我们只修改 data.address = '北京',测试一下是否只有render 1回调都会重新执行:

An article to talk about the principle of Vue responsive implementation

都完美通过测试!!?

总结

An article to talk about the principle of Vue responsive implementation

Vue响应式原理的核心就是ObserverDepWatcher,三者共同构成 MVVM 中的 VM

Observer中进行数据响应式处理以及最终的WatcherDep关系绑定,在数据被读的时候,触发get方法,将 Watcher收集到 Dep中作为依赖;在数据被修改的时候,触发set方法,Dep就遍历 subs 中的订阅者,通知Watcher更新。

本篇文章属于入门篇,并非源码实现,在源码的基础上简化了很多内容,能够便于理解ObserverDepWatcher三者的作用和关系。

本文的源码,以及作者学习 Vue 源码完整的逐行注释源码地址:github.com/yue1123/vue…

(学习视频分享:web前端开发编程基础视频

The above is the detailed content of An article to talk about the principle of Vue responsive implementation. For more information, please follow other related articles on the PHP Chinese website!

Statement
This article is reproduced at:掘金社区. If there is any infringement, please contact admin@php.cn delete
Vue.js's Function: Enhancing User Experience on the FrontendVue.js's Function: Enhancing User Experience on the FrontendApr 19, 2025 am 12:13 AM

Vue.js improves user experience through multiple functions: 1. Responsive system realizes real-time data feedback; 2. Component development improves code reusability; 3. VueRouter provides smooth navigation; 4. Dynamic data binding and transition animation enhance interaction effect; 5. Error processing mechanism ensures user feedback; 6. Performance optimization and best practices improve application performance.

Vue.js: Defining Its Role in Web DevelopmentVue.js: Defining Its Role in Web DevelopmentApr 18, 2025 am 12:07 AM

Vue.js' role in web development is to act as a progressive JavaScript framework that simplifies the development process and improves efficiency. 1) It enables developers to focus on business logic through responsive data binding and component development. 2) The working principle of Vue.js relies on responsive systems and virtual DOM to optimize performance. 3) In actual projects, it is common practice to use Vuex to manage global state and optimize data responsiveness.

Understanding Vue.js: Primarily a Frontend FrameworkUnderstanding Vue.js: Primarily a Frontend FrameworkApr 17, 2025 am 12:20 AM

Vue.js is a progressive JavaScript framework released by You Yuxi in 2014 to build a user interface. Its core advantages include: 1. Responsive data binding, automatic update view of data changes; 2. Component development, the UI can be split into independent and reusable components.

Netflix's Frontend: Examples and Applications of React (or Vue)Netflix's Frontend: Examples and Applications of React (or Vue)Apr 16, 2025 am 12:08 AM

Netflix uses React as its front-end framework. 1) React's componentized development model and strong ecosystem are the main reasons why Netflix chose it. 2) Through componentization, Netflix splits complex interfaces into manageable chunks such as video players, recommendation lists and user comments. 3) React's virtual DOM and component life cycle optimizes rendering efficiency and user interaction management.

The Frontend Landscape: How Netflix Approached its ChoicesThe Frontend Landscape: How Netflix Approached its ChoicesApr 15, 2025 am 12:13 AM

Netflix's choice in front-end technology mainly focuses on three aspects: performance optimization, scalability and user experience. 1. Performance optimization: Netflix chose React as the main framework and developed tools such as SpeedCurve and Boomerang to monitor and optimize the user experience. 2. Scalability: They adopt a micro front-end architecture, splitting applications into independent modules, improving development efficiency and system scalability. 3. User experience: Netflix uses the Material-UI component library to continuously optimize the interface through A/B testing and user feedback to ensure consistency and aesthetics.

React vs. Vue: Which Framework Does Netflix Use?React vs. Vue: Which Framework Does Netflix Use?Apr 14, 2025 am 12:19 AM

Netflixusesacustomframeworkcalled"Gibbon"builtonReact,notReactorVuedirectly.1)TeamExperience:Choosebasedonfamiliarity.2)ProjectComplexity:Vueforsimplerprojects,Reactforcomplexones.3)CustomizationNeeds:Reactoffersmoreflexibility.4)Ecosystema

The Choice of Frameworks: What Drives Netflix's Decisions?The Choice of Frameworks: What Drives Netflix's Decisions?Apr 13, 2025 am 12:05 AM

Netflix mainly considers performance, scalability, development efficiency, ecosystem, technical debt and maintenance costs in framework selection. 1. Performance and scalability: Java and SpringBoot are selected to efficiently process massive data and high concurrent requests. 2. Development efficiency and ecosystem: Use React to improve front-end development efficiency and utilize its rich ecosystem. 3. Technical debt and maintenance costs: Choose Node.js to build microservices to reduce maintenance costs and technical debt.

React, Vue, and the Future of Netflix's FrontendReact, Vue, and the Future of Netflix's FrontendApr 12, 2025 am 12:12 AM

Netflix mainly uses React as the front-end framework, supplemented by Vue for specific functions. 1) React's componentization and virtual DOM improve the performance and development efficiency of Netflix applications. 2) Vue is used in Netflix's internal tools and small projects, and its flexibility and ease of use are key.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

SublimeText3 Linux new version

SublimeText3 Linux new version

SublimeText3 Linux latest version

Dreamweaver Mac version

Dreamweaver Mac version

Visual web development tools

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

Powerful PHP integrated development environment

SecLists

SecLists

SecLists is the ultimate security tester's companion. It is a collection of various types of lists that are frequently used during security assessments, all in one place. SecLists helps make security testing more efficient and productive by conveniently providing all the lists a security tester might need. List types include usernames, passwords, URLs, fuzzing payloads, sensitive data patterns, web shells, and more. The tester can simply pull this repository onto a new test machine and he will have access to every type of list he needs.

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)