Giter Site home page Giter Site logo

lukasbombach / next-super-performance Goto Github PK

View Code? Open in Web Editor NEW
407.0 17.0 19.0 1002 KB

The case of partial hydration (with Next and Preact)

Home Page: https://medium.com/spring-media-techblog/how-we-achieved-the-best-web-performance-with-partial-hydration-20fab9c808d5

JavaScript 46.92% TypeScript 53.08%
javascript webdevelopment nextjs reactjs preact hydration partial-hydration performance

next-super-performance's Introduction

๐ŸŽ next-super-performance

Partial hydration for Next.js with Preact X.


Explanation: At spring we are creating websites for newspapers and we are very, very performance aware.

Newspapers are mostly static pages. Now if we were to create a single page application we would create a huge bundle with mostly unnecessary code.

This does not only mean that users wait for a big file to download, but as Addy Osmami points out there is a huge cost in performance with parsing and executing code. As a vague rule of thumb we can say, the bigger your bundle, the worse your performance.



That is why we aim to cut bundle size by only shipping the code we actually need in the client and leave the rest to server side rendering.

Overview

This repo ist still a proof of concept, we will continue to work on this and implement our work as 2 packages:

  • pool-attendant-preact A library that implements partialy hydration with preact x
  • next-super-performance A Next.js plugin that uses pool-attendant-preact to improve client side performance

On top of partial hydration we will implement loading strategies including critical CSS, critical JS, lazy loading, preloading ressources, etc. as part of next-super-performance.

Documentation

For now we have a partial hydration POC for Next.js and this is how it works. When you create a next.config.js and use our plugin like so

const withSuperPerformance = require("next-super-performance");
module.exports = withSuperPerformance();

2 things will happen:

  • React will be replaced by Preact because it is only 3KB
  • Next.js' main client JavaScript file will be discarded and replaced by a JavaScript file in your control

That means you have to create a client.js in your app's root folder that will act as the entry point for the JavaScript that will be sent to the client. We do this to give you full control of what you want your users to download and, very importantly, to choose the loading strategy that is right for you.

Now pool-attendant-preact comes into play. pool-attendant-preact exports 3 APIs for you:

  • withHydration a HOC that lets you mark your components for hydration
  • hydrate a function to hydrate marked components in the client
  • HydrationData a component that writes serialized props to the client, like NEXT_DATA

Let's explain this by example. Say you have a Next app with a header, a main section and teasers (which may just be images with a text and a headline, for instance). For the sake of this example, let's try and make the teasers 2 & 3 dynamic (just to pick some items on the page) and leave the rest static.

Here is how you would do it:

Install next-super-performance

npm i next-super-performance --save

Create a next.config.js and use the plugin

const withSuperPerformance = require("next-super-performance");
module.exports = withSuperPerformance();

Modify your package.json to make Next use Preact properly (this will alias react to preact and then start the original next scripts without modification):

  "scripts": {
    "dev": "next:performance dev",
    "start": "next:performance start",
    "build": "next:performance build"
  },

Create pages/index.js

import Header from "../components/header";
import Main from "../components/main";
import { HydrationData } from "next-super-performance";

export default function Home() {
  return (
    <section>
      <Header />
      <Main />
      <HydrationData />
    </section>
  );
}

The important part here is <HydrationData /> which will insert something like this:

<script type="application/hydration-data">
  {"1":{"name":"Teaser","props":{"column":2}},"2":{"name":"Teaser","props":{"column":3}}}
</script>

These are the names and props of the components that will be hydrated.

To tell your app that a particular component should be hydrated use withHydration. Our main.js could look like this:

import Teaser from "./teaser";
import { withHydration } from "next-super-performance";

const HydratedTeaser = withHydration(Teaser);

export default function Body() {
  return (
    <main>
      <Teaser column={1} />
      <HydratedTeaser column={2} />
      <HydratedTeaser column={3} />

      <Teaser column={1} />
      <Teaser column={2} />
      <Teaser column={3} />

      <Teaser column={1} />
      <Teaser column={2} />
      <Teaser column={3} />
    </main>
  );
}

In line 4 we have created a component that will be hydrated in the client and we use it 2 times on our page with different props.

withHydration will prepend your component with a marker so that it can be rendered on the server and be found in your HTML on the client. So <HydratedTeaser column={2} /> will become

<Fragment>
  <script type="application/hydration-marker" />
  <Teaser column={2} />
</Fragment>

The last and most crucial part is your client.js which is the code that will ship to your users and which is where you will hydrate your components. For a single component (Teaser) it can be simple as that.

import { hydrate } from "next-super-performance";
import Teaser from "./components/teaser";

hydrate([Teaser]);

Oh, next-super-performance comes with pool-attendant-preact which is why you import everything from here instead of from pool-attendant-preact. It just imports and exports withHydration, hydrate and HydrationData for convenience.

hydrate will find the components you have marked using withHydration and use the data from <HydrationData /> to hydrate them with the components you have passed to them as an array.

This will require you to import the components you want to use in the client (and pass them to the hydrate function). Because client.js is the entry point for all you client code, this also means you will see and control exactly which code you send to your users. Apart from Preact nothing else will be shipped.

If your components have dependencies on their own, those dependencies will be "natuarally" shipped as well because client.js is your entry and every dependendcy will be resolved through webpack.

Status

This repo is a POC and something we will build on. To try this out, clone this repository and then run

# Init Preact Git Sumodule
git submodule init
git submodule update

# Install dependencies
yarn

# Build Preact
cd packages/preact
yarn build

# Build the pool-attendant-preact package
# โ””โ”€ cd to the pool-attendant-preact dir
cd ...
cd packages/pool-attendant-preact
# โ””โ”€ build the package
yarn build

# cd to the app dir
cd ...
cd packages/app

# run the app
yarn dev

Conclusion

This POC seems to work quite well, we could drastically reduce our bundle size. There is still a lot to do though. Next.js still bundles code we don't want to see in the client (like core-js). Also we aim to implement tools and APIs to create a language for performance-critical aspects of your code to provide you with tools to define your critical rendering path.

next-super-performance's People

Contributors

lukasbombach avatar ofhouse avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

next-super-performance's Issues

How to write client.js in case of multiple pages and components?

In the given example we have only one component as Teaser. But in real sceneriose we will have multiple pages with deffierent Components.

How will client.js differentiate which components are realted to a particular page?
Do we have to import all the components in client.js? If so, What would be the strategy for bundling the components?

dist folder not included within published package `pool-attendant-preact`

Running the guide/examples if the README of this respository will result in this error today:

Error: Cannot find module '<project path>\node_modules\pool-attendant-preact\dist\index.cjs.js'. Please verify that the package.json has a valid "main" entry

This is because the dist folder is not included in the published package. For now, I'm manually cloning this repository, making a build and copying the files over, but it would be great to have this fix.

I'd wager this is broken for anyone trying to use the package.

Publish to npm

Hello and great that you did succeed with this!

Can you please publish next-super-performance & pool-attendant-preact to npm? I would like to this test this packages, especially regarding redux & apollo.

Also what would be amazing to have is something similar to when-idle and intersection-observer like this:
https://github.com/maoberlehner/vue-lazy-hydration

Repository not working?

I tried to install and use the repository as described in Status, but I cannot make it work.

Error: Cannot find module 'pool-attendant-preact'

Are the installation steps complete?

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.