极客时间对于推广渠道会有返利优惠,比如山月在极客时间买了一门课,再把课程分享给好友购买,这时极客时间会向山月返利20元左右。
而我现在做了一个返利平台,你可以在上边通过山月的链接购买课程,此时极客时间会向我返利。为了共同学习,而你可以添加我的微信 (shanyue94),我将把极客时间给我的返利发一个红包全部返给你

# html-webpack-plugin 原理解析

在现代前端开发中,如何把打包后的 JS 资源自动注入到 html 中?

如果最终打包生成的 main.js 既没有做 code spliting,也没有做 hash 化路径。大可以通过在 index.html 中手动控制 JS 资源。

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

但往往事与愿违:

  1. main.js 即我们最终生成的文件带有 hash 值,如 main.8a9b3c.js
  2. 由于长期缓存优化的需要,入口文件不仅只有一个,还包括由第三方模块打包而成的 verdor.js,同样带有 hash。
  3. 脚本地址同时需要注入 publicPath,而在生产环境与测试环境的 publicPath 并不一致

因此需要有一个插件自动做这种事情。在 webpack 的世界里,它是 html-webpak-plugin (opens new window),在 rollup 的世界里,它是 @rollup/plugin-html (opens new window)

而注入的原理为当打包器已生成 entryPoint 文件资源后,获得其文件名及 publicPath,并将其注入到 html 中

html-webpack-plugin 为例,它在 compilation 处理资源的 processAssets 获得其打包生成的资源。伪代码如下,可在 mini-node:html-webpack-plugin (opens new window) 获得源码并运行示例。

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

  apply(compiler) {
    const webpack = compiler.webpack

    compiler.hooks.thisCompilation.tap('HtmlWebpackPlugin', (compilation) => {
      // compilation 是 webpack 中最重要的对象,文档见 [compilation-object](https://webpack.js.org/api/compilation-object/#compilation-object-methods)

      compilation.hooks.processAssets.tapAsync({
        name: 'HtmlWebpackPlugin',

        // processAssets 处理资源的时机,此阶段为资源已优化后,更多阶段见文档
        // https://webpack.js.org/api/compilation-hooks/#list-of-asset-processing-stages
        stage: webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE
      }, (compilationAssets, callback) => {
        // compilationAssets 将得到所有生成的资源,如各个 chunk.js、各个 image、css

        // 获取 webpac.output.publicPath 选项,(PS: publicPath 选项有可能是通过函数设置)
        const publicPath = getPublicPath(compilation)

        // 本示例仅仅考虑单个 entryPoint 的情况
        // compilation.entrypoints 可获取入口文件信息
        const entryNames = Array.from(compilation.entrypoints.keys())

        // entryPoint.getFiles() 将获取到该入口的所有资源,并能够保证加载顺序!!!如 runtime-chunk -> main-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 用以生成资源文件,也是最重要的一步
        compilation.emitAsset('index.html', new webpack.sources.RawSource(content))
        callback()
      })
    })
  }
}

关于山月

我的项目:
我的微信:shanyue94,欢迎交流
Last Updated: 7/21/2019, 11:25:08 AM