Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code Splitting and SSR styles #118

Closed
alexandcote opened this issue Mar 19, 2018 · 24 comments
Closed

Code Splitting and SSR styles #118

alexandcote opened this issue Mar 19, 2018 · 24 comments

Comments

@alexandcote
Copy link

First of all, you are genius ! Thanks for all your libs 🙏

I tested this lib during the weekend and it worked like a charm, I love the approach you propose. I'm wondering If you already had issue with brief flash of unstyled content. I found two other closed issues #104 and jaredpalmer/razzle#307 but I think the issue still there with code splitting.

I made some tests and here the result:

Note here that both app run in production mode.

Lazy loaded Home component

2018-03-18 21 34 35

No lazy loaded

2018-03-18 21 33 30

Any idea how can I fix this and keep the code splitting 💅

Thanks a lot again for your time and work 🥇

@fredrik-sogaard
Copy link
Contributor

Can you share how you load your styles? Is is plain css, styled components or something else?

@fredrik-sogaard
Copy link
Contributor

Ah, it’s the example code? You don’t load any other styles?

@alexandcote
Copy link
Author

Yeah the example code, no other styles was added.
I think the styles are inject in the HTML when the component is loaded.

Thanks for your help 😄

@jaredpalmer
Copy link
Owner

This is a razzle problem i think. However, I don't have a great answer for you. Open to PR's though.

@krazyjakee
Copy link

krazyjakee commented Mar 22, 2018

@alexandcote Hmm, is it possible these issues are related? #120

On the server, while material-ui does put out class names on elements, it does not output styles in the header. I get the same unstyled to styled flicker when reloading the page.

EDIT: Ignore me, the issue still exists without async component, so it's a material-ui issue.

@alexandcote
Copy link
Author

@krazyjakee you can try to remove the lazy loading on the home component.

From

{
    path: "/",
    exact: true,
    component: asyncComponent({
      loader: () => import("./Home"), // required
      Placeholder: () => <div>...LOADING...</div> // this is optional, just returns null by default
    })
  },

To

{
    path: "/",
    exact: true,
    component: Home
}

Let me know if it resolve your problem

@krazyjakee
Copy link

@alexandcote sorry, I didn't think to test this first. It did not resolve the problem so it's not the same issue.

@garmeeh
Copy link

garmeeh commented Mar 22, 2018

In After.js here, is this only client side rendered?

If you disable javascript you can see that it renders the Home component contents but only the initial client css that is imported in client.js

@garmeeh
Copy link

garmeeh commented Mar 22, 2018

Tested in Razzle with javascript disabled and page loads with styles as expected so might be an After.js problem?

@alexandcote
Copy link
Author

@garmeeh I think it's a problem with asyncComponent. I try to figure how we can fix this but for now I didn't find a solution. If anyone have a idea, let me know ✌️

@Madumo
Copy link
Contributor

Madumo commented Mar 23, 2018

@alexandcote This is actually a "problem" with webpack, where css chunks are not handled well when imported from a module that is imported with a dynamic import(). You actually need a plugin to handle those chunks.

Might actually be a good idea if after.js would use this by default. After all, code splitting and SSR is the point here. I'll look into it.

But, you could also just go around that problem and use one of the many css-in-js™ solution at your disposal!

I highly suggest JSS.

@alexandcote
Copy link
Author

I tested with JSS like @Madumo suggest and it worked perfectly ! Thanks again 😄
Maybe we should change the example to use JSS @jaredpalmer ?

@garmeeh
Copy link

garmeeh commented Mar 23, 2018

@Madumo makes sense alright. I think this is a Razzle issue rather than after.js since it's building the server etc.

I do think it would be great to have this by default. Currently using extract-css-chunks-webpack-plugin on another project and it means having to use react-universal-component as well. Adding this to Razzle wouldn't be trivial from my understanding on how to implement it.

@jaredpalmer
Copy link
Owner

This is a Razzle issue. However, with latest alpha's of razzle, we use mini-css-extract. Let's move this issue over there.

@AndyArcherKG
Copy link

AndyArcherKG commented Jun 6, 2018

This still seems to be an issue purely with the lazy loading of a component. As @alexandcote states
#118 (comment)

When you make the component not lazy load you do not get the FOUC.
I'm importing a static css file e.g import 'my-stylesheet.css' and this ends up client side rendering instead leading to flash of unstyle content.

I've noticed with the other Razzle examples the CSS file is including on the SSR side, so its definitly the lazy loading side of things associated with After.js asyncComponent()

additionally when the lazy load kicks in the css file is not outputted to the assets.json to be included.

Any ideas?

@SheikhG1900
Copy link

SheikhG1900 commented Aug 7, 2018

It is actually not related to asyncComponent directly. It is webpack behavior to exlude .css files are inside the .js files which are lazily loaded. When we run npm run build, we can see more then one css file are built.
In my case I am loading ./Home file lazily in App.js file.

App.js

import './App.css'
import './tailwind.css'
import React from 'react'
import Route from 'react-router-dom/Route'
import Switch from 'react-router-dom/Switch'
import Loadable from 'react-loadable'

