GithubHelp home page GithubHelp logo

hhy5277 / solid-1 Goto Github PK

View Code? Open in Web Editor NEW

This project forked from solidjs/solid

0.0 1.0 0.0 384 KB

A declarative, efficient, and flexible JavaScript library for building user interfaces.

License: MIT License

JavaScript 100.00%

solid-1's Introduction

drawing
     The Deceptively Simple User Interface Library

Build Status Coverage Status NPM Version Join the community on Spectrum

Solid is yet another declarative Javascript library for creating user interfaces. It does not use a Virtual DOM. Instead it opts to compile it's templates down to real DOM nodes and wrap updates in fine grained computations. This way when your state updates only the code that depends on it runs.

Key Features

  • Real DOM with fine-grained updates (No Virtual DOM! No Dirty Checking Digest Loop!).
  • Declarative data
    • Simple composable primitives without the hidden rules.
    • Function Components with no need for lifecycle methods or specialized configuration objects.
  • Less than 10% slower vs optimized painfully imperative vanilla DOM code. See Solid on JS Framework Benchmark.
  • Tiny in size, 7.07 kB minified and gzipped with all dependencies.
  • Supports modern features like JSX Fragments, Portals, Suspense, and Asynchronous Rendering.
  • Webcomponent friendly
    • Implicit event delegation with Shadow DOM Retargeting
    • Shadow DOM Portals
    • Custom Element friendly Suspense flow

A Simple Component looks like:
const HelloMessage = ({name}) => (
  <div>
    Hello {name}
  </div>
);

createRoot(() =>
  document
    .getElementById("hello-example")
    .appendChild(<HelloMessage name="Taylor" />)
);

Installation

To use Solid with JSX (recommended) run:

> npm install solid-js babel-plugin-jsx-dom-expressions

Solid State

It all starts with State. State objects are immutable so to update you call their companion setter function. Through the use of proxies they give the control of an immutable interface and the performance of a mutable one. Note only Plain Objects and Arrays are deeply wrapped.

import { createState, onCleanup } from 'solid-js'

const CountingComponent = () => {
  const [state, setState] = createState({counter: 0});

  const interval = setInterval(() =>
    setState({counter: state.counter + 1})
  , 1000);

  onCleanup(() => clearInterval(interval));

  return <div>{(state.counter)}</div>;
}

You can also deep set:

const [state, setState] = createState({
  user: {
    firstName: 'John'
    lastName: 'Smith'
  }
});

setState('user', {firstName: 'Jake', middleName: 'Reese'});

You can also use functions:

const [state, setState] = createState({counter: 0});
setState('counter', c => c + 1);

This takes the form similar to ImmutableJS setIn for leaving all mutation control at the top level state object. Keep in mind that setState when setting an object attempts to merge instead of replace.

But where the magic happens is with computations(effects and memos) which automatically track dependencies.

createEffect(() =>
  setState({
    displayName: `${state.user.firstName} ${state.user.lastName}`
  })
);

console.log(state.displayName); // Jake Smith

Whenever any dependency changes the State value will immediately update. JSX expressions can also be wrapped in effects so for something as trivial as a display name you could just inline the expression in the template and have it update automatically.

Solid State also exposes a reconcile method used with setState that does deep diffing to allow for automatic efficient interopt with immutable store technologies like Redux, Apollo, or RxJS.

const unsubscribe = store.subscribe(({ todos }) => (
  setState(reconcile('todos', todos)));
);
onCleanup(() => unsubscribe());

Solid Rendering

To accomplish rendering we use JSX for templating that gets compiled to native DOM element instructions. To do that we take advantage of the Babel Plugin JSX DOM Expressions which while converting JSX to DOM element instructions wraps expressions to be wrapped in our computations when indicated by in inner parens {( )}.

JSX as a templating language brings a lot of benefits. The just being javascript goes beyond just not needing a DSL, but setting up closure based context instead of creating context objects. This is more transparent and easier to follow and debug.

To get setup add this babel plugin config to your .babelrc, webpack, or rollup config:

"plugins": ["jsx-dom-expressions"]

And include at the top of your files:

import { r } from 'solid-js/dom'

Or, if you prefer you can use HyperScript. It does come at signifigant performance hit, as it doesn't benefit from any of the compile time optimizations that set Solid apart in performance. But it is an option for those who want to avoid Babel or do not wish to use JSX. Even though it is much slower it is still in the performance category of most popular frameworks. There are some minor differences from how you would write typical HyperScript as you need to manually wrap expressions in functions to make them observable. More information available at Dom Expressions. Include Solid HyperScript by:

import { h } from 'solid-js/dom'

With HyperScript it is possible to map to element functions or even tagged template literals which offer many different development experiences. See examples below.

Components

Templates in Solid are just Pascal(Capital) cased functions. Their first argument is an props object and return real DOM nodes. Other than that nothing is special about them.

const Parent = () => (
  <section>
    <Label greeting='Hello'>
      <div>John</div>
    </Label>
  </section>
);

const Label = ({greeting, children}) => (
  <>
    <div>{greeting}</div>
    {children}
  </>
);

Since the all nodes from JSX are actual DOM nodes the only responsibility of top level Templates/Components is appending to the DOM. Since change management is independent of code modularization, Solid Templates are sufficient as is to act as Components, or Solid fits easily into other Component structures like Web Components.

import { createState, createRoot } from 'solid-js'

class Component extends HTMLElement {
  constructor () {
    const [state, setState] = createState({});
    const [props, __setProps] = createState({});

    Object.assign(this, {state, setState, props, __setProps});
  }

  connectedCallback() {
    !this.shadowRoot && this.attachShadow({mode: 'open'});

    createRoot(dispose => {
      this.dispose = dispose;
      this.shadowRoot.appendChild(this.render());
    }
  }

  diconnectedCallback() { this.dispose && this.dispose(); }

  attributeChangedCallback(attr, oldVal, newVal) {
    this.__setProps({[attr]: newVal});
  }
}

class MyComponent extends Component {
  constuctor () {
    super();
    this.setState({greeting: 'World'});
  }
  render() {
    return <div>Hello {(state.greeting)}</div>
  }
}

Why?

This project started as trying to find a small performant library to work with Web Components, that had easy interopt with existing standards. It is very inspired by fine grain change detection libraries like Knockout.js and RxJS. The idea here is to ease users into the world of Observable programming by keeping it transparent and starting simple. Classically the Virtual DOM as seen in React for all it's advances has some signifigant trade offs:

  • The VDOM render while performant is still conceptually a constant re-render
    • It feels much more imperative as variable declarations and iterative methods for constructing the tree are constantly re-evaluating.
  • Reintroduced lifecycle function hell that break apart the declarative nature of the data. Ex. relying on blacklisting changes across the tree with shouldComponentUpdate.
  • Homogenous promise of Components and the overly simplistic local state in practice:
    • Imposes boundaries on components to solve performance concerns
    • Prices you into a very specific but not necessarily obvious structure
    • Only served to make it more ambiguous when emerging best practices lead to specialized component classification anyway
  • Abstracts debugging to the point a <div /> is not longer just a div
  • VDOM libraries still are based around having a specialized data objects.

So the driving questions here are:

  • If the data is going to be specialized anyway can we use Proxies to move the complexity into it rather than the rendering while keeping the appearance simple?
  • Can this free up existing constraints on how you modularize your view code?
  • Does this approach ultimately provide more adaptibility while reducing the API surface?
  • Is fine grained change detection fundamentally more performant than the Virtual DOM?

Admittedly it takes a strong reason to not go with the general consensus of best, and most supported libraries and frameworks. And React's Hooks API addresses the majority of what I once considered it's most untenable faults. But I believe there is a better way out there than how we do it today.

I cover this in more detail in my Bring Your Own Framework Blog Series(links below).

Documentation

Examples

Related Projects

  • DOM Expressions The renderer behind Solid.js that enables lightning fast fine grained performance.
  • Babel Plugin JSX DOM Expressions Babel plugin that converts JSX to DOM Expressions.
  • Solid Components Extensions to Solid.js that add a Web Component wrapper and a Context API.
  • React Solid State React Hooks API to use Solid.js paradigm in your existing React apps.
  • S.js The fine grained change detection engine that drives all computations and tracks all dependencies.

Past Articles

Status

This project is still a work in progress. While Solid's change management is reaching stability (this repo), I am still refining the rendering API from the Babel Plugin.

solid-1's People

Contributors

conblem avatar ryansolid avatar trusktr avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.