乐趣区

关于webpack:htmlpluginwebpack模板内容高度定制化

应用 html-plugin-webpack 时个别都会设置 template 属性配置 html 模板。但这存在毛病: 在团队领有多个我的项目时, html 格局的模板内容无奈做到通用性能的对立和针对特定我的项目的扩大。所以如果将模板内容的配置为 js 内容,获取 js 的齐全编码能力, 既能够封装团队中我的项目通用模板内容, 有保障较高的定制能力.

本文中应用的是 html-plugin-webpack 版本是 v3 版本。v4 版本反对更多的选项反对本文的内容,而不是通过插件实现。

下文就以 html 模板中罕用改用为 js 代码怎么配置.

页面题目

如果不配置 html-webpack-plugin 的 template 选项, 配置 title 选项时, 生成的 html 文件中将会有题目:

module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({title: '测试',}),
  ],
};

生成的文件中:

  <head>
    <meta charset="UTF-8">
    <title> 测试 </title>
  </head>

图标

设置 favicon 选项, html-plugin-webpack 会主动将图片文件复制到 output 目录下, 而后应用, 会主动增加上publicPath.

module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({favicon: './build/favicon.ico',}),
  ],
};

生成的 html 代码中:

<link rel="shortcut icon" href="https://you.com/path/to/favicon.ico">

对于挪动端利用, 图标配置举荐应用 favicons-webpack-plugin.

设置 meta 信息

html 文件中通常须要设置许多 meta 信息, 通过 html-webpack-html 的 meta 选项, 能够生成这些 meta 信息, 如:

module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({
      meta: {author: 'xxx 前端团队',},
    }),
  ],
};

如果须要配置 http-equiv 相干, 须要以下语法:

{ 
    meta: {pragma: { 'http-equiv': 'pragma', 'content': 'no-cache'},
    },
}

如果须要对同一个 http-equiv 属性设置屡次值, 须要为:

{ 
    meta: {cacheControl1: { 'http-equiv': 'cache-control', content: 'no-cache'},
        cacheControl2: {'http-equiv': 'cache-control', content: 'no-siteapp'},
        cacheControl3: {'http-equiv': 'cache-control', content: 'no-transform'},
    },
}

一个较完整的配置:

module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({
      meta: {
        author: 'xxx 前端团队',
        cacheControl1: {'http-equiv': 'cache-control', content: 'no-cache'},
        cacheControl2: {'http-equiv': 'cache-control', content: 'no-siteapp'},
        cacheControl3: {'http-equiv': 'cache-control', content: 'no-transform'},
        expires: {'http-equiv': 'expires', content: '0'},
        compatible: {'http-equiv': 'X-UA-Compatible', content: 'IE=edge,chrome=1'},
      },
    }),
  ],
};

生成的 html 代码:

<meta name="author" content="xxx 前端团队">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="cache-control" content="no-siteapp">
<meta http-equiv="cache-control" content="no-transform">
<meta http-equiv="expires" content="0">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

额定的资源

html 中通常须要一些额定的资源, 如一些库 (jquery, vue) 全局提供, 一些 polyfill 资源等,举荐应用 html-webpack-tags-plugin)来解决。

html-webpack-tags-plugin 只是在模板中增加一些额定的资源标签, 不会对指标资源文件做任何其余操作,须要配合 copy-webpack-plugin 一起应用, copy-webpack-plugin 可能把额定的资源文件 (可能在 node_modules 中) 复制到 webpack 配置中的 output 目录下.

这里以导入的谷歌剖析文件为例, 如果在我的项目根目录下有谷歌剖析单页面利用插件autotrack:

|--assets
  |--autotrack.min.js
  |--autotrack.min.js.map

配置:

module.exports = {
  // ...
  output: {
    // ...
    path: path.resolve(__dirname, 'dist')
  },

  plugins: [
    // 须要开启 HtmlWebpackPlugin 插件
    new HtmlWebpackPlugin({title: '测试',}),
   new CopyWebpackPlugin([{
      from: './assets',
      to: './dist',
      toType: 'dir',
    }]),
    new HtmlWebpackTagsPlugin({
      tags: [{
        path: 'autotrack/autotrack.js',
        attributes: {async: 'true',},
      }],
      scripts: [{
        path: 'https://www.google-analytics.com/analytics.js',
        attributes: {
          type: 'text/javascript',
          async: 'true',
        },
        append: false,
      }],
    }),
  ],
};

生成的 dist 目录:

|--dist
  |--autotrack
    |--autotrack.min.js
    |--autotrack.min.js.map
  |--bundle.js
  |--index.html

html代码内容:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title> 测试 </title>
  </head>
  <body>
  <script type="text/javascript" src="https://www.google-analytics.com/analytics.js" async="true"></script><script type="text/javascript" src="/bundle.js"></script><script type="text/javascript" src="/autotrack/autotrack.js" async="true"></script></body>
