Reactivity in detail | vue.js (2023)

One of the most distinctive features of Vue is its unobtrusive reactivity system. Component state consists of reactive JavaScript objects. If you change them, the view will be updated. This makes state management easy and intuitive, but it's also important to understand how it works to avoid some common pitfalls. In this section, we'll delve into some of the low-level details of Vue's reactivity system.

What is reactivity?#

This term pops up a lot in programming these days, but what do people mean when they say it? Reactivity is a programming paradigm that allows us to declaratively adapt to changes. The canonical example that people often point out why it's awesome is an Excel spreadsheet:

ONEBC
0

1

1

2

2

3

Here cell A2 is defined using a formula of= A0 + A1(You can click on A2 to view or edit the formula) so the table gives us 3. No surprises. But if you update A0 or A1, you will find that A2 is also automatically updated.

JavaScript generally doesn't work this way. If we were to write something similar in JavaScript:

js

LeaveA0= 1LeaveA1= 2LeaveA2=A0+A1console.Protocol(A2)// 3A0= 2console.Protocol(A2)// yet 3

when we mutateA0,A2does not change automatically.

How would we do this in JavaScript? First, to run the updated code againA2, let's wrap it in a function:

js

LeaveA2occupation Update() { A2 = A0 + A1}

So we need to define some terms:

  • It isUpdate()function produces aside effect, orIt is madein short, because it changes the state of the program.

  • A0eA1are considereddependenciesof the effect, as their values ​​are used to run the effect. The effect must beParticipantsto its dependencies.

What we need is a magic function that can be calledUpdate()(aIt is made) at any timeA0orA1(adependencies) change:

js

whenDepsChange(Update)

DieswhenDepsChange()Role has the following tasks:

(Video) Reactivity in Vue 3 - How does it work?

  1. Tracing when a variable is read. For example, when evaluating the expressionA0 + A1, BothA0eA1are read.

  2. If a variable is read while an effect is running, make that effect a subscriber to that variable. for example, becauseA0eA1when to be readUpdate()will be executedUpdate()becomes a subscriber to bothA0eA1after the first call.

  3. Detects when a variable mutates. For example, ifA0when a new value is assigned, notify all your subscriber effects to run again.

How reactivity works in Vue#

We can't really keep track of reading and writing local variables like in the example. There is simply no mechanism for this in vanilla JavaScript. What weI canto do, however, is read and write from interceptobject properties.

There are two ways to intercept property access in JavaScript:getter/standardizereProxys🇧🇷 Vue 2 exclusively used getters/setters due to browser support limitations. In Vue 3, proxies are used for reactive objects and getters/setters for refs. Here is pseudo-code that illustrates how they work:

js

occupation reactive(obj) { Returns Neu Proxy(obj, {to receive(target, key) { spore(target, key) Returns target[key] },defined as(target, key, Wert) { target[key]= Wert Deduction(target, key) } })}occupation Ref.(Wert) { until refObject = { to receiveWert() { spore(refObject, 'Wert') Returns Wert }, defined asWert(new value) { Wert = new value Deduction(refObject, 'Wert') } } Returns refObject}

TIPP

The code snippets here and below are intended to explain the main concepts as simply as possible, leaving out a lot of detail and ignoring edge cases.

This explains someLimitations of Reactive Objectswhich we discussed in the basic section:

  • If you assign or unstructure ownership of a reactive object to a local variable, the reactivity becomes "disconnected" because accessing the local variable no longer triggers the get/set proxy pitfalls.

  • The proxy returned fromreactive(), although it behaves exactly like the original, it has a different identity compared to the original===Operator.

WithinSpur()we check if there is a continuous effect. If so, find the subscriber effects (stored in a set) for the trace trace and add the effect to the set:

js

// This is placed right before an effect// be executed. We'll deal with that later.Leaveactive effectoccupation spore(target, key) { What if(active effect){ until effects = getSubscribersForProperty(target, key) effects.Add(active effect) }}

Effect signatures are stored in a global fileWeakMap<Target, Map<Key, Set<Effect>>>data structure. If no signature effect set is found for a property (crawled for the first time), it will be created. This is whatgetSubscribersForProperty()Function does, in short. For the sake of simplicity, we will skip its details.

