Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

Hello, I’m Shanyue.

This is my front end engineering knowledge card collection 5/36 published in Denver

In modern front-end development, how to automatically inject packaged JS resources into HTML?

If the resulting main.js is packaged without either code spliting or a hashed path. You can manually control JS resources in index.html.

<body>
  <script src="main.js" defer />
</body>
Copy the code

But it often doesn’t:

  1. main.jsThat is, we end up generating files with hash values such asmain.8a9b3c.js.
  2. Because of the need for long-term cache optimization, there is not only one entry file, but also packaged by third-party modulesverdor.js, again with hash.
  3. Script addresses also need to be injectedpublicPathIn the production environment and the test environment, the publicPath is different

So you need a plug-in to do this automatically. In the Webpack world, it’s htML-webpak-plugin, and in the rollup world, it’s @rollup/plugin-html.

The principle of injection is that after the wrapper has generated the entryPoint file resource, it obtains its filename andpublicPathAnd inject it into HTML

In the case of the HTml-webpack-plugin, it obtains its packaged resources from processAssets of the compilation processing resources. The pseudo-code is as follows, available in the mini-node:html-webpack-plugin source code and run the sample.

class HtmlWebpackPlugin {
  constructor(options) {
    this.options = options || {}
  }

  apply(compiler) {
    const webpack = compiler.webpack

    compiler.hooks.thisCompilation.tap('HtmlWebpackPlugin'.(compilation) = > {
      // Compilation is the most important object in webpack, See document [compilation - object] (https://webpack.js.org/api/compilation-object/#compilation-object-methods)

      compilation.hooks.processAssets.tapAsync({
        name: 'HtmlWebpackPlugin'.// The time when processAssets processes the resource. This stage is after the resource has been optimized. See the documentation for more stages
        // https://webpack.js.org/api/compilation-hooks/#list-of-asset-processing-stages
        stage: webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE
      }, (compilationAssets, callback) = > {
        // compilationAssets will get all generated resources such as chunk.js, images, CSS

        // Get the webpac.output.publicPath option, (PS: publicPath option may be set by function)
        const publicPath = getPublicPath(compilation)

        // This example considers only a single entryPoint case
        // compilation. Entrypoints can be used to obtain information about imported files
        const entryNames = Array.from(compilation.entrypoints.keys())

        // entryPoint.getFiles() will fetch all the resources in this entry and guarantee the loading order!! If the runtime - the chunk - > the main - the chunk
        const assets = entryNames.map(entryName= > compilation.entrypoints.get(entryName).getFiles()).flat()
        const scripts = assets.map(src= > publicPath + src)
        const content = html({ title: this.options.title || 'Demo', scripts })

        // emitAsset is used to generate resource files, which is also the most important step
        compilation.emitAsset('index.html'.new webpack.sources.RawSource(content))
        callback()
      })
    })
  }
}
Copy the code