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

前言

前篇文章介绍了 Webpack 里的三种 hash 类型,如何给构建输出的文件加 hash 值:juejin.cn/post/728000...

日常开发中,可能会遇到有些第三方库不是通过 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 下。用法很简单。

js 复制代码
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 中。

js 复制代码
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,就支持了缓存。

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

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

js 复制代码
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 也可以支持。

js 复制代码
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;
    },
}),

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

contenthash

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

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

对于 Webpack 三种 hash 类型区别,参考之前文章:怎么给 Webpack 构建生成的文件加version?

那能不能用 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 值。

js 复制代码
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\css 静态文件 copy 到 output 中,并且自动引用到 html head 中,而且支持加 hash 支持 version 缓存。主要用到了三个 Webpack plugins:

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

相关推荐
却尘8 分钟前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare9 分钟前
浅浅看一下设计模式
前端
Lee川13 分钟前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix40 分钟前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人43 分钟前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl1 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人1 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼1 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端
布列瑟农的星空1 小时前
前端都能看懂的Rust入门教程(三)——控制流语句
前端·后端·rust