缘由
这几天看到 Web Workers 时想到了一个点子,既然 Shared Web Workers 可以使同源策略下的不同标签页都能访问到同一个 Web Worker,那么在 Web Worker 中写一个状态管理,将状态传递出去,那不就是跨标签页的状态管理了。
于是我萌生了将 Redux 的思想整合进 Web Worker 的想法,可以想象一下在 Shared Web Worker 中有一份状态树,而页面中的数据都来源于 Web Worker,页面更改数据时会分发到 Web Worker 中,Web Worker 中更新完后又会将 state 更新通知给页面(不仅仅是进行操作的那个页面)。
简单实现
先创建一个 SharedWorker
const reduxWorker = new SharedWorker("store.js");
在合适的地方启动它
reduxWorker.port.start();
一个简单封装的往 Web Worker 发消息的函数
function sendMessage(message) {
reduxWorker.port.postMessage(message);
}
接下来首先需要创建 store,往 Web Worker 发送
// 一个初始的 store
const store = {};
function createStore(store) {
sendMessage({
store,
});
}
在 SharedWorker 也就是我的 store.js 中,需要能够处理创建 store 还有分发 action
const store = {};
// 一个能够更新 user 数据的简单 reducer
const reducer = (state, { type, user }) => {
switch (type) {
case "UPDATE_ITEM":
return {
...state,
user,
};
default:
return state;
}
};
onconnect = function (e) {
const port = e.ports[0];
portList.push(port);
port.start();
port.postMessage("connected.");
port.onmessage = function (e) {
const { store: newStore, action } = e.data;
if (newStore) {
Object.assign(store, newStore);
}
if (action) {
store.state = reducer(store.state, action || {});
// 向每个标签页发送状态
portList.map(item => {
item.postMessage(store.state);
});
}
};
};
这样子传递 action 更改数据也很简单
// 更改 User 信息
sendMessage({
action: { type: "UPDATE_ITEM", user: { name: "小明", age: 118 } },
});
// 拾取状态,不作更改
sendMessage({ action: {} });
而页面这边可以在 onmessage 中接收消息拿到状态
reduxWorker.port.onmessage = function (e) {
const { state } = e.data;
// ...
};
改进优化
- 可以写一个消息件来处理消息的传递
- 在页面中自己维护一个完整的 state,消息件的作用是将 action 派发给它