Giter Site home page Giter Site logo

SSR + code-splitting about reactql HOT 8 CLOSED

leebenson avatar leebenson commented on May 8, 2024
SSR + code-splitting

from reactql.

Comments (8)

leebenson avatar leebenson commented on May 8, 2024 2

Thanks @kuka.

Code-splitting on the server should actually work out-the-box.

Because we're using Webpack 2 to generate bundles on both the client and the server, any strategy that uses require.ensure() (or import()-based Promises - I'm pushing an update for that this morning) should resolve well on the server, too. The reason I opted for server-side bundling is exactly for that reason - it should be possible (for the most part) to write code in one place, and know that it'll work on the server, too. Same with .css, .json, inline image loading, etc.

The one thing that's currently missing is a built-in HOC for returning a 'loading' component whilst split code hasn't yet returned back from its round-trip, and a recommended pattern in the docs for doing the actual split.

In the meantime, I whipped together this very quick example to demonstrate it working in practice:

Let's say you have this:

src/test.js

export default 'code-splitting for the win!';

And then inside src/app.js, we modify the <Message> component to grab the code-split message, instead of the current GraphQL:

class Message extends React.PureComponent {
  constructor() {
    super();
    this.state = {
      message: '',
    };
  }

  componentDidMount() {
    import('./test').then(mod => {
      this.setState({
        message: mod.default,
      });
    });
  }

  render() {
    return (
      <div>
        <h2>Message from code-splitting: <em>{this.state.message}</em></h2>
      </div>
    );
  }
}

Running npm start will yield the expected result:

screen shot 2017-03-31 at 08 14 56

And you can see the network request that grabbed this asynchronously:

network

Now, the neat this is that you also get this on the server, too - running npm run build-run will confirm:

Browser files generated

Hash: c09946598a0df5285bba2bd58c399409d8f2a767
Version: webpack 2.3.2
Child
Hash: c09946598a0df5285bba
Time: 8738ms
Asset Size Chunks Chunk Names
0.js 143 bytes 0 [emitted] <--- this is the async code-split
browser.js 5.98 kB 1 [emitted] browser
vendor.js 344 kB 2 [emitted] [big] vendor
assets/css/style.css 452 bytes 1 [emitted] browser
browser.js.gz 2.32 kB [emitted]
assets/css/style.css.gz 285 bytes [emitted]
vendor.js.gz 99.9 kB [emitted]

And on the server, too

Hash: 2bd58c399409d8f2a767
Time: 1949ms
      Asset       Size  Chunks             Chunk Names
0.server.js  251 bytes       0  [emitted]   <--- automatically loaded by the server!
  server.js    23.5 kB       1  [emitted]  javascript

Of course, in this example, the <Message> component attempts to set its internal 'message' state after the import() Promise resolves. At this point, the server has already turned the React chain into HTML and fired it back to the screen.

Since import() returns a Promise when the code-split data is available, the solution is to use a lib like redial to decorate components with data requirements, and then await the full chain being ready before rendering into HTML. This would work in the same way that Apollo's getDataFromTree does for GraphQL queries.

In a (soon-ish) future version, I'll add those features to the starter kit, and update src/app.js to show examples of code splitting at work, and how you can simulate synchronous loading for a server environment.

from reactql.

leebenson avatar leebenson commented on May 8, 2024 2

@scf4 - this actually works in production, but not via the new dev server:

test.js

import React from 'react';

export default () => (<h1>Test!</h1>);

app.js

// ...
function asyncComponent(getComponent) {
  return class AsyncComponent extends React.Component {
    static Component = null;
    state = { Component: AsyncComponent.Component };

    componentWillMount() {
      if (!this.state.Component) {
        getComponent().then(Component => {
          AsyncComponent.Component = Component;
          this.setState({ Component });
        });
      }
    }
    render() {
      const { Component } = this.state;
      if (Component) {
        return <Component {...this.props} />;
      }
      return null;
    }
  };
}

const Test = asyncComponent(() =>
  import('./test').then(module => module.default),
);

// Export a simple component that allows clicking on list items to change
// the route, along with a <Route> 'listener' that will conditionally display
// the <Page> component based on the route name
export default () => (
  <div>
    <Helmet
      title="ReactQL application"
      meta={[{
        name: 'description',
        content: 'ReactQL starter kit app',
      }]} />
    <div className={css.hello}>
      <img src={logo} alt="ReactQL" className={css.logo} />
    </div>
    <hr />
    <GraphQLMessage />
    <hr />
    <ul>
      <li><Link to="/">Home</Link></li>
      <li><Link to="/page/about">About</Link></li>
      <li><Link to="/page/contact">Contact</Link></li>
    </ul>
    <hr />
    <Switch>
      <Route exact path="/" component={Home} />
      <Route path="/page/:name" component={Page} />
      <Route path="/test" component={Test} />
      <Route component={NotFound} />
    </Switch>
    <hr />
    <p>Runtime info:</p>
    <Stats />
    <hr />
    <p>Stylesheet examples:</p>
    <Styles />
  </div>
);

// ...

I'm tracking this in reactql/kit#30

from reactql.

kuka avatar kuka commented on May 8, 2024 1

Thank you for a concise reply, Lee!

What you’ve written is going to tremendously help in my and others’ situations.

I consider this closed.

from reactql.

scf4 avatar scf4 commented on May 8, 2024

Can you split vendor.js based on the routes which require it?

Edit: actually I see it will already do this. But I'm not quite sure how to get code splitting working on server rendering too, since I call import in componentDidMount?

from reactql.

leebenson avatar leebenson commented on May 8, 2024

@scf4 - are you able to share the code for what you're trying to do?

Code splitting should work on the server in exactly the same way (i.e. you can use await import('./someModule'); in both environments), but if you have some kind of DOM-only code you want to load in the client only, you can keep that in componentDidMount and it should be split on the client route only.

from reactql.

scf4 avatar scf4 commented on May 8, 2024

@leebenson I'm using this example pretty much: https://gist.github.com/acdlite/a68433004f9d6b4cbc83b5cc3990c194

The server still renders everything else, it just doesn't load the async loaded component. So disabling JS means you can't see the async loaded component.

from reactql.

scf4 avatar scf4 commented on May 8, 2024

@leebenson Thanks Lee, I'll give that asyncComponent function a try now.

Edit: it worked, thank you 😄

from reactql.

leebenson avatar leebenson commented on May 8, 2024

cool @scf4 👍

react-async-component looks like a good way to handle code-splitting. I haven't tried it, but it should work with the ReactQL webpack flow.

from reactql.

Related Issues (20)

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.