网页 综合 更新时间 2026-11-14 02:18:13

Reactivity in Depth | Vue.js

自动蜘蛛 vuejs.org 正文约 5227 字
Reactivity in Depth | Vue.js
Creating an Application
Reactivity Fundamentals
Class and Style Bindings
Conditional Rendering
Fallthrough Attributes
Single-File Components
Server-Side Rendering (SSR)
Production Deployment
TS with Composition API
Render Functions & JSX
Vue and Web Components
Animation Techniques
One of Vue’s most distinctive features is the unobtrusive reactivity system. Component state consists of reactive JavaScript objects. When you modify them, the view updates. It makes state management simple and intuitive, but it’s also important to understand how it works to avoid some common gotchas. In this section, we are going to dig into some of the lower-level details of Vue’s reactivity system.
This term comes up in programming quite a bit these days, but what do people mean when they say it? Reactivity is a programming paradigm that allows us to adjust to changes in a declarative manner. The canonical example that people usually show, because it’s a great one, is an Excel spreadsheet:
Here cell A2 is defined via a formula of = A0 + A1 (you can click on A2 to view or edit the formula), so the spreadsheet gives us 3. No surprises there. But if you update A0 or A1, you'll notice that A2 automagically updates too.
JavaScript doesn’t usually work like this. If we were to write something comparable in JavaScript:
When we mutate A0, A2 does not change automatically.
So how would we do this in JavaScript? First, in order to re-run the code that updates A2, let's wrap it in a function:
Then, we need to define a few terms:
The update() function produces a side effect, or effect for short, because it modifies the state of the program.
A0 and A1 are considered dependencies of the effect, as their values are used to perform the effect. The effect is said to be a subscriber to its dependencies.
What we need is a magic function that can invoke update() (the effect) whenever A0 or A1 (the dependencies) change:
This whenDepsChange() function has the following tasks:
Track when a variable is read. E.g. when evaluating the expression A0 + A1, both A0 and A1 are read.
If a variable is read when there is a currently running effect, make that effect a subscriber to that variable. E.g. because A0 and A1 are read when update() is being executed, update() becomes a subscriber to both A0 and A1 after the first call.
Detect when a variable is mutated. E.g. when A0 is assigned a new value, notify all its subscriber effects to re-run.
We can't really track the reading and writing of local variables like in the example. There's just no mechanism for doing that in vanilla JavaScript. What we can do though, is intercept the reading and writing of object properties.
There are two ways of intercepting property access in JavaScript: getter / setters and Proxies. Vue 2 used getter / setters exclusively due to browser support limitations. In Vue 3, Proxies are used for reactive objects and getter / setters are used for refs. Here's some pseudo-code that illustrates how they work:
Code snippets here and below are meant to explain the core concepts in the simplest form possible, so many details are omitted, and edge cases ignored.
This explains a few limitations of reactive objects that we have discussed in the fundamentals section:
When you assign or destructure a reactive object's property to a local variable, accessing or assigning to that variable is non-reactive because it no longer triggers the get / set proxy traps on the source object. Note this "disconnect" only affects the variable binding - if the variable points to a non-primitive value such as an object, mutating the object would still be reactive.
The returned proxy from reactive(), although behaving just like the original, has a different identity if we compare it to the original using the === operator.
Inside track(), we check whether there is a currently running effect. If there is one, we lookup the subscriber effects (stored in a Set) for the property being tracked, and add the effect to the Set:
Effect subscriptions are stored in a global WeakMap<target, Map<key, Set<effect>>> data structure. If no subscribing effects Set was found for a property (tracked for the first time), it will be created. This is what the getSubscribersForProperty() function does, in short. For simplicity, we will skip its details.
Inside trigger(), we again lookup the subscriber effects for the property. But this time we invoke them instead:
Now let's circle back to the whenDepsChange() function:
It wraps the raw update function in an effect that sets itself as the current active effect before running the actual update. This enables track() calls during the update to locate the current active effect.
At this point, we have created an effect that automatically tracks its dependencies, and re-runs whenever a dependency changes. We call this a Reactive Effect.
Vue provides an API that allows you to create reactive effects: watchEffect(). In fact, you may have noticed that it works pretty similarly to the magical whenDepsChange() in the example. We can now rework the original example using actual Vue APIs:
Using a reactive effect to mutate a ref isn't the most interesting use case - in fact, using a computed property makes it more declarative:
访问原文 返回搜索 https://vuejs.org/guide/extras/reactivity-in-depth
本页为82搜索生成的网页快照,内容来自站内索引;演示数据的原文链接仅作示例。