原文連接: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 插件開發模板 中用到,歡迎圍觀~