electron 开发经验之谈系列-在渲染、主进程间共享数据

本文最后更新于:2021年3月2日 下午

场景

有时候我们需要在主进程和渲染层共享某些数据,而 electron ipc 通信 显然更适合传递消息而不适合共享数据。

相关依赖

事实上,我们这个需求已经有人考虑过了,例如 electron-store 就已经实现了可以在渲染层、主进程均可使用。

那么,我们直接用 electron-store 有什么问题么?
是的,electron 仅能在 electron 中使用,所以在浏览器上会报错,而这对于开发环境而言是无法接受的,故而还需要检测环境使用不同的实现。

创建浏览器兼容层

使用策略模式实现不同环境下使用不同的存储

  • 浏览器使用 localStorage 实现
  • electron 中则使用 electron-store 实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import type Store from "electron-store";
import isElectron from "is-electron";
import { DeepReadonly } from "utility-types";

interface BaseStore {
get(key: string): string | null;

set(key: string, value: string): void;
}

class ElectronStoreImpl implements BaseStore {
private store: Store;

constructor() {
const Store = window.require("electron-store");
this.store = new Store();
}

set(key: string, value: string): void {
this.store.set(key, value);
}

get(key: string) {
return this.store.get(key) as string;
}
}

class LocalStorageImpl implements BaseStore {
get(key: string) {
return localStorage.getItem(key);
}

set(key: string, value: string) {
localStorage.setItem(key, value);
}
}

const symbol = Symbol("ElectronStore.store");

export class ElectronStore {
private readonly [symbol]: BaseStore = isElectron()
? new ElectronStoreImpl()
: new LocalStorageImpl();

static getInstance<T extends object>(
init?: Partial<T>
): Partial<{ [P in keyof T]: DeepReadonly<T[P]> }> {
const electronStore = new ElectronStore();

const proxy = new Proxy({} as any, {
get(target: any, p: string): any {
const text = electronStore[symbol].get(p);
try {
if (text === null || text === undefined) {
return null;
}
return JSON.parse(text);
} catch (e) {
return null;
}
},
set(target: any, p: string, value: any): boolean {
electronStore[symbol].set(
p,
value !== undefined && value !== null ? JSON.stringify(value) : value
);
return true;
},
});
if (init) {
Object.entries(init).forEach(([k, v]) => {
const text = electronStore[symbol].get(k);
if (text === null || text === undefined) {
proxy[k] = v;
}
});
}
return proxy;
}
}

你可能发现了,为了简化使用的 API,这里使用了代理模式拦截了对实例的访问,修改为使用 get/set 方法取值和设值。

使用

使用起来和一个普通的对象没什么区别,直接通过 . 访问或设置属性即可。

1
2
3
4
5
const userStore = ElectronStore.getInstance<{ name: string; age: number }>();
userStore.name = "liuli";
console.log(userStore.name === "liuli");
userStore.age = 1;
console.log(userStore.age === 1);

具体代码在 electron_example,由于是一个浅层封装,所以并未发布,但可以直接将模块复制到项目中使用。