</html>

两外也能够应用 html-webpack-tags-plugin 的加强库 html-webpack-deploy-plugin

内联 js 代码或者其余间接在 html 中援用的 js 代码

理论开发过程中通常还须要在 html 文件中蕴含一些内联的 js 代码或者援用第三方代码, 如配置谷歌剖析调用的代码。

能够应用 webpack-concat-plugin:))进行额定的 js 资源的导入。

webpack-concat-plugin 比 html-webpack-tags-plugin 的益处是反对对导入的 js 文件进行压缩.

webpack-concat-plugin 对于指标 js 资源, 会主动复制到以后 webpack 配置 output 目录下, 所以不须要配合 [copy-webpack-plugin] 应用.

如果须要在单页面利用的 index.html 导入 google-analytics-starter.js 内容: 

;((function(){window.ga = window.ga || function () {(ga.q = ga.q || []).push(arguments) }; ga.l = +new Date;
      ga('create', 'UA-63187412-4', 'auto');
      ga('require', 'eventTracker');
      ga('require', 'outboundLinkTracker');
      ga('require', 'urlChangeTracker');
      ga('send', 'pageview');
})());

此时须要在 webpack 配置:

module.exports = {
  // ...
  output: {
    // ...
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new HtmlWebpackPlugin({title: '测试',}),

    new WebpackConcatPlugin({
      uglify: false,
      sourceMap: false,
      name: 'google-analytics-starter',
      outputPath: 'static',
      fileName: '[name].[hash:8].js',
      filesToConcat: [path.resolve(__dirname, './google-analytics-starter.js')],
    }),
  ],
};

生成的 html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title> 测试 </title>
  </head>
  <body>
  <script type="text/javascript" src="/static/google-analytics-starter.8f8f0b03.js"></script><script type="text/javascript" src="/bundle.js"></script></body>
</html>

对于内联代码, 倡议抽取为一个一个 js 文件, 通过 webpack-concat-plugin 援用外链. 对于可能造成的资源申请过多, webpack-concat-plugin 能够配置 filesToConcat 为一个目录(多个目录也行), 会整体打成一个包.

其余逻辑追加

在 html 模板中, 通常还须要设置一些其余的内容, 比方供渲染框架挂载的 dom 元素.

<div id="app"></div>

这种框架临时还未在社区找到, 不过能够编写一个简略的 html-plugin-webpack 插件实现:

const _ = require('lodash');

function resetOptionToFn(option) {if (_.isArray(option)) {return (tags) => {tags.push(...option);
    };
  }
  if (_.isPlainObject(option)) {if (option.before || option.after) {return (tags) => {tags.unshift(...[].concat(option.before || []));
        tags.push(...[].concat(option.after || []));
      };
    }
    return (tags) => {tags.push(option);
    };
  }
  return () => {};
}

module.exports = class HtmlCodePlugin {
  constructor({
    body,
    head,
  } = {}) {this.body = resetOptionToFn(body);
    this.head = resetOptionToFn(head);
  }

  apply(_compiler) {
    const compiler = _compiler;
    compiler.hooks.compilation.tap(this.constructor.name, (compilation) => {compilation.hooks.htmlWebpackPluginAlterAssetTags.tapAsync(this.constructor.name, async (data, cb) => {
        try {this.body(data.body);
          this.head(data.head);
          cb(null, data);
        } catch (error) {cb(error, null);
        }
      });
    });
  }
};

而后就能够在 webpack 中配置:

module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({title: '测试',}),
    new MyPlugin({
      body: {
        tagName: 'div',
        selfClosingTag: false,
        voidTag: false,
        attributes: {id: 'app',},
      },
    }),
  ],
};

生成的 html 代码:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title> 测试 </title>
  </head>
  <body>
  <script type="text/javascript" src="/bundle.js"></script><div id="app"></div></body>
</html>

对于其余的如正文代码, HTML 中 IE 判断语句 if !IE 都能够实现相似 html-webpack-plugin 的插件。

案例代码中曾经公布了一个能够应用的 npm 库: webpack-plugin-html-append-tag。

写在最初

上文中列举了许多 html 模板中的内容怎么用 js 去配置, 这对应用对立构建工具构建多个我的项目的团队十分有用. 利用 js 的编程能力, 能够把团队通用的模板配置内容封装在构建工具内, 并提供对我的项目中对模板内容定制提供 api, 提供封装和扩大能力.

因为过后在开发时,html-plugin-webpack@3 还不反对更多的内容定制,只能通过插件去实现。但随着 v4 版本的到来,很多内容曾经通过配置属性即可反对,无需像本文一样应用一些插件来反对。

退出移动版