How does bundle.js inject style content into index.html?

Viewed 2964

I'm reading a Single Page Application(React) book which describes the use of bundler as:

App.js

import React from 'react';
import './App.css';

export default function App() {
   ...
}

When you use the import keyword to declare a dependency on static content, the decision about how to handle the content is left to the development tools i.e webpack. For small files, the content will be included in the bundle.js file, along with the JavaScript code required to add the content to the HTML document. This is what happens with the App.css file that was imported

so I add a style in App.css to check:

App.css

testClass {
   background-color: red 
}

I can see that index.html does have a style element that contain the content of App.css. I copied the bundle.js file to my code editor and tried searching testClass keyword in bundle.js, there was nothing. so what does it mean by "the content will be included in the bundle.js file"? I mean if it is there, I should be able to see some javascript code to manipulate DOM API as:

var styleNode = document.createElement('style');
styleNode.styleSheet.cssText = 'testClass { background-color: red }';
document.getElementsByTagName('head')[0].appendChild(styleNode);

The code is simplified by me, but you get the idea, which is that I should at least can locate testClass keyword in bundle.js if the content will be included in the bundle.js file? so how does bundle.js inject style content into index.html?

1 Answers

Im assuming that you are using create-react-app (which by default uses webpack for asset management)

If that's the case...

  • The style tag is not injected from client, but from the server, when you run npm run build
  • Webpack uses plugin system to generate assets. One of them being HtmlWebpackPlugin

As per the documentation.

The HtmlWebpackPlugin simplifies creation of HTML files to serve your webpack bundles.

The plugin will generate an HTML5 file for you that includes all your webpack bundles in the body using script tags. Just add the plugin to your webpack config as follows:

var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');

module.exports = {
  entry: 'index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js'
  },
  plugins: [new HtmlWebpackPlugin()]
};

This will generate a file dist/index.html containing the following:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>webpack App</title>
  </head>
  <body>
    <script src="index_bundle.js"></script>
  </body>
</html>

If you have multiple webpack entry points, they will all be included with tags in the generated HTML.

If you have any CSS assets in webpack's output (for example, CSS extracted with the MiniCssExtractPlugin) then these will be included with tags in the element of generated HTML.

create-react-app by default uses MiniCssExtractPlugin (For production build). So inserting the <link> to html is handled by HtmlWebpackPlugin

Input to the template

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html> 

Output from HtmlWebpack plugin

<!doctype html><html lang="en"><head><title>React App</title><link href="/static/css/main.b100e6da.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(c){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],l=0,a=[];l<n.length;l++)t=n[l],Object.prototype.hasOwnProperty.call(i,t)&&i[t]&&a.push(i[t][0]),i[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(c[r]=o[r]);for(s&&s(e);a.length;)a.shift()();return p.push.apply(p,u||[]),f()}function f(){for(var e,r=0;r<p.length;r++){for(var t=p[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==i[u]&&(n=!1)}n&&(p.splice(r--,1),e=l(l.s=t[0]))}return e}var t={},i={1:0},p=[];function l(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return c[e].call(r.exports,r,r.exports,l),r.l=!0,r.exports}l.m=c,l.c=t,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(r,e){if(1&e&&(r=l(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)l.d(t,n,function(e){return r[e]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var r=this["webpackJsonpcra-ejected"]=this["webpackJsonpcra-ejected"]||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;f()}([])</script><script src="/static/js/2.af205322.chunk.js"></script><script src="/static/js/main.be430d78.chunk.js"></script></body></html>
Related