GithubHelp home page GithubHelp logo

aweary / rapscallion Goto Github PK

View Code? Open in Web Editor NEW

This project forked from formidablelabs/rapscallion

0.0 2.0 0.0 102 KB

Asynchronous React VirtualDOM renderer for SSR.

License: MIT License

JavaScript 100.00%

rapscallion's Introduction

Rapscallion

CircleCI

Overview

Rapscallion is a React VirtualDOM renderer for the server. Its notable features are as follows:

  • Rendering is asynchronous and non-blocking.
  • With no concurrent renders, Rapscallion is roughly the same speed as React's renderToString.
  • With 10 concurrent renders, Rapscallion is roughly twice the speed of renderToString.
  • It provides a streaming interface so that you can start sending content to the client immediately.
  • It provides a stream templating feature, so that you can wrap your component's HTML in boilerplate without giving up benefits of streaming.
  • It provides a component caching API to further speed-up your rendering.

Big thanks to @briancavalier for his fantastic Most.js library!

Table of Contents

Installation

Using npm:

$ npm install --save rapscallion

In Node.js:

const {
  renderToStream,
  renderToString,
  toNodeStream,
  streamTemplate,
  tuneAsynchronicity
} = require("rapscallion");

// ...

API

renderToString

renderToString(VirtualDomNode) -> Promise<String>

This function evaluates a React VirtualDOM Element, and returns a Promise that resolves to the component's evaluated HTML string.

Example:

renderToString(<MyComponent {...props} />)
    .then(htmlString => console.log(htmlString));

renderToStream

renderToStream(VirtualDomNode) -> MostStream<StringSegment>

This function evalues a React VirtualDOM Element, and returns a Most.js stream. This stream will emit string segments of HTML as the DOM tree is asynchronously traversed and evaluated.

Example:

const componentStream = renderToStream(<MyComponent prop="stuff" />);
componentStream.observe(segment => process.stdout.write(segment));

toNodeStream

toNodeStream(MostStream<StringSegment>) -> NodeStream<StringSegment>

This function translates Most.js streams to Node streams. You'll be able to pipe the output of these Node streams to an HTTP Response object or to disk.

Example:

app.get('/example', function(req, res){
  const componentHtmlStream = renderToStream(<MyComponent />);
  toNodeStream(componentHtmlStream).pipe(res);
});

streamTemplate

streamTemplate`TEMPLATE LITERAL` -> MostStream<StringSegment>

See the section below for usage instructions.


tuneAsynchronicity

tuneAsynchronicity(PositiveInteger)

Rapscallion allows you to tune the asynchronicity of your renders. By default, rapscallion batches events in your stream of HTML segments. These batches are processed in a synchronous-like way. This gives you the benefits of asynchronous rendering without losing too much synchronous rendering performance.

The default value is 100 and equates to the approximate speed-performance of React's renderToString. With this value, Rapscallion takes about the same amount of time as React to render a VirtualDOM tree.

However, you may want to change this number if your server is under heavy load. Possible values are the set of all positive integers. Lower numbers will be "more asynchronous" (shorter periods between I/O processing) and higher numbers will be "more synchronous" (higher performance).

Caching

Caching is performed on a per-component level, is completely opt-in, and should be used judiciously. The gist is this: you define a cacheKey prop on your component, and that component will only be rendered once for that particular key. cacheKey can be set on both React components and html React elements.

If you cache components that change often, this will result in slower performance. But if you're careful to cache only those components for which 1) a cacheKey is easy to compute, and 2) will have a small set of keys (i.e. the props don't change often), you can see considerable performance improvements.

Example:

const Child = ({ val }) => (
  <div>
    ComponentA
  </div>
);

const Parent = ({ toVal }) => (
  <div cacheKey={ `Parent:${toVal}` }>
    {
      _.range(toVal).map(val => (
        <Child cacheKey={ `Child:${val}` } key={val} />
      ))
    }
  </div>
);

Promise.resolve()
  // The first render will take the expected duration.
  .then(() => renderToString(<Parent toVal={5} />))
  // The second render will be much faster, due to multiple cache hits.
  .then(() => renderToString(<Parent toVal={6} />))
  // The third render will be near-instantaneous, due to a top-level cache hit.
  .then(() => renderToString(<Parent toVal={6} />));

Stream templates

With React's default renderToString, it is a common pattern to define a function that takes the rendered output and inserts it into some HTML boilerplate; <html> tags and the like.

Rapscallion allows you to stream the rendered content of your components as they are generated. However, this makes it somewhat less simple to wrap that component in your HTML boilerplate.

Fortunately, Rapscallion provides stream templates. They look very similar to normal template strings, with a couple of exceptions.

  1. You add streamTemplate as a template literal tag.
  2. Using template literals' expression interpolation, you can insert streams into the template.
  3. You can also insert functions into the template that will be evaluated after the previous content has been streamed.

The return value is a Most.js stream.

That may not be clear in the abstract, so here's an example:

import { renderToStream, streamTemplate, toNodeStream } from "rapscallion";

// ...

app.get('/example', function(req, res){
  // ...

  const store = createStore(/* ... */);
  const componentHtmlStream = renderToStream(<MyComponent store={store} />);

  const responseStream = streamTemplate`
    <html>
    <body>
      ${componentHtmlStream}
      <script>
        window._initialState = ${() => JSON.stringify(store.getState())};
      </script>
    </body>
    </html>
  `;

  toNodeStream(responseStream).pipe(res);
});

Note that the template includes both a stream of HTML text (componentHtmlStream) and a function that evaluates to the store's state - something you'll often want to do with SSR.

Benchmarks

The below benchmarks do not represent a typical use-case. Instead, they represent the absolute best case scenario for component caching - up to 2,100x faster!

However, you'll note that even without caching, a concurrent workload will be processed in roughly half the time of React, without any of the blocking!

Starting benchmark for 10 concurrent render operations...
renderToString took 6.595226639 seconds.
rapscallion, no caching took 3.490436779 seconds.
rapscallion, caching DIVs took 0.814969248 seconds.
rapscallion, caching DIVs (second time) took 0.002369651 seconds.
rapscallion, caching Components took 0.123907298 seconds.
rapscallion, caching Components (second time) took 0.003139111 seconds.

License

MIT License

rapscallion's People

Contributors

divmain avatar exogen avatar

Watchers

 avatar  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.