meteor / meteor-feature-requests

A tracker for Meteor issues that are requests for new functionality, not bugs.
Other
89 stars 3 forks source link

Preload dynamic import chunks #151

Open jbaxleyiii opened 7 years ago

jbaxleyiii commented 7 years ago

Feature

Support including (via script tag or inline?) the bundled modules of dynamically imported before application startup occurs.

Use Case

Let's say that I have a simple react app with server side rendering:

import Loadable from "react-loadable";
import Loading from "./my-loading-component";

const LoadableComponent = Loadable({
  loader: () => import("./my-component"),
  loading: Loading
});

export default class App extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello world</h1>
        <LoadableComponent />
      </div>
    );
  }
}

During the server render, I have would sync access to ./my-component resulting in the markup of that component being generated and sent alongside Hello world to the client for viewing. While the user is distracted by the wonderful SSR'd content, I download and bootup my client side application. During that bootup, the react app does not have access to ./my-component so it returns a promise while loading it. Since react is sync in its loading, my wonderful content disappears to the user while the module is sent over DDP. After it is loaded, the app re-renders and my content is back.

This flash makes the app feel broken, does more work on the client, and results in a less than ideal experience for the user.

In an ideal world, since meteor would know over the course of the server side render, which modules were accessed, it would include script tags for the bootstrap, modules, then application.

For an example implementation on a framework level: next For an example in userland / config level: webpack-flush-chunks

benjamn commented 7 years ago

At first I thought this would be tricky because we don't use <script> tags or even HTTP to fetch dynamic modules, so HTML-based prefetching techniques wouldn't make sense for Meteor.

Then I realized that we have a lot more control over the delivery of dynamic modules than other frameworks do, so I think there is definitely a way to make this work. We just can't directly borrow what other frameworks have done.

I think the essence of our approach should be to send information down as part of the initial response that can prime the IndexedDB module cache, so that the modules are immediately available when/if the client imports them dynamically. To prime the cache we just need the string of code we would have fetched, along with its hash. Seems pretty straightforward, actually!

In order for this technique to help, we just need to make sure the cache gets primed before any client code calls import(...). So there may be some trickiness around injecting the priming code in the right place, but I think that's doable.

benjamn commented 7 years ago

Basically you want to call require("meteor/dynamic-import/cache.js").setMany, defined here.

Here’s where we currently call it (after receiving fetched dynamic modules).

The cache.setMany function takes an object mapping module identifiers to objects { version: <hash>, source: <string> }. It might even be possible to determine the hash from the module ID, like we do a little earlier in that code.

lhz516 commented 6 years ago

My solution to this issue is to load the needed async component before rendering routes so that the first page of the app will look like synchronized (no flash). Here's a package I wrote react-code-split-ssr. Hope it helps

sirpy commented 6 years ago

@lhz516 can you elaborate a little on your solution how it achieves ssr?

lhz516 commented 6 years ago

@sirpy It's in the README

minhna commented 6 years ago

Is there any update? It's very interesting.

berkaytheunicorn commented 6 years ago

any updates on this issue?

piotrek-horodenski commented 5 years ago

I am writing vuejs app with meteor. It would be quite nice to be able to define chunks for dynamic imports, I noticed there is a solution for it in vue-cli:

() => import(/* webpackChunkName: "group-admin" */ './Manage.vue')

I suggest to use it with just renaming webpackChunkName into meteorChunkName ;-)