5

RFC: Angular Signals🚦 · angular/angular · Discussion #49685 · GitHub

 1 year ago
source link: https://github.com/angular/angular/discussions/49685
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

RFC: Angular Signals

Authors: @alxhub, @atscott, @dylhunn, @jelbourn, @pkozlowski-opensource
Area: Angular Framework
Posted: April 3, 2023
Status: Open

(this is the main RFC document, which links to the others)

We teased this in February, and now it's time to dig into the details: the Angular team requests your comments on our plan to adopt signals as a reactive primitive for Angular.

Before you dive in

Background context

Before going further, it's helpful if you understand How Angular change detection works today.

What kind of feedback are we looking for?

While we're excited to hear any and all comments on Angular Signals, we have additionally called out specific discussion points throughout the RFC for directed feedback. When you leave a comment, be sure to note the open question(s) to which you're responding.

As always, keep our code of conduct in mind. We know that a proposal of this scope significantly impacts Angular and the way you use it in your projects. We appreciate your commitment and investment in this project and ask to keep comments respectful. Remember, we are all just folks doing our best with limited resources.

How this RFC is organized

Because Angular Signals is a large project, we've split this RFC into several smaller docs. We recommend first reading the overview in this document and then jumping to the more specific docs in order.

Goals

This is a big change to Angular, so it's important to talk about why we're choosing to take this step. As we began our investigation into reactivity, we set out several goals we wanted to achieve that we felt would greatly improve Angular, based strongly on developer feedback over the last few years:

  • Angular has a clear and unified model for how data flows through an application.
  • The framework has built-in support for declarative derived state (a common feature request).
  • Only the parts of the UI that need updating should be synchronized, at or even below the granularity of individual components.
  • Smooth interoperability with reactive libraries such as RxJS.
  • Better guardrails to avoid common pitfalls that lead to poor change detection performance and avoid common pain points such as ExpressionChangedAfterItHasBeenChecked errors.
  • A viable path towards writing fully zoneless applications, eliminating the overhead, pitfalls, and quirks of zone.js.
  • Simplification of many framework concepts, such as queries and lifecycle hooks.

We think the best way to achieve our listed goals is to add fine-grained reactivity to the framework with Signals.

An RFC in four parts

If you're interested in the choice of signals as the reactive foundation of the framework, see the sub-discussion RFC #1: Signals for Angular Reactivity.

If you want to discuss our signal API and implementation itself, see RFC #2: Signal APIs.

If you're interested in exploring how using signals will look in Angular, then start with RFC #3: Signal-based components.

And finally, if you're curious about interoperability with RxJS, see RFC #4: Observable and Signal Interoperability.

Wrapping up

That's it!

This RFC has been a long time in the making. We're excited (and relieved) to finally share the details. Whether you're a long-time Angular developer or just stopping by to check out this new buzz on Angular, we're looking forward to obsessing over your every comment.

We want to make a special thanks to all of the hard work and innovation that have inspired us in the web community. Signals are clearly having a moment in the discourse today. Our ideas build on top of a large body of work– Preact, Vue, and SolidJS in particular have been inspirations for this project.

Frequently asked questions

Is this change going to be backward compatible?

Absolutely! Backward-compatibility is non-negotiable and we do everything to assure that the existing applications and libraries continue working as-is, without any modification. This is a gradual, opt-in change.

Is this another Angular rewrite?

No! This is mostly an additive change with some deeper modifications to the change detection algorithm. We take backwards compatibility seriously, and validate all changes with both our own suite of comprehensive tests and against the tests of thousands of Google applications built with Angular.

Is Angular getting less opinionated?

It's true that recent (and upcoming) changes to Angular have introduced new ways of building Angular applications. Most notably this includes standalone components and now signals. We introduce these new approaches over time to evolve the framework based on community feedback and our own observations of Angular in the wild. Our commitment to maintaining backwards compatibility means that the ecosystem ends up supporting multiple ways of doing things.

The result isn't that Angular is less opinionated, but instead that the opinions change over time. Typically, the most recent features represent the way we think most Angular developers should build their apps. We will always clearly communicate the preferred approach and update documentation / tooling accordingly. At the same time, the old way of building will live on for backwards compatibility (following our support policy).

