preface

SolidJS supports modern front-end features such as JSX, Fragments, Context, Portals, Suspense, Streaming SSR, Error Boundaries, and concurrent rendering. Both client and server currently have the highest performance scores!

At first glance, it has everything, how can other frames live, but when you really understand it, you will realize that it is really a treasure frame ~

In addition, because it is a late comer, it does not have such heavy historical burden (like the React Hook principle, which requires compatibility with class Component, resulting in too complicated source code), its code is very easy to read and can be used as a beginner to learn.

Any framework can be seen as a combination of ideas and design patterns, so we can look at this framework with a learning mindset.

1. Test your hand

Let’s start with a simple counter Demo,

import { render } from 'solid-js/web';
import { createSignal, createEffect } from 'solid-js';

const Counter = () = > {
  const [getCount, setCount] = createSignal(0);
  const add = () = > setCount(getCount() + 1);
  createEffect(() = > {
    console.log('count is change:', getCount());
  });

  return (
    <button type='button' onClick={add}>
      {getCount()}
    </button>
  );
};

render(() = > <Counter />.document.getElementById('root'));
Copy the code

Ooh, it’s like React

There is also no need to manually declare the effect dependency, looking at a bit of Vue3 shadow.

Take a look at the code package above,

Found js file unexpectedly less than 8K, really can be so delicate!

In fact, SolidJS is not only a small package size, but also a Number 1 performance!

Take a look at the jS-Framework-benchmark scoring results

Vanilla! Vanilla! Vanilla!

Vanilla was, in fact, pure native JavaScript, often used as a benchmark for performance comparisons.

So why does SolidJS not only achieve small size, but also achieve strong performance, and even surpass the front-end giants Vue, React?

Let’s briefly analyze its principle to find out!

2. Age: 43

2-1. Balancing the pros and cons of JSX versus Template

It’s worth mentioning the pros and cons of JSX and Template

  • jsx
    • Advantages: AsjsAnd syntactic sugar has a high degree of flexibility and can be written at will
    • Disadvantages: Difficult to analyze operation intent at compile time because it is too flexible
  • template
    • Advantages: Because of syntax limitations, most code with operational intent (V-if, V-for) can be identified at compile time for optimization
    • Disadvantages: Limited writing style, inferior in most casesjsxflexible

Some of you might be wondering, what can you do at compile time, is it really important to recognize the intent of the operation?

Speaking of which, Vue3,

Compared with Vue2, Vue3 achieved a qualitative leap in performance, which is inseparable from the compilation phase optimization.

  • For example, at compile time, mark the nodes in the template that will never change and store them as static nodes, bypassing them in future updates.

  • 2. Distinguish v-IF and V-for blocks in advance, so as to bypass unnecessary judgments in future diff;

  • 3. When binding props, record which properties are subject to change. In future diff, only compare “dynamic nodes and properties that may change” and skip “nodes and properties that will never change”.

  • There are also cache event handlers and so on

The compile phase is necessary, but why can’t JSX do that?

Imagine if we wanted to do a conditional rendering of a component. By default in template we could only do this with the directive V-if:

<template>
  <div>
    <span v-if="status === 1">through</span>
    <span v-else-if="status === 1">Refused to</span>
  </div>
</template>
Copy the code

But there are many ways to write JSX, and you can think of just three:

return status === 1 ? <span>through</span> : status === 2 ? <span>Refused to</span> : null;
Copy the code
return (
  <>
    {status === 1 && <span>through</span>}
    {status === 2 && <span>Refused to</span>}
  </>
);
Copy the code
switch (status) {
  case 1:
    return <span>through</span>;
  case 2:
    return <span>Refused to</span>;
}
Copy the code

If every case were decided, the compile phase would be complicated, time-consuming, and muggle-like.

Vue3 does so many compile-time optimizations because of its specific template syntax, making as many recognisable optimizations as possible within the constraints.

JSX is so free that it is difficult to do intention analysis, so it seems that too free is not a perfect thing

SolidJS adopts the following solution: a layer of specification is made on the basis of JSX, which is called Control Flow in Chinese

Written like a default component for compile-time optimization:

return (
  <Switch>
    <Match when={status= = =1}>
      <span>through</span>
    </Match>
    <Match when={status= = =2}>
      <span>Refused to</span>
    </Match>
  </Switch>
);
Copy the code

In this way, you can do intent analysis at compile time, know in advance that you are doing conditional rendering, and then compile into the corresponding DOM operation.

It can be briefly summarized as:

  • It takes advantage of template for easier compile-time optimization

  • While retaining the flexibility of JSX

2-2. No Dom Diff

No Dom Diff means that SolidJS abandons virtual Dom and adopts node-level update in terms of update granularity.

In terms of update granularity, we can first summarize several mainstream front-end schemes:

  • Application level updates: Status updates cause the entire applicationrenderWhich content to render depends oncoordinateResults. includingReact
  • Component-level updates: State updates only cause the components bound to that state to render, again depending on the result of coordination. The representative works are VUe2. X
  • Node-level update: the node update bound to the status by direct contact method, that is, point-to-point update. Representative works include Vue1. X, Svelte and SolidJS

React may surprise you, but isn’t it a component-level update?

Then we can consider a question: why do hooks have order limits and use effects have dirty data?

This is because React updates the entire VDom /Fiber tree and diff the old and new trees to determine which components are actually updated. React is not a component-level update. This is why, prior to React Fiber, it was easy to get stuck with updates if not optimized properly, whereas vue2.x didn’t.

Vu1.x abandoned node-level updates because the Vue responsive principle at the time used three main objects: Observer, Dep and Watcher are all complex objects (refer to complex objects and simple objects). Since we need to recursively observe the sub-objects under data, there will be a lot of observers in an application, and it is easy to take up too much memory and cause the lag problem. So Vue2. X is a component-level update.

SolidJS uses simple object storage for all three objects, and does not need recursive observation, so it occupies very little memory.

To solve the problem of occupying too much memory, the next step is how to update the DOM. The specific approach is: generate dom operation methods like INSERT, update and DELETE in advance at the compile stage, and call them directly when updating in the future.

The reason for the creation of virtual DOM is not only to save DOM overhead, but also cross-platform development (React Native, Weex). How does SolidJS implement cross-platform rendering?

You might have noticed this line of code

import { render } from 'solid-js/web';
Copy the code

Yes, the concept of SolidJS and React is very similar. As progressive frameworks, they both separate the core library from the render library,

Solid-js/Web is a set of node operation methods for web platform. If we want to do custom rendering in the future, we only need to implement a solid-JS /Android or solid-JS /IOS. Is it a novel idea, cross-platform and performance?

2-3. Recompile time

  • Generate node rendering methods ahead of time

    As mentioned earlier, SolidJS draws on the standard writing method of part template in JSX, analyzes the intention in the compilation stage, and generates the corresponding DOM operation method in advance

  • Pack as needed and shrink

    This step, also known as tree-shaking, is to package only the modules that are used, further reducing the size of the packaged resources.

2-4. Light · Run time

The lack of diff makes the runtime code lighter, so SolidJS is more concise when updating.

  • This is the JS call stack for SolidJS when it is updated

  • This is the JS call stack for React V16 when it is updated

    These other features make it easier for another thing to land: the micro front end

    One of the major pain points of the microfront-end today is that the volume of resources packaged by multiple projects is too large, and when run light, the code will be small, making it ideal for Web Components (a new w3c standard). Imagine a future where Vue and React are light. At runtime, we can compile components into Web components and reference them directly in the main application. No more worrying about different technology stacks and versions!

2-5. Hooks that are not limited by sequence

React Hook was first implemented in the front-end framework. However, because React always promotes immutable, the update process of the whole tree must be repeated every time it is updated. React Hook cannot be used in conditional loops, otherwise rendering results may be affected. It’s possible, but not certain

Later, UVU released Vue3.0, which was accompanied by Composite API, commonly known as Vue3 Hook. Since Vue2 has adopted component-level update granularity since then, coupled with the reactive principle of automatic collection of dependencies, Vue3 Hook has no order/condition restrictions. It can also be nested.

The response principle of SolidJS borrows from React Hook while retaining Vue3’s dependency collection model, making it very silky to use.

Vue3 automatically encapsulates non-reference types such as number, String, and Boolean as objects with the current attribute:

const count = ref(0)

const show = () = > {
  console.log('count:' + count.value)
}
Copy the code

Because basic data types cannot be Proxy, data hijacking cannot be realized.

With SolidJS, you simply change the way you get data to a getter method:

const [getCount, setCount] = createSignal(0)

const show = () = > {
  console.log('count:' + getCount())
}
Copy the code

Inside the method can also do data hijacking, it is clever ah!

The process is like this:

  1. ingetterMethods collect dependencies internally
  2. insetterMethod touch depends on update

For more detailed principles, please refer to this article [Vue3 source code] to observe changes in VUE from the source level – responsive principle

Unlike Vue3, Vue3 retains the virtual DOM, so diff still takes up runtime time,

SolidJS ignores this step, so it performs better.

3. The other

  • Scaffolding: Degit, internally integrated with Vite.

  • Supports TS and is type-friendly

  • Most features of modern front-end frameworks: Fragments, Portals, Context, Suspense, event delegation, SSR, and so on

Authentic, typical “I want it all”

4. Summarize and think

They say the front end comes in a cycle,

Tailwind CSS, for example, is a modern version of Bootstrap

SSR is much like the modern version of JSP

The packaging of front-end frameworks gradually shrinks in size, trying to get back to the size of native JS projects

And the native DOM experienced virtual DOM will slowly return to the native DOM

Of course, the most concerned question is whether SolidJS can be used in the production environment? React is easier to read than React because it has most of the features of a modern front-end and doesn’t have a lot of historical versions.