使用 webpack-dev-derver 监听客户端的热更新事件?



这是一个边缘情况,但了解它会有所帮助。

当使用 webpack-dev-server 开发扩展以保持扩展代码最新时,收听"webpackHotUpdate"会很有用

带有内容脚本的 Chrome 扩展程序通常有两个方面:

  1. 背景
  2. 注入的内容脚本

当将 webpack-dev-server 与 HMR 一起使用时,后台页面保持同步就可以了。 但是,内容脚本需要重新加载扩展才能反映更改。 我可以通过侦听来自hotEmmiter的"webpackHotUpdate"事件,然后请求重新加载来解决此问题。 目前,我以一种可怕且非常不可靠的黑客方式工作。

var hotEmitter = __webpack_require__(XX)
hotEmitter.on('webpackHotUpdate', function() {
console.log('Reloading Extension')
chrome.runtime.reload()
})

XX 仅表示当前分配给发射器的编号。 可以想象,每当构建更改时,这种情况都会发生变化,因此这是一个非常临时的概念证明。

我想我可以设置自己的套接字,但这似乎有点矫枉过正,因为事件已经被转移了,我只是想听。

我最近才对 webpack 生态系统更加熟悉,因此非常感谢任何指导。

好的!

我通过环顾四周解决了这个问题:

https://github.com/facebookincubator/create-react-app/blob/master/packages/react-dev-utils/webpackHotDevClient.js

非常感谢 create-react-app 团队明智地使用评论。

我创建了一个精简版本,专门用于处理扩展开发的重新加载条件。

var SockJS = require('sockjs-client')
var url = require('url')
// Connect to WebpackDevServer via a socket.
var connection = new SockJS(
url.format({
// Default values - Updated to your own
protocol: 'http',
hostname: 'localhost',
port: '3000',
// Hardcoded in WebpackDevServer
pathname: '/sockjs-node',
})
)
var isFirstCompilation = true
var mostRecentCompilationHash = null
connection.onmessage = function(e) {
var message = JSON.parse(e.data)
switch (message.type) {
case 'hash':
handleAvailableHash(message.data)
break
case 'still-ok':
case 'ok':
case 'content-changed':
handleSuccess()
break
default:
// Do nothing.
}
}
// Is there a newer version of this code available?
function isUpdateAvailable() {
/* globals __webpack_hash__ */
// __webpack_hash__ is the hash of the current compilation.
// It's a global variable injected by Webpack.
return mostRecentCompilationHash !== __webpack_hash__
}
function handleAvailableHash(data){
mostRecentCompilationHash = data
}
function handleSuccess() {
var isHotUpdate     = !isFirstCompilation
isFirstCompilation  = false
if (isHotUpdate) { handleUpdates() }
}
function handleUpdates() {
if (!isUpdateAvailable()) return
console.log('%c Reloading Extension', 'color: #FF00FF')
chrome.runtime.reload()
}

当您准备好使用它时(仅在开发期间),您可以简单地将其添加到您的背景.js入口点

module.exports = {
entry: {
background: [
path.resolve(__dirname, 'reloader.js'), 
path.resolve(__dirname, 'background.js')
]
}
}




对于最初要求的实际挂钩到事件发射器,您只需从 webpack/hot/emitter 要求它,因为该文件导出了所使用的事件发射器的实例。
if(module.hot) {
var lastHash
var upToDate = function upToDate() {
return lastHash.indexOf(__webpack_hash__) >= 0
}
var clientEmitter = require('webpack/hot/emitter')
clientEmitter.on('webpackHotUpdate', function(currentHash) {
lastHash = currentHash
if(upToDate()) return
console.log('%c Reloading Extension', 'color: #FF00FF')
chrome.runtime.reload()
})
}

这只是直接从源代码精简的版本:

https://github.com/webpack/webpack/blob/master/hot/dev-server.js

我已经微调了crx-hotreload包的核心逻辑,并提出了一个与构建工具无关的解决方案(这意味着它可以与 Webpack 一起使用,也可以与其他任何东西一起使用)。

它向扩展名询问其目录(通过chrome.runtime.getPackageDirectoryEntry),然后监视该目录的文件更改。在该目录中添加/删除/更改文件后,它将调用chrome.runtime.reload().

如果您还需要重新加载活动选项卡(在开发内容脚本时),则应运行 tabs.query,从结果中获取第一个(活动)选项卡并对其调用 reload。

整个逻辑是~35行代码:

/* global chrome */
const filesInDirectory = dir => new Promise(resolve =>
dir.createReader().readEntries(entries =>
Promise.all(entries.filter(e => e.name[0] !== '.').map(e =>
e.isDirectory
? filesInDirectory(e)
: new Promise(resolve => e.file(resolve))
))
.then(files => [].concat(...files))
.then(resolve)
)
)
const timestampForFilesInDirectory = dir => filesInDirectory(dir)
.then(files => files.map(f => f.name + f.lastModifiedDate).join())
const watchChanges = (dir, lastTimestamp) => {
timestampForFilesInDirectory(dir).then(timestamp => {
if (!lastTimestamp || (lastTimestamp === timestamp)) {
setTimeout(() => watchChanges(dir, timestamp), 1000)
} else {
console.log('%c 🚀 Reloading Extension', 'color: #FF00FF')
chrome.runtime.reload()
}
})
}
// Init if in dev environment
chrome.management.getSelf(self => {
if (self.installType === 'development' &&
'getPackageDirectoryEntry' in chrome.runtime
) {
console.log('%c 📦 Watching for file changes', 'color: #FF00FF')
chrome.runtime.getPackageDirectoryEntry(dir => watchChanges(dir))
}
})

应将此脚本添加到manifest.json文件的后台脚本条目中:

"background": ["reloader.js", "background.js"]

以及自述文件中带有简单解释的要点:https://gist.github.com/andreasvirkus/c9f91ddb201fc78042bf7d814af47121

对于希望其内容脚本具有HMR(不仅仅是热重载)功能的人。我写了一篇文章 Chrome 扩展程序的热门模块替换和 WebPack 插件 crx-load-script-webpack-plugin for it.

WebPack 插件覆盖了 WebPack 的加载脚本机制。具有适当的输出路径、端口和清单权限配置。HMR 将工作。

// webpack.config.js

const CrxLoadScriptWebpackPlugin = require('@cooby/crx-load-script-webpack-plugin');
module.exports = {
mode: 'development',
devServer: {
/**
* We need devServer write files to disk,
* But don't want it reload whole page because of the output file changes.
*/
static: { watch: false },
/**
* Set WebSocket url to dev-server, instead of the default `${publicPath}/ws`
*/
client: {
webSocketURL: 'ws://localhost:8080/ws',
},
/**
* The host of the page of your script extension runs on.
* You'll see `[webpack-dev-server] Invalid Host/Origin header` if this is not set.
*/ 
allowedHosts: ['web.whatsapp.com'],
devMiddleware: {
/**
* Write file to output folder /build, so we can execute it later.
*/
writeToDisk: true,
},
},
plugins: [
/** 
* Enable HMR related plugins. 
*/
new webpack.HotModuleReplacementPlugin(),
new CrxLoadScriptWebpackPlugin(),
new ReactRefreshWebpackPlugin({
overlay: false,
}),
],
}

manifest.json

{
"manifest_version": 3,
"permissions": [
"scripting"
],
"web_accessible_resources": [
{
"resources": [
"*.hot-update.json",
],
"matches": [
"https://web.whatsapp.com/*"
]
}
],
"host_permissions": [
"https://web.whatsapp.com/*"
]
}

相关内容

  • 没有找到相关文章

最新更新