likes
comments
collection
share

如何通过Webpack把第三方js文件自动引用到html中,并且支持hash

作者站长头像
站长
· 阅读数 15

前言

日常开发中,可能会遇到有些第三方库不是通过 npm 引用,而是直接通过 download 下来的 js\css 文件,然后直接通过 append 到 html head 里全局使用的。这样就会有两个操作需要支持:

  1. 把用到的 js\css 静态文件 copy 到服务器环境路径下,即 Webpack output.path 路径下。
  2. 把这些 js\css 静态文件 append 到 html head中,并解决 version 更新缓存问题。

copy-webpack-plugin

github.com/webpack-con…

可以将需要在html里直接引用的第三方js\css文件,copy 到 output 下。用法很简单。

const CopyPlugin = require("copy-webpack-plugin");

plugins: [
    new CopyPlugin({
        patterns: [
            { context: './lib', from: '**', to: 'lib', info: { minimized: true } },
        ],
    }),

此例是把 ./lib 路径下所有文件 copy 到 Webpack 配置的 output.path 路径下的 ib 路径下。

也可以支持单个文件 copy,也支持glob语法匹配,更多用法参考官网。

html-webpack-tags-plugin

github.com/jharris4/ht…

配合 html-webpack-plugin,支持把具体的 js\css 静态文件引入到 html 中。

const HtmlWebpackTagsPlugin = require('html-webpack-tags-plugin');

plugins: [
    new HtmlWebpackPlugin(),
    new HtmlWebpackTagsPlugin({
        links: [
            `lib/lib1.css`,
        ],
        scripts: [
            `lib/lib1.js`,
        ],
        append: false, // 设置为false会把引入的文件顺序高于html-webpack-plugin中的chunk文件
        hash: true, // 引入文件时,加上hash后缀
    }),

这里注意 hash: true 这个属性,会给文件自动加上hash后缀。相当于给这些文件加上了version,就支持了缓存。

如何通过Webpack把第三方js文件自动引用到html中,并且支持hash

如何通过Webpack把第三方js文件自动引用到html中,并且支持hash

生成的 html 文件中可以看到,lib文件后面自动加上了hash参数。

如果文件名本来就有参数,也可以单独设置某个文件的hash。

scripts: [
    {
        path: `lib/lib2.js?p1=123`,
        hash: (path, hash) => path + '&hash=' + hash, // 只作用单个文件,生成 lib/lib2.js?p1=123&hash={hash}
    },
    `lib/lib1.js`,
],
hash: true, // 作用所有文件

把hash加到文件名中

Webpack 构建 chunk 文件,都支持文件名内部加 hash,比如这样 index.{hash}.jshtml-webpack-tags-plugin 也可以支持。

new CopyPlugin({
    patterns: [
        // 首先copy文件时,给文件名加上hash
        { context: './lib', from: '**', to: 'lib/[name].[hash][ext]', info: { minimized: true } },
    ],
}),
new HtmlWebpackTagsPlugin({
    links: [
        `lib/lib1.css`,
    ],
    scripts: [
        `lib/lib1.js`,
    ],
    append: false,
    hash: function (assetName, hash) {
        // 可以自定义append时的文件名吗,修改原文件名加上hash
        assetName = assetName.replace(/\.js$/, '.' + hash + '.js');
        assetName = assetName.replace(/\.css$/, '.' + hash + '.css');
        return assetName;
    },
}),

如何通过Webpack把第三方js文件自动引用到html中,并且支持hash

如何通过Webpack把第三方js文件自动引用到html中,并且支持hash

这样就跟 chunk 文件名一致了。

contenthash

但是还有个问题,Webpack 的 hash 有三种类型,hash chunkhash contenthash,这里的hash对应的是第一种 hash 类型,即对应全局唯一hash值,任何入口文件改动时,都会更改这个hash,这样就有两个问题。

  1. 第三方文件没改动,但是改了其它code,则构建时会改变这个hash值,浏览器加载第三方文件时,就会重新请求,不会走缓存。
  2. 如果只改动第三方文件,不改其它code,那构建时这个hash值还是旧的,这样浏览器就还会读缓存。

那能不能用 contenthash 呢?每次构建时,通过 html-webpack-tags-plugin 引用的文件都会根据自己的文件内容生成 hash,这样上面俩问题就解决了。

不幸的是 html-webpack-tags-plugin 不支持 contenthash,得自己写逻辑生成:github.com/jharris4/ht…

实现也不复杂,原理就是在调用 plugins 之前,通过 crypto 或 md5 之类的工具,把这些文件挨个转换生成一个 contenthash,然后在 copy plugin 和 tags plugin 里把 contenthash 拼接到对应文件名里。

不用hash

不过有个更简单的方案,把第三方文件放在以version命名的路径下,或者直接给文件名里加上version。例如:lib/v1.1/lib1.jslib/lib1-1.1.js,每次更新版本都改下folder或file name,也就不用加hash了。

这样有个缺点是每次更新第三方,都得改下文件名以及 Webpack 里配置的文件名。

运行时获取hash

有些静态 js\css 是需要在js里运行时引用的,并不是直接 append 到 html 中的,这种是需要在运行时获取 hash 值,Webpack 提供了一个编译变量 __webpack_hash__

webpack.docschina.org/api/module-…

这个变量同样是全局的 hash 值。

const src = '/lib/lib1.js?' + __webpack_hash__;
const tag = document.createElement('script');
tag.type = 'text/javascript';
tag.src = src;
document.head.appendChild(tag);

编译后的代码里,可以debug看下取到了hash值。

如何通过Webpack把第三方js文件自动引用到html中,并且支持hash

总结

本文主要介绍了如何通过 Webpack,每次构建时把一些例如第三方的 js\css 静态文件 copy 到 output 中,并且自动引用到 html head 中,而且支持加 hash 支持 version 缓存。主要用到了三个 Webpack plugins:

源码参考:github.com/markz-demo/…