const Home = Loadable({
  loader: () => import('./Home'),
  loading: () => null,
})
const App = () => (
  <Switch>
    <Route exact path="/" component={Home} />
  </Switch>
)

when I run npm run build i got following.

build command result (please note, two .css files are here.)
Second chunk build\static\css\1.bundle.ca7057dd.css contains ./Home.css contents.

  44.79 KB  build\static\js\bundle.a50ceeae.js
  6.56 KB   build\static\js\2.17674f1a.chunk.js
  819 B     build\static\css\bundle.cf147a1e.css  --- main  --
  622 B     build\static\js\1.862187fa.chunk.js
  261 B     build\static\css\1.bundle.ca7057dd.css -- with home.css --

assets.json
Our assets.json file contains the link of first bundle from above list.

{
  "client": {
    "css": "/static/css/bundle.cf147a1e.css",  // -- main --
    "js": "/static/js/bundle.a50ceeae.js"
  }
}

So flash will be noticed in production mode as second .css is not getting referenced.

Html

<html lang="">
  <head>
    <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
    <meta charSet='utf-8' />
    <title>Welcome to Razzle</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="/static/css/bundle.5c3433c4.css">  <!-- main only -->
    </head>
-----------
-----------
</html>

Solution
we need to explicitly include that .css. Below is the link which explain how to handle server rendering with react-loadable.
https://github.com/jamiebuilds/react-loadable#how-do-i-handle-other-styles-css-or-sourcemaps-map-with-server-side-rendering.

@heshamelmasry77
Copy link

@SheikhG1900 hey man can you please tell me how you got the other styles in the chunks ?

@greatwitenorth
Copy link

It seems like this was never resolved. I'm still seeing the flash behavior described by the OP. I'm using a vanilla install of afterjs, ran the production build and served it and I'm still seeing the flash.

@SheikhG1900
Copy link

SheikhG1900 commented Nov 23, 2018

@SheikhG1900 hey man can you please tell me how you got the other styles in the chunks ?

@heshamelmasry77 please check this code
https://github.com/SheikhG1900/with-razzle/blob/73774e85bd28d1a579b9c9f8ca923d689312ed0e/src/server.tsx#L35-L51

@swcho
Copy link

swcho commented Jan 16, 2019

It's bit ugly but I got to manage to work it with isomorphic-style-loader.

https://github.com/swcho/razzle-with-afterjs-custom/blob/a19cea7ee334b83da2de02b4a44d8aaf2a0fd24f/src/server.tsx#L22-L70

At least, for style perspective, after.js should provide some way to inject additional header after in render function.
But, it would be tricky as style or link tag can be provided after render finished...

@krazyjakee
Copy link

krazyjakee commented Jan 16, 2019

We had very strange issues with certain classes not matching up on initial client render.

The fix was to use only react-dom/render in production but for development, only use hydrate after the initial mount.

I may still have a misunderstanding of how everything works but this fixed it for us. We have a very specific setup so this is pseudo code.

client.js

import React from 'react';
import { render, hydrate } from 'react-dom';
import { ensureReady } from '@jaredpalmer/after';
import After from '@jaredpalmer/after/After';
import routes from './routes';

ensureReady(routes).then(data => {
  let renderMethod = render;
  if (window.NODE_ENV === 'development') {
    renderMethod = module.hot ? render : hydrate;
  }

  renderMethod(
    <After data={data} routes={routes} />,
    document.getElementById('root'),
    () => {
      const jssStyles = document.getElementById('jss-ssr');
      if (jssStyles && jssStyles.parentNode) {
        jssStyles.parentNode.removeChild(jssStyles);
      }
    }
  );
});

if (module.hot) {
  module.hot.accept();
}

@s123121
Copy link

s123121 commented Mar 21, 2019

From what I research, you may suffer for FOUC because your main css bundle does not include all the necessary styles (because your css is spit among entry). Digging into the code, it is marked as @todo https://github.com/jaredpalmer/razzle/blob/43160eb1cdbbe5b4353a714a3db7faba629a1bd3/packages/razzle/config/createConfig.js#L555

You can combine all css into 1 file inside your razzle.config.js

  config.optimization = {
      splitChunks: {
        cacheGroups: {
          styles: {
            name: 'styles',
            test: /\.css$/,
            chunks: 'all',
            enforce: true,
          },
        },
      },
    }

You also need to change your html accordingly

@Asmadeus
Copy link

Solution
we need to explicitly include that .css. Below is the link which explain how to handle server rendering with react-loadable.
https://github.com/jamiebuilds/react-loadable#how-do-i-handle-other-styles-css-or-sourcemaps-map-with-server-side-rendering.

@SheikhG1900 Is it possible to handle css chunks with asyncComponent from after.js? I tried to do it follow by instructions with react-loadable, but in this case method getInitialProps of component doesn't execute.

@nimaa77
Copy link
Collaborator

nimaa77 commented Jan 20, 2020

it's too late I know, but we fixed this in #232 and it's going to get merged soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests