繁体   English   中英

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

[英]Listen for hot update events on the client side with webpack-dev-derver?

这有点边缘,但知道会有所帮助。

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

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

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

当使用带有HMR的webpack-dev-server时,后台页面保持同步就好了。 但是,内容脚本需要重新加载扩展以反映更改。 我可以通过从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()
}

当您准备好使用它时(仅在开发期间),您只需将其添加到background.js入口点即可

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




对于实际挂钩到事件发射器的原因,您可以从webpack / hot / emitter请求它,因为该文件导出了一个使用的EventEmitter实例。

 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.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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM