原文连接:github.com/lzwaiwai/bl…javascript
在编写 chrome 插件开发模板 的时候,遇到了 webpack 的 热更新不彻底生效 的问题。html
不生效情景:vue
当 chrome 插件的 manifest.json 中的 background, content_scripts 被配置为 js 的形式时(以下),这些脚本必须在文件目录中存在(注意:background 的 scripts 和 content_scripts 的 js 两个属性是不能使用远程连接的)。java
{
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["content.js"],
"all_frames": true
}
],
}
复制代码
生效情景: 当 chrome 插件的 manifest.json 中的 background, popup 被配置为 html(js 在 html 中被以 远程连接 的形式引入) 的形式时(以下),react
{
"browser_action": {
"default_popup": "pages/popup.html"
},
"background": {
"persistent": false,
"page": "pages/background.html"
},
}
复制代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="http://127.0.0.1:3000/js/background.js"></script>
</head>
</html>
复制代码
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"></head>
<body>
<div id="chrome-popup-root"></div>
<script src="http://127.0.0.1:3000/js/popup"></script>
</body>
</html>
复制代码
分析缘由:webpack
在配置 webpack 的热更新的时候,咱们都会在 entry 配置以下配置: webpack-hot-middleware/client?path=http://127.0.0.1:3000/__webpack_hmr&noInfo=false&reload=true&quiet=false
git
而后开发过程当中,咱们会在 network 中发现不少 http://127.0.0.1:3000/__webpack_hmr/xxx 这样的网络请求,这些请求就是用来获取对应脚本的修改部分,并触发其发生热更新,最后局部进行从新渲染。因此这样的热更新只能支持远程连接配置的形式。这就是 chrome 插件开发时的热更新不彻底生效的缘由。github
上面咱们提到 manifest.json 中的 background 的 scripts 和 content_scripts 的 js 两个属性是不能使用远程连接的,因此 webpack 的热更新并不适合 js 脚本配置的 chrome 插件开发。web
而开发过 chrome 插件的同窗都知道,对于使用本地脚本配置的方式,在修改了脚本后,须要去应用扩展后台手动更新插件,不然插件不会更新。(这里推荐另外一个插件,能够重载其余当前正在开发的 chrome 插件:github.com/arikw/chrom…)chrome
因此,这里就衍生出了开发 chrome 插件过程当中修改代码后可否自动重载的问题。
咱们逆向思考:要重载插件,须要得到代码的修改时机;要得到代码的修改时机,须要了解 webpack 在每次修改编译后如何通知到客户端。
因而继续衍生出如下几个问题:
咱们一个一个来解决
查阅 webpack 文档咱们能够找到以下方法:
const compiler = webpack(webpackConfig)
compiler.hooks.done.tap('name', () => {
// todo
})
复制代码
首先请详细看一下这几篇文章和源码:《Server-Sent Events》、《Webpack 热更新实现原理分析》、《EventSource client for Node.js and Browser (polyfill)》
SSE 就是一种由服务端直接将信息推送给客户端的单向通道,相比 websocket 则会更加轻量。
客户端:
if ('EventSource' in window) {
const source = new EventSource(`http://localhost:3000/sse`)
source.addEventListener('open', (event) => {
console.log('Auto Reload Listen: ', event);
}, false);
source.addEventListener('compiled', (event) => { // 自定义事件
console.log('compiled: ', event);
// todo
}, false);
source.addEventListener('error', (event) => {
console.log('error: ', event);
}, false);
}
复制代码
服务端:
const SseStream = require('ssestream');
const compiler = webpack(webpackConfig);
app.use('/sse', webpackChromeExtensionsReloadMiddleware(compiler));
function webpackChromeExtensionsReloadMiddleware(compiler, opts = {}) {
opts.heartbeat = opts.heartbeat || 5 * 1000;
const middleware = function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
const sseStream = new SseStream(req) // 构造 sse 的请求
sseStream.pipe(res)
if (compiler.hooks) {
compiler.hooks.done.tap('webpack-chrome-extensions-reload-middleware', () => {
sseStream.write({ // 请求数据
event: 'compiled',
data: 'compiled'
})
})
}
res.on('close', () => {
console.log('close connection')
sseStream.unpipe(res)
})
next();
};
return middleware;
}
复制代码
在 2 中客户端的 todo 中,咱们在这里的回调里就能够实时监听到代码的改动,这里咱们只须要执行以下代码便可。
chrome.runtime.reload();
复制代码
以上机制已被我以前编写的 vue 和 react 版本的 chrome 插件开发模板 中用到,欢迎围观~