Translated from developer.mozilla.org/en-US/docs/…

Each Javascript framework has its own different way of updating the DOM, handling browser events, and providing a pleasant experience for developers to use. This article will explore the main features of the “Big Four” frameworks, taking a high-level look at how they work and how they differ.

Domain-specific Language (DSL)

All of the frameworks discussed in this article are javascript-driven and allow you to build your own applications using DSLS. In particular, React uses JSX on a large scale to write its own components, and Ember uses Handlebars. Unlike HTML, these languages know how to read data values, which can be used to simplify the process of writing a UI.

Angular applications often use Typescript heavily. Typesscrit does not involve UI writing, but it is a DSL language with some notable differences from Javascript.

DSLS cannot be run directly by browsers; They must first be converted to Javascript or HTML. Transformation is an additional step in the development process, but framework templates generally contain the necessary tools to handle this step, or can be configured to include it. You don’t need to use these DSL languages when building framework applications, but embracing them will simplify your development process and make it easier to reach out to the framework community.

JSX

JSX, which supports Javascript and XML, is an extension of the Javascript language that brings htML-like syntax to the Javascript environment. It was developed by the React team for use in React, but can also be used to develop other applications – such as Vue.

Here is a simple JSX example:

const subject = "World"; const header = { <header> <h1>Hello, {subject}! </h1> </header> );Copy the code

This expression represents an HTML

element that contains a

element. The curly bracketed subject in line 4 tells the application to read the value of subject and insert it into our

When using React, JSX from the previous section is compiled to look like this:

var subject = "World";
var header = React.createElement("header", null,
    React.createElement("h1", null, "Hello, ", subject, "!")
);
Copy the code

When the browser does the final rendering, the above fragments will all produce HTML, like this:

<header> <h1>Hello, World! </h1> </header>Copy the code

Handlebars

The Handlebars template language is not specific to Ember applications, but it is used heavily in Ember applications. Handlebars code is similar to HTML, but it can choose to extract data from elsewhere. This data can be used to influence the HTML that the application eventually builds.

Like JSX, Handlebars uses curly braces to inject the value of a variable. Handlebars use double curly braces instead of single curly braces.

Give me an example of a template

<header> <h1>Hello, {{subject}}! </h1> </header>Copy the code

And this data

{
  subject: "World"
}
Copy the code

HandleBars will build pages like this

<header> <h1>Hello, World! </h1> </header>Copy the code

TypeScript

TypeScript is a superset of JavaScript, which means it extends JavaScript — all JavaScript code is valid TypeScript, but not vice versa. TypeScript is useful because it allows developers to enforce their code. For example, consider a function add() that takes the integers A and b and returns their sum.

In Js, this method looks like this

function add(a, b) {
  return a + b;
}
Copy the code

This code may seem trivial to someone used to JavaScript, but it can still be clearer. JavaScript allows us to concatenate strings together using the + operator, so if a and B are strings, this function still technically works — it probably won’t give you the result you want. What if we just want numbers passed into this function? TypeScript makes this possible:

function add(a: number, b: number) {
  return a + b;
}
Copy the code

The: number after each argument tells TypeScript that both A and B must be numbers. If we use this function and pass ‘2’ as an argument, TypeScript will throw errors at compile time and we will be forced to fix our errors. We could write our own JavaScript to throw these errors for us, but that would make our source code even more verbose. It might make more sense to let TypeScript handle such checks for us. (Note: In fact, since I use TS to now, ts type check is not as simple as the example, it is really difficult to understand the real use of TS, I think the friends who have used TS to develop large projects should have experienced it).

Writing Components

As described in the previous chapter, most frameworks have some kind of component model. React components can be written using JSX, Ember components using Handlebars, and Angular and Vue components can easily extend HTML using template syntax. Regardless of their views on how to write components, each framework’s components provide a way to describe the external properties they might need, the internal states that components should manage, and the events that users can trigger on component tags. The code snippets for the rest of this section use React as an example and are written in JSX.

The Properties (attributes)

The React representation of the AuthorCredit component might look like this:

function AuthorCredit(props) {
  return (
    <figure>
      <img src={props.src} alt={props.alt} />
      <figcaption>{props.byline}</figcaption>
    </figure>
  );
}
Copy the code