WithinDeduction(), we look again for effects from subscriber to property. But this time we call them:

js

occupation Deduction(target, key) { until effects = getSubscribersForProperty(target, key) effects.for each((It is made) => It is made())}

Now let's go back to the circleswhenDepsChange()Occupation:

(Video) How does Vue.js reactivity work? Implementing a reactive system - Rubén Valseca - Vue Day 2019

js

occupation whenDepsChange(Update) { until It is made = () => { active effect = It is made Update() active effect = Null } It is made()}

It wraps the rawUpdateFunction on an effect that sets itself to the currently active effect before the actual update is performed. This allowsSpur()Calls during update to locate the currently active effect.

At this point, we've created an effect that automatically tracks its dependencies and runs again whenever a dependency changes. we call itreactive effect.

Vue provides an API that lets you create reactive effects:watchEffect()🇧🇷 In fact, you may have noticed that it works quite similarly to magic.whenDepsChange()for example. Now we can review the original example using real Vue APIs:

js

to import { Ref., watchEffect } Fora 'I see'untilA0= Ref.(0)untilA1= Ref.(1)untilA2= Ref.()watchEffect(() => { // Tracks A0 and A1 A2.Wert = A0.Wert + A1.Wert})// trigger the effectA0.Wert= 2

Using a reactive effect to modify a reference isn't the most interesting use case - actually using a computed property becomes more declarative:

js

to import { Ref., calculated } Fora 'I see'untilA0= Ref.(0)untilA1= Ref.(1)untilA2= calculated(() =>A0.Wert+A1.Wert)A0.Wert= 2

Internally,calculatedmanages its invalidation and recalculation using a reactive effect.

So what is an example of a common and useful reactive effect? Well, updating the DOM! We can implement a simple "reactive rendering" like this:

js

to import { Ref., watchEffect } Fora 'I see'untilcounting= Ref.(0)watchEffect(() => { document.body.HTML interno = `count is:${counting.Wert}`})// update the DOMcounting.Wert++

In fact, this is quite similar to how a Vue component keeps state and the DOM in sync - each instance of the component creates a reactive effect to render and update the DOM. Of course, Vue components use much more efficient methods to update the DOM thanHTML interno🇧🇷 This is discussed inrendering engine.

It isref(),calculated()ewatchEffect()The APIs are part of the composition API. If you've only used the Options API with Vue before, you'll find that the Composition API is closer to how Vue's reactivity system works under the hood. In fact, in Vue 3, the Options API is implemented on top of the Composition API. All property accesses to the component instance (Dies) resolves getters/setters for reactivity tracking and options likeverecalculatedcall their composition API equivalents internally.

Runtime versus compile-time reactivity#

Vue's reactivity system is primarily runtime based: tracing and triggering are all performed while the code is running directly in the browser. The advantages of runtime reactivity are that it can work without a compilation step and there are fewer edge cases. On the other hand, it is limited by JavaScript syntax limitations.

We've already encountered a limitation in the previous example: JavaScript doesn't provide us with a way to intercept the reading and writing of local variables, so we always have to access the reactive state as object properties, using reactive objects or refs.

we have experienced itreactivity transformationFeature to reduce code verbosity:

js

(Video) Vue JS 3 Tutorial - 54 - Reactivity and toRefs

LeaveA0= $ ref(0)LeaveA1= $ ref(1)// Tracing variables readuntilA2= $calculated(() =>A0+A1)// Trigger on writing the variableA0= 2

This snippet is compiled by automatically appending exactly what we would have written without the transform.Wertfor references to variables. With Reactivity Transform, Vue's reactivity system becomes a hybrid system.

reactivity debugging#

It's great that Vue's reactivity system automatically tracks dependencies, but in some cases we may want to identify exactly what is being tracked or what is causing a component to re-render.

Component Debug Hooks#

We can debug which dependencies are used during rendering of a component and which dependency triggers an update using therenderTrackedonRenderTrackederenderTriggeredonRenderTriggeredlifecycle hook. Both hooks receive a debugger event containing information about the dependency in question. It is recommended that ascrubberdeclaration in the callbacks to explore the dependency interactively:

I see

<road map settings>to import { onRenderTracked, onRenderTriggered } Fora 'I see'onRenderTracked((event) => { scrubber})onRenderTriggered((event) => { scrubber})</road map>

js

Export Originally { renderTracked(event) { scrubber }, renderTriggered(event) { scrubber }}

TIPP

Component debug hooks only work in development mode.

Debug event objects are of the following type:

ts

Type DebuggerEvent = { It is made: reactive effect target: Object Type: | TrackOpTypes /* 'received' | 'has' | 'repeat' */ | TriggerOpTypes /* 'define' | 'add' | 'delete' | 'Clear' */ key: any new value?: any old value?: any old target?: Map<any, any> | Phrase<any>}

computed debugging#

We can debug computed properties by passing them incalculated()a second options object withon the trackeonTriggerremember:

  • on the trackinvoked when a property or reactive reference is tracked as a dependency.
  • onTriggeris called when the watcher callback is triggered by a dependency mutation.

Both callbacks receive events from the debugger in thesame formatcomo Component-Debug-Hooks:

js

untilAnother= calculated(() =>counting.Wert+ 1, { on the track(e) { // raised when count.value is tracked as a dependency scrubber }, onTrigger(e) { // triggered when count.value mutates scrubber }})// plusOne access must trigger onTrackconsole.Protocol(Another.Wert)// altera count.value, deve disparar onTriggercounting.Wert++

TIPP

(Video) JavaScript Reactivity Explained Visually

on the trackeonTriggercalculated options only work in development mode.

Inspector Debugging#

Equal tocalculated(), Observers also support thaton the trackeonTriggerOptions:

js

ver(Those ones,call back, { on the track(e) { scrubber }, onTrigger(e) { scrubber }})watchEffect(call back, { on the track(e) { scrubber }, onTrigger(e) { scrubber }})

TIPP

on the trackeonTriggerObserver options only work in development mode.

Integration with external government systems#

Vue's reactivity system works by deeply transforming plain JavaScript objects into reactive proxies. Deep conversion can be unnecessary or sometimes undesirable when integrating with external state management systems (for example, when an external solution also uses proxies).

The general idea of ​​integrating Vue's reactivity system with an external state management solution is to keep the external state in ashallow reference🇧🇷 A superficial suggestion is reactive only when it is.Wertthe property is accessed - the intrinsic value remains intact. When the external state changes, replace the ref value to trigger updates.

immutable data#

If you implement an undo/redo function, you'll probably want to take a snapshot of the application's state every time a user edits it. However, Vue's mutable reactivity system is no longer suitable for this when the state tree is large, as serializing the entire state object on every update can be expensive in terms of CPU and memory costs.

immutable data structuresResolve this by never changing the state objects - instead, new objects are created that share the same unmodified parts with the old ones. There are several ways to use immutable data in JavaScript, but we recommend using themalwayswith Vue because it allows you to use immutable data while keeping the mutable syntax more ergonomic.

We can always integrate with Vue via a simple composite:

js

to importto productFora 'always'to import { shallow reference } Fora 'I see'Export occupation always use(basic state) { until Illness = shallow reference(basic state) until Update = (updater) => { Illness.Wert = to product(Illness.Wert, updater) } Returns[Illness, Update]}

Experimente no playground

state machines#

state machineis a template for describing all the possible states an application can be in and all the possible ways to transition from one state to another. While this might be overkill for simple components, it can help make complex state flows more robust and manageable.

One of the most popular implementations of state machines in JavaScript isXZustand🇧🇷 Here is an element that can be composed to integrate with it:

js

to import { createMachine, interpret } Fora 'xZustand'to import { shallow reference } Fora 'I see'Export occupation useMachine(options) { until machine = createMachine(options) until Illness = shallow reference(machine.initial state) until Service = interpret(machine) .in the transition((New Condition) =>(Illness.Wert = New Condition)) .start() until from you = (event) => Service.from you(event) Returns[Illness, from you]}

Experimente no playground

RxJS#

RxJSis a library for working with asynchronous event streams. This oneShowUsolibrary provides the@vueuse/rxjsAdd-on to connect RxJS streams to Vue's reactivity system.

(Video) Vue 3: Reactivity Made Easy (ref, reactive, toRefs... oh my!)

FAQs

What is Vue 3 reactivity? ›

Reactivity is a programming paradigm that allows us to adjust to changes in a declarative manner. Photo by Kyle Hinkson on Unsplash. JavaScript variables are not reactive by default. JS Proxy provide the mechanism to achieve the reactiveness.

What is reactive dependency in Vue? ›

Reactive dependencies are those that are observable, like data properties or Vuex state. Non-reactive dependencies would be something like a simple variable or something from local-storage. For example const someArray = [1, 2, 3] export default { data: () => ({ foo: 'foo' }), computed: { FOO () { return this. foo.

Is Vue store reactive? ›

Vuex stores are reactive. When Vue components retrieve state from it, they will reactively and efficiently update if the store's state changes. You cannot directly mutate the store's state. The only way to change a store's state is by explicitly committing mutations.

What are reactive properties? ›

Reactive properties are properties that can trigger the reactive update cycle when changed, re-rendering the component, and optionally be read or written to attributes.

What is reactive VAR? ›

A ReactiveVar holds a single value that can be get and set, such that calling set will invalidate any Computations that called get , according to the usual contract for reactive data sources.

What is reactivity in testing? ›

If you have been tested for HIV infection, you may be told that the result is 'reactive'. While this is sometimes described as a 'positive' result, it could be a false positive. The result indicates that the test has reacted to something in your blood and this should be investigated further.

How many types of reactivity are there? ›

The 5 primary types of chemical reactions are: Combination reaction. Decomposition reaction. Displacement reaction.

What is reactivity response? ›

It means that you react to situations through your emotions. Here, you can often come across as blaming, resentful, insecure, or angry. Common statements made when someone is being reactive include: “It's just the way I am”, “There's nothing I can do”, “She ruined my day”, “The teacher wasn't fair”.

How do I emit data from Vue? ›

Emitting Events with setup()

$emit() to send our event. Instead, we can access our emit method by using the second argument of our setup function – context . context has access to your components slots, attributes, and most importantly for us, its emit method. We can call context.

How do you use data () in Vue? ›

data # A function that returns the initial reactive state for the component instance. The function is expected to return a plain JavaScript object, which will be made reactive by Vue. After the instance is created, the reactive data object can be accessed as this.

How do I create a dynamic table in Vue? ›

vue so we'll extract the code to a separate component with the following steps:
  1. Create a new component EditableTable. ...
  2. Add two props in the EditableTable component ( items and fields )
  3. Move the existing code but without the data part ( items and fields ) as this now is required to be passed as a prop by the consumer.
Feb 16, 2022

How do you make props reactive? ›

To create a reactive field prop simply pass a function as its value, and use the exposed get method to reference props of another fields.

Videos

1. Stop Using .value with ref In Vue 3! Reactivity Transformed Explained!
(Program With Erik)
2. Reactivity in Vue 2.0
(Code with James)
3. Fundamentals of Reactivity in Vue 3
(Justin Brooks)
4. An Animated Guide to Vue 3 Reactivity and Internals
(Coding Tech)
5. When to Use Ref vs. Reactive // Vue Tips
(LearnVue)
6. REACTIVITY IN VUE.JS | How it works? | JavaScript Meetup Bangalore #40
(NerdyCap)

References

Top Articles
Latest Posts
Article information

Author: Aracelis Kilback

Last Updated: 07/22/2023

Views: 5941

Rating: 4.3 / 5 (64 voted)

Reviews: 87% of readers found this page helpful

Author information

Name: Aracelis Kilback

Birthday: 1994-11-22

Address: Apt. 895 30151 Green Plain, Lake Mariela, RI 98141

Phone: +5992291857476

Job: Legal Officer

Hobby: LARPing, role-playing games, Slacklining, Reading, Inline skating, Brazilian jiu-jitsu, Dance

Introduction: My name is Aracelis Kilback, I am a nice, gentle, agreeable, joyous, attractive, combative, gifted person who loves writing and wants to share my knowledge and understanding with you.