今天在写代码的时候,发现webpack 的热更新功能执行了两次,每次刷新页面,就执行两次初始化函数,导致了bug,决定解决下。
看控制台可以发现,热更新的日志打印了两遍:
[HMR] Waiting for update signal from WDS...
[HMR] Waiting for update signal from WDS...
// console.log('init')
// console.log('init')
[WDS] Hot Module Replacement enabled.
[WDS] Hot Module Replacement enabled.
[WDS] Live Reloading enabled.
[WDS] Live Reloading enabled.
刚开始以为是启动了两个webpack-dev-server 导致的,但是关闭掉一个并没有解决问题,网上查了好久,也没找到多少有用的信息,最后用英文搜索了一会儿找到了一个帖子。
文章里面说,是由于在html模板文件中,包含了下面这行代码导致的:
<script src="/bundle.js"></script>
原因是因为,html-webpack-plugin 插件,会自动帮我们注入script 标签,如果我们自己再引用一遍,那就会加载两遍了。听起来很有道理,我审查元素看了一下,果然文档中存在两个script 标签。

而我的模板文件是这样的:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"></div> <div id="module"></div> <script src="/index.js"></script> </body> </html>
可以看到,截图中第一个js 引文是来自我写死的文件,第二个是插件帮我们注入的,后边还带着一串哈希值。找到原因就好办了,删除之后,果然重复加载的问题解决了。终于可以下班了,都11点了🤦♂️。
题外话
重复加载会导致什么问题呢?
最明显的是很多初始化函数会执行两次,如果是幂等的函数还好,只是影响性能;但如果函数不是幂等的,就会引入bug,和很多意想不到的问题。
举个栗子:
// utils.js (function(){ const log = console.log console.log = function (...parms) { log('info:', ...parms) } })() // main.js console.log('hello') // 预期 info: hello // 实际 info:info: hello
原因就是,热更新执行了两次,导致这段代码执行了两次,第二次写的函数,其实已经被第一次加载的时候重写过一遍了。
有时候在写框架的时候,考虑到使用者可能会不小心将代码重复执行,比如上面的hot load 的问题。但如果代码重复执行了,可能会导致bug ,这个时候,需要保证代码重复执行,也是幂等的。可以像下面这样改写一下:
(function () { const log = console.log function newLog (...parms) { log('info:', ...parms) } newLog.isRewrite = true //加个标记 if (newLog.isRewrite) return console.log = newLog })()