{props. SRC}, {props. Alt}, and {props. Byline} represent where our props will be inserted into the component. To render this component, we write code like this where we want to render it (possibly within another component) :

<AuthorCredit
  src="./assets/zelda.png"
  alt="Portrait of Zelda Schiff"
  byline="Zelda Schiff is editor-in-chief of the Library Times."
/>
Copy the code

This will eventually render the following in the browser

Element, whose structure is defined in the AuthorCredit component, and whose content is defined in the props contained in the AuthorCredit component call:

<figure>
  <img
    src="assets/zelda.png"
    alt="Portrait of Zelda Schiff"
  >
  <figcaption>
    Zelda Schiff is editor-in-chief of the Library Times.
  </figcaption>
</figure>
Copy the code

State of affairs

We talked about the concept of state in the previous chapter — a robust state handling mechanism is key to an effective framework, and each component may have data that it needs to control its state. This state persists in some way as long as the component is in use. Like props, state can be used to influence how components are rendered.

For example, consider a button that counts how many times it is clicked. This component should be responsible for keeping track of its own counting state, written like this:

function CounterButton() {
  const [count] = useState(0);
  return (
    <button>Clicked {count} times</button>
  );
}
Copy the code

UseState () is a React hook that, given an initial data value, tracks that value when updated. The code will initially render in the browser as follows:

<button>Clicked 0 times</button>
Copy the code

The useState() call tracks the count in a robust manner throughout the application, without requiring you to write your own code to do so.

The Events of the day

To be interactive, components need ways to respond to browser events so that our application can respond to our users. Each framework provides its own syntax to listen for browser events, which references the names of equivalent native browser events. In React, listening for click events requires a special property onClick.

Let’s update our CounterButton code from above to allow it to count clicks:

function CounterButton() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>Clicked {count} times</button>
  );
}
Copy the code

In this version, we use the extra useState() functionality to create a special setCount() function that we can call to update the value of count. We call this function at line 4 and set count to its current value plus one.

Styling Components

Each framework provides a way to define styles for your components or for your entire application. Although each framework defines component styles slightly differently, they all give you a variety of ways to do so. By adding some helper modules, you can style your framework application in Sass or Less, or transform your CSS stylesheets using PostCSS.

Handling dependencies

All major frameworks provide mechanisms for handling dependencies — using components in other components, sometimes with multiple hierarchies. As with other features, the exact mechanics vary between frameworks, but the end result is the same. Components tend to import components into other components using standard JavaScript module syntax or at least something similar.

Components in Components

A major benefit of a component-based UI architecture is that components can be composed together. Just as you can write HTML tags inside each other to build Web sites, you can use components inside other components to build Web applications. Each framework allows you to write components that leverage (and therefore rely on) other components.

For example, our AuthorCredit React component might be used in the article component. This means that the article needs to import AuthorCredit.

import AuthorCredit from "./components/AuthorCredit";
Copy the code

Once that’s done, AuthorCredit could be used inside the Article component like this:

. <AuthorCredit /> ...Copy the code

Dependency Injection

Real-world applications typically involve component structures with multiple layers of nesting. An AuthorCredit component has many layers nested, and for some reason it may need data from the most root-level of our application.

Suppose we are building a magazine website with the following structure:

<App>
  <Home>
    <Article>
      <AuthorCredit {/* props */} />
    </Article>
  </Home>
</App>
Copy the code

Our App component has the data required by our AuthorCredit component. We could rewrite Home and Article so that they know to pass the props along, but if there are many, many layers between the source and destination of our data, this might become tedious. This is too much: the front page and article don’t actually use the Author’s portrait or byline, but if we want to put this information into AuthorCredit, we need to change Home and Author to fit it.

The problem of passing data through multiple layers of components is called item drill-down and is not ideal for large applications.

To circumvent prop drill-down, the framework provides a feature called dependency injection, which is a way to get some data directly to the component that needs it, without going through an intermediate level. Each framework implements dependency injection by a different name and in a different way, but the end result is the same.