Why are you working on signals and not feature XYZ?

In any popular framework there are always more features requested than we have bandwidth to work on at any given point in time. We are always focusing on projects that are the most helpful and impactful given the long-term evolution and stability of Angular. Signals are one of those projects.

First of all, improved runtime performance and optional zone.js was on our public roadmap for quite some time. Signals allow us to tackle this item.

More importantly, reactivity is at the very heart of a model-driven UI framework. By rethinking change detection, we open doors to simplification and evolutions that were not possible before. Faster performance, better forms, improved state management, and streamlined authoring format are prominent examples.

By baking-in reactivity into the framework we introduce primitives that the community can build upon.

Having said all this, the work on reactivity does not stop the work on the other important framework improvements. As an example, Angular v16 introduces the improved hydration, required inputs and tons of other improvements. We will continue working on other roadmap items while the reactivity work is in progress!

Appendix: How Angular change detection works today

Angular was built on the idea of a declarative, model-driven UI. Your application state is the source of truth and the framework automatically updates the page based on model changes. You declaratively express the mapping of model to UI via an HTML-like template. To keep the page up-to-date, the framework must track model changes over time.

zone.js

Angular uses zone.js to track various events in a browser (ex. DOM events, network requests and timers). Zone.js tracks these events by monkey-patching (wrapping and patching a callback function at runtime) any API that might lead to an application model change. When such an event occurs, however, Angular does not have any information as to what specific changes have occurred, or even if there were any changes at all.

Upon receiving event notification from zone.js, Angular will read (pull) new model values and update UI based on diffing the previously seen model values.

This approach provides excellent developer experience for small and simple applications:

  • You can use plain JavaScript data structures
  • Your state can live anywhere
  • You can mutate your state any way, any place (no need for setState or similar APIs)

In practice, however, large applications often grow to see zone.js become a source of performance issues and developer-facing complexity. As the web platform continues to grow and evolve, it also represents a rising maintenance cost for the Angular team.

While zone.js provides the mechanism that tells Angular when to run change detection, the next section discusses what this change detection entails.

Global, top-down change detection

Angular's default strategy is to run change detection over the entire component tree to make sure that the DOM reflects the most up-to-date model. Because Angular has no information about which parts of the application state have actually changed, it must check everything. In practice, however, only a fraction of the entire application state changes and only a handful of components need to be re-rendered.

Unidirectional data flow

Angular's change detection was designed to refresh application state once. The change detection process starts from the root of the component tree and walks all components down to the leaf nodes.

A single refresh traversal is sufficient if and only if components don’t modify the state of any of their ancestors after they have been visited. Unfortunately, the framework provides several convenient APIs to mutate state exactly in this way mid-traversal.

The component tree matches the rendered DOM tree, which means that change detection traverses the application in rendering order. However, several common web interaction patterns roll up descendant node states into ancestor nodes (e.g. form validity is computed as a function of descendant control states). This leads to the most "popular" error in Angular - ExpressionChangedAfterItHasBeenCheckedError.

OnPush

Angular developers can optionally configure change detection to only run on a subset of the component tree using the OnPush strategy. Components configured as OnPush and their descendants are checked for changes only under specific conditions:

  • A browser event originated in a component in question
  • One of the component’s @Input()s changed as a result of a template binding
  • A component was explicitly marked for check

While the OnPush strategy can reduce some of the performance cost, this strategy is limited:

  • Because OnPush components prevent their descendants from being checked, descendent components that depend on global state may not correctly update if they depend on global state outside of their ancestor components.
  • Because change detection always starts from the application root, running change detection on an OnPush component additionally checks every component between that component and the root, limiting the value of the optimization.

RxJS integration

You might notice that none of this overview includes RxJS. Angular does not internally use RxJS to propagate state or drive rendering in any way. Instead, Angular uses RxJS as a convenient data structure to express streams of events over time (such as for EventEmitter), completely disconnected from the change detection and rendering system.

You must be logged in to vote

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK