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

FOUC with Webpacker 4.0.0.pre.pre.2 and webpack-dev-server with HMR: true #1542

Closed
mikeappell opened this issue Jun 1, 2018 · 11 comments
Closed

Comments

@mikeappell
Copy link

mikeappell commented Jun 1, 2018

I'm getting a flash of unstyled content whenever I reload a page in the most current version of Webpacker 4.x.

I'm mostly using the vanilla config; however, as far as I can tell this takes into account hmr being enabled and switches to style-loader from mini-css-extract-plugin. It's worth mentioning that this is an upgrade from Webpacker v3.5 to 4; I did run rails webpacker:install after the upgrade.

Here's my config:

# Gemfile.lock
webpacker (4.0.0.pre.pre.2)

...

# package.json
"dependencies": {
    "@rails/webpacker": "4.0.0-pre.2",
    "autoprefixer": "^8.5.1",
    "babel-loader": "^7.1.4",
    "bootstrap-sass": "^3.3.7",
    "case-sensitive-paths-webpack-plugin": "^2.1.2",
    "clean-webpack-plugin": "^0.1.19",
    "compression-webpack-plugin": "^1.1.11",
    "css-hot-loader": "^1.3.9",
    "css-loader": "^0.28.11",
    "file-loader": "^1.1.11",
    "font-awesome": "^4.7.0",
    "jquery": "^3.3.1",
    "jquery-ujs": "^1.2.2",
    "mini-css-extract-plugin": "^0.4.0",
    "moment": "^2.22.1",
    "node": "^10.2.0",
    "node-sass": "^4.9.0",
    "popper.js": "^1.14.3",
    "postcss-loader": "^2.1.5",
    "precss": "^3.1.2",
    "resolve-url-loader": "^2.3.0",
    "sass-loader": "^7.0.1",
    "style-loader": "^0.21.0",
    "uglifyjs-webpack-plugin": "^1.2.5",
    "url-loader": "^1.0.1",
    "webpack-manifest-plugin": "^2.0.3",
    "webpack-merge": "^4.1.2"
"devDependencies": {
    "webpack-cli": "^2.1.4",
    "webpack-dev-server": "^3.1.4"

...

# webpacker.yml
default: &default
  source_path: app/javascript
  source_entry_path: packs
  public_output_path: packs
  cache_path: tmp/cache/webpacker

  # Additional paths webpack should lookup modules
  # ['app/assets', 'engine/foo/app/assets']
  resolved_paths: ['vendor/assets/javascripts', 'vendor/assets/stylesheets', 'public/javascripts', 'public/assets/images']

  # Reload manifest.json on all requests so we reload latest compiled packs
  cache_manifest: false

  extensions:
    - .js
    - .jsx
    - .sass
    - .scss
    - .css
    - .module.sass
    - .module.scss
    - .module.css
    - .png
    - .svg
    - .gif
    - .jpeg
    - .jpg

development:
  <<: *default
  compile: true

  # Reference: https://webpack.js.org/configuration/dev-server/
  dev_server:
    https: false
    host: localhost
    port: 3035
    public: localhost:3035
    hmr: true
    # Inline should be set to true if using HMR
    inline: true
    overlay: true
    compress: true
    disable_host_check: true
    use_local_ip: false
    quiet: false
    headers:
      'Access-Control-Allow-Origin': '*'
    watch_options:
      ignored: /node_modules/

test: &test
  <<: *default

  # Compile test packs to a separate directory
  public_output_path: packs-test

production: &production
  <<: *default

  # Production depends on precompilation of packs prior to booting for performance.
  compile: false

  # Cache manifest.json for performance
  cache_manifest: true

staging:
  <<: *production

qa:
  <<: *production

integration:
  <<: *production

...
  
# environment.js
const { environment } = require('@rails/webpacker')
const { env } = require('process')
const { resolve } = require('path');
const rootPath = resolve(__dirname, '../..');
 
const CleanWebpackPlugin = require('clean-webpack-plugin');

const fileLoader = environment.loaders.get('file');
fileLoader.use = [
  {
    loader: 'url-loader',
    options: {
      name: '[path][name]-[hash].[ext]',
      limit: 10000,
      context: 'app/assets',
    }
  }
]
fileLoader.test = /\.(jpg|jpeg|png|gif|tiff|ico|svg)$/i

environment.loaders.append('font', {
  test: /\.(eot|otf|ttf|woff|woff2)$/i,
  use: [
    {
      loader: 'url-loader',
      options: {
        name: '[path][name]-[hash].[ext]',
        limit: 10000,
        context: 'node_modules',
      }
    }
  ]
})

const pathsToClean = [
  'packs',
  'packs-test'
]
const cleanOptions = {
  root: resolve(rootPath, 'public'),
  verbose: true,
}
if (env.NODE_ENV !== 'test') environment.plugins.insert('CleanWebpackPlugin', new CleanWebpackPlugin(pathsToClean, cleanOptions));

module.exports = environment

development.js is vanilla. I'm aware I've a lot of dependencies I can remove from package.json (mostly from playing around with an earlier version of Webpacker) but I assume they're not at fault here.

Apologies if this isn't a ton of info to go on, and am happy to provide anything required. Kudos =)


Edit a day or so later:

I'm going to add a bit to this. When I set hmr and inline to false and have webpack-dev-server running, I had expected to begin seeing the CSS without the flash of unstyled content at the beginning. Instead, I'm never seeing my compiled css and the page remains unstyled.

I'd guess this is somehow due to a misconfiguration on my part with Webpacker; however, I'd have nonetheless expected the default configuration of Webpacker to be close enough to optimal to not require much tinkering on my part, so I'm mentioning this here. I am, of course, also curious what I'm doing wrong 😄

@josephecombs
Copy link

josephecombs commented Jun 13, 2018

I tried a ton of configurations to get hmr working, and nothing works.

I am on 4.0.0.pre.pre.2, and I tried adding each of mini-css-extract-plugin and extract-css-chunks-webpack-plugin

extract-text-webpack-plugin no longer works with Webpack 4.

@G-Rath
Copy link

G-Rath commented Jan 10, 2019

I've got the same thing, using typescript (awesome-typescript-loader), and scss file, imported into my js.

@ksouthworth
Copy link

ksouthworth commented Mar 7, 2019

I'm also seeing the FOUC with webpacker 4.0.2 and Rails 5.2, when using the webpack-dev-server in the Rails development environment

@gauravtiwari
Copy link
Member

You probably want to set extract_css: true in development that way CSS would be extracted out of JS into a separate bundle and you won't see flashing due to CSS in JS.

@jakeNiemiec
Copy link
Member

jakeNiemiec commented Mar 7, 2019

webpack-dev-server uses the style-loader which injects the styles from the JS file. To give you an idea of the process:

*something changes* (If you only changed a js file, start at 5.)

  1. compile scss (🍾 biggest bottleneck 🍾)
  2. run through postcss
  3. pass to css-loader to convert styles into CommonJS
  4. pass to style-loader to make a script that injects cjs styles into the <head>
  5. reimport your CommonJS styles in the js file and re-bundle that file
  6. convert the file to js if you use typescript-loader like @G-Rath
  7. send the new js chunk (along with cjs styles) over a websocket to the browser and execute
  8. inject the cjs styles into the <head>

This issue has also been discussed a lot by non-webpackER folks, eg:

mini-css-extract-plugin works for me, but you need to get it just right with weird things like this since mini-css-extract-plugin does not work with WPD:

if (window.isHot()) {
  import('./styles.scss');
}

Once you start to understand the steps, you can connect the dots in a way that makes sense for your stack. Hope this helps.

@derrickmar
Copy link

@gauravtiwari setting extract_css = true in development requires that you use <%= stylesheet_pack_tag %> in order to load css.

In general, our team is experiencing this pain point: #1720 (comment).

Which requires you add the right style sheets with stylesheet_pack_tag whenever a react component uses a stylesheet. Is there a better way to do this?

Screenshot 2019-05-18 17 37 55

@jakeNiemiec
Copy link
Member

@derrickmar To solve this problem, we set the style extractor to only extract styles from files following a naming convention, eg: name.index.scss. This allows your react components to import their local styles via JS, thus, you would not need to include those tags.

Here is the regex for the MiniCssExtractPlugin loader: test: /(.+?)\.index\.scss$/
Here is the regex for the local styles loader: test: /^((?!\.index).)+\.s?css$/

This is a batteries not included solution, you will need to somehow override webpackERs internals in order to achieve the behavior you want:

use.unshift(MiniCssExtractPlugin.loader)

@derrickmar
Copy link

@jakeNiemiec that's an interesting idea but that doesn't solve FOUC because local styles are still imported via JS as you said.

@jakeNiemiec
Copy link
Member

@derrickmar doesn't solve FOUC because local styles are still imported via JS as you said

A: If you add the JS in your <head>, there should be no FOUC. There will always be a bit of FOUC with webpack-dev-server since the code is optimized for quick code changes, not render/loading speed. But, this new development may be the behavior you are looking for: #2062 (comment)

B: If you intend for your app to be rendered server-side, you would need to seek help in other SSR-related issues in this repo like #2085 or #2074. (I don't use SSR)

@LeBenLeBen
Copy link

LeBenLeBen commented Dec 22, 2019

I was able to prevent FOUC and enable HMR for stylesheets like this:

In Webpacker development.js file:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// Replace default MiniCssExtractPlugin loader config to enable HMR
// You might want to change "sass" to the loader that you actually use
// Default loaders are `css`, `sass`, `moduleCss` or `moduleSass`
environment.loaders.get('sass').use[0] = {
  loader: MiniCssExtractPlugin.loader,
  options: {
    hmr: true,
  },
};

// Remove [hash] from filename in the plugin so HMR is able to match the file to refresh
environment.plugins.get('MiniCssExtract').options.filename = 'css/[name].css';

In webpacker.yml, enable CSS extraction for the development env:

development:
  extract_css: true
  # ...
  dev_server:
    hmr: true
    inline: true
    # ...

Ensure you have <%= stylesheet_pack_tag "your_pack_name" %> somewhere in your <head> in development too.

@guillaumebriday
Copy link
Member

Can this issue be closed ?

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

9 participants