Angular calls this process dependency injection; Vue has provide() and Inject () component methods; React has a Context API; Ember shares status through services. (Emphasis here)

Lifecycle(= Lifecycle)

In the context of a framework, a component’s life cycle is the set of stages that a component goes through from browser rendering (commonly called mounting) to removal from the DOM (commonly called unmounting). Each framework names these lifecycle phases differently, and not all frameworks allow developers to access the same phases. All frameworks follow the same common model: they allow developers to perform certain actions during component installation, rendering, unloading, and the many stages in between.

The render phase is the most important to understand, because it repeats the most when your users interact with your application. It runs every time the browser needs to render new information, whether that information is an addition, deletion, or editing of content in the browser.

This React component lifecycle diagram provides a general overview of the concept.

Rendering Elements (Rendering elements)

Just like the lifecycle, the framework takes a different but similar approach to presenting your application. They all keep track of the currently rendered version of the browser DOM, and each makes slightly different decisions about how the DOM should change as components in the application are rerendered. Because the framework makes these decisions for you, you usually don’t interact with the DOM yourself. This abstraction of the DOM is more complex and memory intensive than updating the DOM yourself, but without it, frameworks won’t allow you to program in the declarative way they’re known for.

The virtual DOM is a way to store information about the browser DOM in JavaScript memory. Your application updates this copy of the DOM and then compares it to the “real” DOM (the DOM actually rendered to the user) to determine what to render. The application builds a “difference” to compare the updated virtual DOM to the currently rendered DOM, and uses that difference to apply the update to the real DOM. React and Vue both use a virtual DOM model, but they do not apply exactly the same logic when differentiating or rendering.

You can read more about the Virtual DOM in the React documentation.

The incremental DOM is similar to the virtual DOM in that it builds a DOM difference to determine what to render, but differs in that it does not create a full copy of the DOM in JavaScript memory. It ignores the parts of the DOM that do not need to be changed. Angular is the only framework that uses incremental DOM discussed so far in this module. You can read more about incremental DOM on the Auth0 blog. Glimmer VM is unique to Ember. It is neither a virtual DOM nor an incremental DOM; It is a separate process by which Ember’s templates are converted into a form of “bytecode” that is easier and faster to read than JavaScript. (Interesting Ember, I don’t think we use it that much in China.)

Routing

As discussed in the previous chapter, routing is an important part of the Web experience. To avoid a bad experience in a sufficiently complex application with a large number of views, each framework covered in this module provides a library (or libraries) to help developers implement client-side routing in their applications.

For Testing

All applications benefit from test coverage to ensure that your software continues to behave the way you expect it to, and Web applications are no exception. Each framework’s ecosystem provides tools to help you write tests. The test tools are not built into the framework itself, but command-line interface tools for generating framework applications give you access to the appropriate test tools.

Each framework has a wide range of tools in its ecosystem, with capabilities such as unit and integration testing.

The Test library is a suite of testing utilities with tools for many JavaScript environments, including React, Vue, and Angular. The Ember documentation covers tests for Ember applications.

Here’s a quick test of our CounterButton with the help of the React test library — it tests for things like the presence of the button and whether the button displays the correct text after being hit 0, 1, and 2 times:

import React from "react";
import { render, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";

import CounterButton from "./CounterButton";

it("renders a semantic with an initial state of 0", () => {
  const { getByRole } = render(<CounterButton />);
  const btn = getByRole("button");

  expect(btn).toBeInTheDocument();
  expect(btn).toHaveTextContent("Clicked 0 times");
});

it("Increments the count when clicked", () => {
  const { getByRole } = render(<CounterButton />);
  const btn = getByRole("button");

  fireEvent.click(btn);
  expect(btn).toHaveTextContent("Clicked 1 times");

  fireEvent.click(btn);
  expect(btn).toHaveTextContent("Clicked 2 times");
});
Copy the code

Summary

At this point, you should have a better understanding of the actual languages, features, and tools you will use when creating applications using the framework. I’m sure you’re keen to get started and actually do some coding, and that’s what you’ll do next! At this point, you can choose which framework to start with:

  • React

  • Amber

  • Vue

    Note: We only have three framework tutorial series available right now, but we hope to have more available in the future.