使用 Webpack 5 - 使用服务器端模板引擎时如何在 html 中引用带有 [contenthash] 替换的文件?

问题描述 投票:0回答:1

使用时

copy-webpack-plugin

并将

to:
指定为
'images/[name].[contenthash][ext]'

如何在模板中引用这些文件而不使用

html-webpack-plugin
还是
handlebars-loader

我已经用以下解决方案解决了这个问题:

/**
 * Create a new plugin class.
 */
const StoreAssetsInfoKeysPlugin = class StoreAssetsInfoKeysPlugin {
  /**
   * Define `apply` as its prototype method.
   * @param hooks
   */
  apply({ hooks }) {
    /**
     * Using the 'afterCompile' hook:
     * Use the 'assetsInfo' Map which contains the output of all asset paths,
     * construct an array containing all these paths,
     * then write an 'assets.json' file somewhere in your project.
     */
    hooks.afterCompile.tapAsync('StoreAssetsInfoKeysPlugin', ({ assetsInfo }, callback) => {
      fs.writeFileSync('/path/to/assets.json', JSON
        .stringify(Array
          .from(assetsInfo.entries())
          .map(([key]) => `/assets/${key}`)));

      callback();
    });
  }
};
/**
 * The Webpack configuration for this
 * example looks like this.
 */
export default {
  plugins: [
    new StoreAssetsInfoKeysPlugin(),
    new CopyPlugin({
      patterns: [{
        from: '/path/to/images/*',
        to: 'images/[name].[contenthash][ext]',
      }],
    }),
  ],
};
/**
 * Create a helper function which will find the
 * correct path from the 'assets.json'
 */
export const getAssetPath = function getAssetPath(directory, filename, ext) {
  return JSON
    .parse(fs.readFileSync('/path/to/assets.json'))
    .find((value) => value
      .match(`^.*?\\b${directory}\\b.*?\\b${filename}\\b.*?\\.${ext}\\b.*?$`));
};
/**
 * Then use the helper function within your
 * server-side templating engine:
 */
getAssetPath('images', 'my-image', 'svg');

这是一个合适的解决方案吗?

这是我在服务器端模板中通过

[contenthash]
替换来实现获取文件名的第一个解决方案。

我是 Webpack 的新手,因此对这种方法的任何想法将不胜感激。

javascript webpack handlebars.js webpack-5 templating-engine
1个回答
0
投票

我尝试详细定义目标:

  1. 我们有一个模板文件(例如index.hbs)
  2. 源模板文件中应引用
    source files
    脚本、样式、图像和其他资源
  3. 在处理后的模板文件中应进行哈希处理
    output filenames
    包含
    contenthash
  4. 模板文件一定不能渲染成HTML,要通过服务端渲染来使用模板

目标正确吗?

如果是,那么您可以使用一个强大的“html-bundler-webpack-plugin”,而无需

html-webpack-plugin
handlebars-loader
copy-webpack-plugin

例如有示例的文件结构:

src/views/index.hbs
src/scss/styles.scss
src/js/main.js
src/images/picture.png

dist/ <= output directory for processed files

有源模板文件

src/views/index.hbs
(或其他HTML文件):

<html>
<head>
  <!-- source files with path relative to the template file -->
  <link href="../scss/styles.scss" rel="stylesheet">
  <script src="../js/main.js" defer="defer"></script>
</head>
<body>
  {{> header }}
  <h1>Hello World!</h1>
  
  <!-- source image file with path relative to the template file -->
  <img src="../images/picture.png">

  {{> footer }}
</body>
</html>

webpack.config.js

const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
module.exports = {
  output: {
    path: path.resolve(__dirname, 'dist'), // output path for processed files
  },,
  plugins: [
    new HtmlBundlerPlugin({
      entry: {
        // define templates here
        // the key is output file path w/o extension, e.g.:
        index: 'src/views/index.hbs', // => dist/index.hbs
      },
      js: {
        // output filename of compiled JavaScript
        filename: 'js/[name].[contenthash:8].js',
      },
      css: {
        // output filename of extracted CSS
        filename: 'css/[name].[contenthash:8].css',
      },
      // defaults used Eta (ESJ like) template engine to render into HTML
      // if you will render .hbs to .html, then define as `handlebars`
      preprocessor: false, // <= disable rendering into HTML to keep original template content
    }),
  ],

  module: {
    rules: [
      {
        test: /\.(scss)$/,
        use: ['css-loader', 'sass-loader'],
      },
      {
        test: /\.(ico|png|jp?g|svg)/,
        type: 'asset/resource',
        generator: {
          // output filename of images
          filename: 'img/[name].[hash:8][ext]',
        },
      },
    ],
  },
};

如果您要保留原始模板内容并仅将源资源文件替换为其散列输出文件名,则使用

preprocessor: false
插件选项禁用渲染。

处理后的(未渲染为 html)模板将如下所示:

<html>
<head>
  <!-- output filenames relative to the dist/ directory -->
  <link href="css/styles.0f23efdf.css" rel="stylesheet" />
  <script src="js/main.5317c1f6.js" defer="defer"></script>
</head>
<body>
  {{> header }}
  <h1>Hello World!</h1>

  <!-- output filenames relative to the dist/ directory -->
  <img src="img/picture.7b396424.png" />

  {{> footer }}
</body>
</html>

如果您要将任何模板渲染为 HTML,您可以“开箱即用”使用受支持的模板引擎之一:EtaEJSHandlebarsNunjucksLiquidJS

将车把模板渲染为 HTML 的插件配置:

  new HtmlBundlerPlugin({
      entry: {
        // define templates here
        // the key is output file path w/o extension, e.g.:
        index: 'src/views/index.hbs', // => dist/index.html
      },
      js: {
        // output filename of compiled JavaScript
        filename: 'js/[name].[contenthash:8].js',
      },
      css: {
        // output filename of extracted CSS
        filename: 'css/[name].[contenthash:8].css',
      },
      // specify the `handlebars` template engine
      preprocessor: 'handlebars',
      // define handlebars options
      preprocessorOptions: {
        partials: ['src/views/partials'],
      },
    })

渲染的 HTML:

<html>
<head>
  <!-- output filenames relative to the dist/ directory -->
  <link href="css/styles.0f23efdf.css" rel="stylesheet" />
  <script src="js/main.5317c1f6.js" defer="defer"></script>
</head>
<body>
  <div class="header">html of your header partials</div>
  <h1>Hello World!</h1>

  <!-- output filenames relative to the dist/ directory -->
  <img src="img/picture.7b396424.png" />

  <div class="footer">html of your footer partials</div>
</body>
</html>
© www.soinside.com 2019 - 2024. All rights reserved.