@taicode/common-web 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/output/cache-api/cache-api.d.ts +13 -0
- package/output/cache-api/cache-api.d.ts.map +1 -0
- package/output/cache-api/cache-api.js +114 -0
- package/output/cache-api/cache-api.test.d.ts +2 -0
- package/output/cache-api/cache-api.test.d.ts.map +1 -0
- package/output/cache-api/cache-api.test.js +348 -0
- package/output/cache-api/index.d.ts +2 -0
- package/output/cache-api/index.d.ts.map +1 -0
- package/output/cache-api/index.js +1 -0
- package/output/helpers/cache-api/cache-api.d.ts +13 -0
- package/output/helpers/cache-api/cache-api.d.ts.map +1 -0
- package/output/helpers/cache-api/cache-api.js +114 -0
- package/output/helpers/cache-api/cache-api.test.d.ts +2 -0
- package/output/helpers/cache-api/cache-api.test.d.ts.map +1 -0
- package/output/helpers/cache-api/cache-api.test.js +348 -0
- package/output/helpers/cache-api/index.d.ts +2 -0
- package/output/helpers/cache-api/index.d.ts.map +1 -0
- package/output/helpers/cache-api/index.js +1 -0
- package/output/helpers/side-cache/side-cache.d.ts +5 -2
- package/output/helpers/side-cache/side-cache.d.ts.map +1 -1
- package/output/helpers/side-cache/side-cache.js +41 -10
- package/output/helpers/side-cache/side-cache.test.js +166 -76
- package/output/service/index.d.ts +2 -0
- package/output/service/index.d.ts.map +1 -0
- package/output/service/index.js +1 -0
- package/output/service/service.d.ts +114 -0
- package/output/service/service.d.ts.map +1 -0
- package/output/service/service.js +189 -0
- package/output/service/service.test.d.ts +2 -0
- package/output/service/service.test.d.ts.map +1 -0
- package/output/service/service.test.jsx +367 -0
- package/output/side-cache/index.d.ts +2 -0
- package/output/side-cache/index.d.ts.map +1 -0
- package/output/side-cache/index.js +1 -0
- package/output/side-cache/side-cache.d.ts +10 -0
- package/output/side-cache/side-cache.d.ts.map +1 -0
- package/output/side-cache/side-cache.js +137 -0
- package/output/side-cache/side-cache.test.d.ts +2 -0
- package/output/side-cache/side-cache.test.d.ts.map +1 -0
- package/output/side-cache/side-cache.test.js +179 -0
- package/output/use-observer/index.d.ts +2 -0
- package/output/use-observer/index.d.ts.map +1 -0
- package/output/use-observer/index.js +1 -0
- package/output/use-observer/use-observer.d.ts +3 -0
- package/output/use-observer/use-observer.d.ts.map +1 -0
- package/output/use-observer/use-observer.js +16 -0
- package/output/use-observer/use-observer.test.d.ts +2 -0
- package/output/use-observer/use-observer.test.d.ts.map +1 -0
- package/output/use-observer/use-observer.test.jsx +134 -0
- package/package.json +2 -1
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
35
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
36
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
37
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
38
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
39
|
+
};
|
|
40
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
41
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
42
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
43
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
44
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
45
|
+
};
|
|
46
|
+
import React, { act } from 'react';
|
|
47
|
+
import { Service } from '@taicode/common-base';
|
|
48
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
49
|
+
import { render, renderHook, waitFor } from '@testing-library/react';
|
|
50
|
+
import { observable, makeObservable, runInAction, action } from 'mobx';
|
|
51
|
+
import { useService, ServiceProvider } from './service';
|
|
52
|
+
// 创建测试用的服务类
|
|
53
|
+
let TestService = (() => {
|
|
54
|
+
var _a, _TestService_count_accessor_storage, _TestService_users_accessor_storage, _TestService_inited_accessor_storage;
|
|
55
|
+
let _classSuper = Service;
|
|
56
|
+
let _instanceExtraInitializers = [];
|
|
57
|
+
let _count_decorators;
|
|
58
|
+
let _count_initializers = [];
|
|
59
|
+
let _count_extraInitializers = [];
|
|
60
|
+
let _users_decorators;
|
|
61
|
+
let _users_initializers = [];
|
|
62
|
+
let _users_extraInitializers = [];
|
|
63
|
+
let _inited_decorators;
|
|
64
|
+
let _inited_initializers = [];
|
|
65
|
+
let _inited_extraInitializers = [];
|
|
66
|
+
let _increment_decorators;
|
|
67
|
+
let _addUser_decorators;
|
|
68
|
+
return _a = class TestService extends _classSuper {
|
|
69
|
+
get count() { return __classPrivateFieldGet(this, _TestService_count_accessor_storage, "f"); }
|
|
70
|
+
set count(value) { __classPrivateFieldSet(this, _TestService_count_accessor_storage, value, "f"); }
|
|
71
|
+
get users() { return __classPrivateFieldGet(this, _TestService_users_accessor_storage, "f"); }
|
|
72
|
+
set users(value) { __classPrivateFieldSet(this, _TestService_users_accessor_storage, value, "f"); }
|
|
73
|
+
get inited() { return __classPrivateFieldGet(this, _TestService_inited_accessor_storage, "f"); }
|
|
74
|
+
set inited(value) { __classPrivateFieldSet(this, _TestService_inited_accessor_storage, value, "f"); }
|
|
75
|
+
constructor() {
|
|
76
|
+
super();
|
|
77
|
+
_TestService_count_accessor_storage.set(this, (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _count_initializers, 0)));
|
|
78
|
+
_TestService_users_accessor_storage.set(this, (__runInitializers(this, _count_extraInitializers), __runInitializers(this, _users_initializers, [])));
|
|
79
|
+
_TestService_inited_accessor_storage.set(this, (__runInitializers(this, _users_extraInitializers), __runInitializers(this, _inited_initializers, false)));
|
|
80
|
+
__runInitializers(this, _inited_extraInitializers);
|
|
81
|
+
makeObservable(this);
|
|
82
|
+
}
|
|
83
|
+
async init() {
|
|
84
|
+
runInAction(() => {
|
|
85
|
+
this.users = ['Alice', 'Bob'];
|
|
86
|
+
});
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
increment() {
|
|
90
|
+
this.count++;
|
|
91
|
+
}
|
|
92
|
+
addUser(name) {
|
|
93
|
+
this.users.push(name);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
_TestService_count_accessor_storage = new WeakMap(),
|
|
97
|
+
_TestService_users_accessor_storage = new WeakMap(),
|
|
98
|
+
_TestService_inited_accessor_storage = new WeakMap(),
|
|
99
|
+
(() => {
|
|
100
|
+
var _b;
|
|
101
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_b = _classSuper[Symbol.metadata]) !== null && _b !== void 0 ? _b : null) : void 0;
|
|
102
|
+
_count_decorators = [observable];
|
|
103
|
+
_users_decorators = [observable];
|
|
104
|
+
_inited_decorators = [observable];
|
|
105
|
+
_increment_decorators = [action];
|
|
106
|
+
_addUser_decorators = [action];
|
|
107
|
+
__esDecorate(_a, null, _count_decorators, { kind: "accessor", name: "count", static: false, private: false, access: { has: obj => "count" in obj, get: obj => obj.count, set: (obj, value) => { obj.count = value; } }, metadata: _metadata }, _count_initializers, _count_extraInitializers);
|
|
108
|
+
__esDecorate(_a, null, _users_decorators, { kind: "accessor", name: "users", static: false, private: false, access: { has: obj => "users" in obj, get: obj => obj.users, set: (obj, value) => { obj.users = value; } }, metadata: _metadata }, _users_initializers, _users_extraInitializers);
|
|
109
|
+
__esDecorate(_a, null, _inited_decorators, { kind: "accessor", name: "inited", static: false, private: false, access: { has: obj => "inited" in obj, get: obj => obj.inited, set: (obj, value) => { obj.inited = value; } }, metadata: _metadata }, _inited_initializers, _inited_extraInitializers);
|
|
110
|
+
__esDecorate(_a, null, _increment_decorators, { kind: "method", name: "increment", static: false, private: false, access: { has: obj => "increment" in obj, get: obj => obj.increment }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
111
|
+
__esDecorate(_a, null, _addUser_decorators, { kind: "method", name: "addUser", static: false, private: false, access: { has: obj => "addUser" in obj, get: obj => obj.addUser }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
112
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
113
|
+
})(),
|
|
114
|
+
_a;
|
|
115
|
+
})();
|
|
116
|
+
// 另一个测试服务类
|
|
117
|
+
let AnotherService = (() => {
|
|
118
|
+
var _a, _AnotherService_value_accessor_storage;
|
|
119
|
+
let _instanceExtraInitializers = [];
|
|
120
|
+
let _value_decorators;
|
|
121
|
+
let _value_initializers = [];
|
|
122
|
+
let _value_extraInitializers = [];
|
|
123
|
+
let _setValue_decorators;
|
|
124
|
+
return _a = class AnotherService {
|
|
125
|
+
get value() { return __classPrivateFieldGet(this, _AnotherService_value_accessor_storage, "f"); }
|
|
126
|
+
set value(value) { __classPrivateFieldSet(this, _AnotherService_value_accessor_storage, value, "f"); }
|
|
127
|
+
constructor() {
|
|
128
|
+
_AnotherService_value_accessor_storage.set(this, (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _value_initializers, 'initial')));
|
|
129
|
+
__runInitializers(this, _value_extraInitializers);
|
|
130
|
+
makeObservable(this);
|
|
131
|
+
}
|
|
132
|
+
setValue(newValue) {
|
|
133
|
+
this.value = newValue;
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
_AnotherService_value_accessor_storage = new WeakMap(),
|
|
137
|
+
(() => {
|
|
138
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
139
|
+
_value_decorators = [observable];
|
|
140
|
+
_setValue_decorators = [action];
|
|
141
|
+
__esDecorate(_a, null, _value_decorators, { kind: "accessor", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
|
|
142
|
+
__esDecorate(_a, null, _setValue_decorators, { kind: "method", name: "setValue", static: false, private: false, access: { has: obj => "setValue" in obj, get: obj => obj.setValue }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
143
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
144
|
+
})(),
|
|
145
|
+
_a;
|
|
146
|
+
})();
|
|
147
|
+
// 没有 init 方法的服务类
|
|
148
|
+
let SimpleService = (() => {
|
|
149
|
+
var _a, _SimpleService_data_accessor_storage;
|
|
150
|
+
let _data_decorators;
|
|
151
|
+
let _data_initializers = [];
|
|
152
|
+
let _data_extraInitializers = [];
|
|
153
|
+
return _a = class SimpleService {
|
|
154
|
+
get data() { return __classPrivateFieldGet(this, _SimpleService_data_accessor_storage, "f"); }
|
|
155
|
+
set data(value) { __classPrivateFieldSet(this, _SimpleService_data_accessor_storage, value, "f"); }
|
|
156
|
+
constructor() {
|
|
157
|
+
_SimpleService_data_accessor_storage.set(this, __runInitializers(this, _data_initializers, 'simple'));
|
|
158
|
+
__runInitializers(this, _data_extraInitializers);
|
|
159
|
+
makeObservable(this);
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
_SimpleService_data_accessor_storage = new WeakMap(),
|
|
163
|
+
(() => {
|
|
164
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
165
|
+
_data_decorators = [observable];
|
|
166
|
+
__esDecorate(_a, null, _data_decorators, { kind: "accessor", name: "data", static: false, private: false, access: { has: obj => "data" in obj, get: obj => obj.data, set: (obj, value) => { obj.data = value; } }, metadata: _metadata }, _data_initializers, _data_extraInitializers);
|
|
167
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
168
|
+
})(),
|
|
169
|
+
_a;
|
|
170
|
+
})();
|
|
171
|
+
// 创建测试用的 Provider 组件
|
|
172
|
+
function createTestProvider(services) {
|
|
173
|
+
return function TestProvider({ children }) {
|
|
174
|
+
return (<ServiceProvider services={services}>
|
|
175
|
+
{children}
|
|
176
|
+
</ServiceProvider>);
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
describe('service integration', () => {
|
|
180
|
+
beforeEach(() => {
|
|
181
|
+
vi.clearAllMocks();
|
|
182
|
+
});
|
|
183
|
+
describe('useService', () => {
|
|
184
|
+
it('应该能够获取服务实例并选择数据', () => {
|
|
185
|
+
const TestProvider = createTestProvider([TestService]);
|
|
186
|
+
const { result } = renderHook(() => useService(TestService, service => service.count), { wrapper: TestProvider });
|
|
187
|
+
expect(result.current).toBe(0);
|
|
188
|
+
});
|
|
189
|
+
it('应该在服务数据变化时触发重新渲染', async () => {
|
|
190
|
+
const TestProvider = createTestProvider([TestService]);
|
|
191
|
+
const { result } = renderHook(() => ({
|
|
192
|
+
count: useService(TestService, service => service.count),
|
|
193
|
+
service: useService(TestService, service => service)
|
|
194
|
+
}), { wrapper: TestProvider });
|
|
195
|
+
expect(result.current.count).toBe(0);
|
|
196
|
+
// 修改服务数据
|
|
197
|
+
act(() => {
|
|
198
|
+
result.current.service.increment();
|
|
199
|
+
});
|
|
200
|
+
await waitFor(() => {
|
|
201
|
+
expect(result.current.count).toBe(1);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
it('应该能够选择复杂数据结构', async () => {
|
|
205
|
+
const TestProvider = createTestProvider([TestService]);
|
|
206
|
+
const { result } = renderHook(() => ({
|
|
207
|
+
userCount: useService(TestService, service => service.users.length),
|
|
208
|
+
service: useService(TestService, service => service)
|
|
209
|
+
}), { wrapper: TestProvider });
|
|
210
|
+
// 等待初始化完成
|
|
211
|
+
await waitFor(() => {
|
|
212
|
+
expect(result.current.userCount).toBe(2);
|
|
213
|
+
});
|
|
214
|
+
// 添加新用户
|
|
215
|
+
act(() => {
|
|
216
|
+
result.current.service.addUser('Charlie');
|
|
217
|
+
});
|
|
218
|
+
await waitFor(() => {
|
|
219
|
+
expect(result.current.userCount).toBe(3);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
it('应该在没有 ServiceProvider 时抛出错误', () => {
|
|
223
|
+
expect(() => {
|
|
224
|
+
renderHook(() => useService(TestService, service => service.count));
|
|
225
|
+
}).toThrow('Must be a child of ServiceProvider.');
|
|
226
|
+
});
|
|
227
|
+
it('应该能够处理多个服务', () => {
|
|
228
|
+
const TestProvider = createTestProvider([TestService, AnotherService]);
|
|
229
|
+
const { result } = renderHook(() => ({
|
|
230
|
+
count: useService(TestService, service => service.count),
|
|
231
|
+
value: useService(AnotherService, service => service.value)
|
|
232
|
+
}), { wrapper: TestProvider });
|
|
233
|
+
expect(result.current.count).toBe(0);
|
|
234
|
+
expect(result.current.value).toBe('initial');
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
describe('ServiceProvider', () => {
|
|
238
|
+
it('应该能够渲染子组件', () => {
|
|
239
|
+
const { container } = render(<ServiceProvider services={[TestService]}>
|
|
240
|
+
<div data-testid="child">Test Child</div>
|
|
241
|
+
</ServiceProvider>);
|
|
242
|
+
expect(container.querySelector('[data-testid="child"]')).toBeTruthy();
|
|
243
|
+
});
|
|
244
|
+
it('应该能够嵌套使用', () => {
|
|
245
|
+
const ParentProvider = createTestProvider([TestService]);
|
|
246
|
+
const ChildProvider = createTestProvider([AnotherService]);
|
|
247
|
+
const { result } = renderHook(() => ({
|
|
248
|
+
count: useService(TestService, service => service.count),
|
|
249
|
+
value: useService(AnotherService, service => service.value)
|
|
250
|
+
}), {
|
|
251
|
+
wrapper: ({ children }) => (<ParentProvider>
|
|
252
|
+
<ChildProvider>
|
|
253
|
+
{children}
|
|
254
|
+
</ChildProvider>
|
|
255
|
+
</ParentProvider>)
|
|
256
|
+
});
|
|
257
|
+
expect(result.current.count).toBe(0);
|
|
258
|
+
expect(result.current.value).toBe('initial');
|
|
259
|
+
});
|
|
260
|
+
it('应该自动初始化实现了 Service 接口的服务', async () => {
|
|
261
|
+
const TestProvider = createTestProvider([TestService]);
|
|
262
|
+
const { result } = renderHook(() => useService(TestService, service => ({
|
|
263
|
+
users: service.users,
|
|
264
|
+
inited: service.inited
|
|
265
|
+
})), { wrapper: TestProvider });
|
|
266
|
+
// 等待初始化完成
|
|
267
|
+
await waitFor(() => {
|
|
268
|
+
expect(result.current.inited).toBe(true);
|
|
269
|
+
expect(result.current.users).toEqual(['Alice', 'Bob']);
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
it('应该能够处理没有 init 方法的服务', () => {
|
|
273
|
+
const TestProvider = createTestProvider([SimpleService]);
|
|
274
|
+
const { result } = renderHook(() => useService(SimpleService, service => service.data), { wrapper: TestProvider });
|
|
275
|
+
expect(result.current).toBe('simple');
|
|
276
|
+
});
|
|
277
|
+
it('应该能够处理空的服务数组', () => {
|
|
278
|
+
const { container } = render(<ServiceProvider services={[]}>
|
|
279
|
+
<div data-testid="child">Test Child</div>
|
|
280
|
+
</ServiceProvider>);
|
|
281
|
+
expect(container.querySelector('[data-testid="child"]')).toBeTruthy();
|
|
282
|
+
});
|
|
283
|
+
it('应该能够处理初始化失败的服务', async () => {
|
|
284
|
+
// 创建一个初始化会失败的服务
|
|
285
|
+
let FailingService = (() => {
|
|
286
|
+
var _a, _FailingService_inited_accessor_storage;
|
|
287
|
+
let _classSuper = Service;
|
|
288
|
+
let _inited_decorators;
|
|
289
|
+
let _inited_initializers = [];
|
|
290
|
+
let _inited_extraInitializers = [];
|
|
291
|
+
return _a = class FailingService extends _classSuper {
|
|
292
|
+
get inited() { return __classPrivateFieldGet(this, _FailingService_inited_accessor_storage, "f"); }
|
|
293
|
+
set inited(value) { __classPrivateFieldSet(this, _FailingService_inited_accessor_storage, value, "f"); }
|
|
294
|
+
constructor() {
|
|
295
|
+
super();
|
|
296
|
+
_FailingService_inited_accessor_storage.set(this, __runInitializers(this, _inited_initializers, false));
|
|
297
|
+
__runInitializers(this, _inited_extraInitializers);
|
|
298
|
+
makeObservable(this);
|
|
299
|
+
}
|
|
300
|
+
async init() {
|
|
301
|
+
throw new Error('Init failed');
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
_FailingService_inited_accessor_storage = new WeakMap(),
|
|
305
|
+
(() => {
|
|
306
|
+
var _b;
|
|
307
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_b = _classSuper[Symbol.metadata]) !== null && _b !== void 0 ? _b : null) : void 0;
|
|
308
|
+
_inited_decorators = [observable];
|
|
309
|
+
__esDecorate(_a, null, _inited_decorators, { kind: "accessor", name: "inited", static: false, private: false, access: { has: obj => "inited" in obj, get: obj => obj.inited, set: (obj, value) => { obj.inited = value; } }, metadata: _metadata }, _inited_initializers, _inited_extraInitializers);
|
|
310
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
311
|
+
})(),
|
|
312
|
+
_a;
|
|
313
|
+
})();
|
|
314
|
+
const TestProvider = createTestProvider([FailingService]);
|
|
315
|
+
const { result } = renderHook(() => useService(FailingService, service => service.inited), { wrapper: TestProvider });
|
|
316
|
+
// 等待初始化处理完成,即使失败也应该保持 inited 为 false
|
|
317
|
+
await waitFor(() => {
|
|
318
|
+
expect(result.current).toBe(false); // 因为初始化失败,inited 应该保持 false
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
describe('边界情况', () => {
|
|
323
|
+
it('useService 应该能够处理选择器返回 undefined 的情况', () => {
|
|
324
|
+
const TestProvider = createTestProvider([TestService]);
|
|
325
|
+
const { result } = renderHook(() => useService(TestService, () => undefined), { wrapper: TestProvider });
|
|
326
|
+
expect(result.current).toBeUndefined();
|
|
327
|
+
});
|
|
328
|
+
it('应该能够处理 selector 函数变化的情况', async () => {
|
|
329
|
+
const TestProvider = createTestProvider([TestService]);
|
|
330
|
+
let useCount = true;
|
|
331
|
+
const { result, rerender } = renderHook(() => ({
|
|
332
|
+
selected: useService(TestService, service => useCount ? service.count : service.users.length),
|
|
333
|
+
service: useService(TestService, service => service)
|
|
334
|
+
}), { wrapper: TestProvider });
|
|
335
|
+
expect(result.current.selected).toBe(0);
|
|
336
|
+
// 修改 selector
|
|
337
|
+
useCount = false;
|
|
338
|
+
rerender();
|
|
339
|
+
await waitFor(() => {
|
|
340
|
+
expect(result.current.selected).toBe(2); // users 初始化后包含 ['Alice', 'Bob'],长度为 2
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
describe('性能测试', () => {
|
|
345
|
+
it('useService 应该在 selector 返回相同值时不触发重渲染', async () => {
|
|
346
|
+
const TestProvider = createTestProvider([TestService]);
|
|
347
|
+
let renderCount = 0;
|
|
348
|
+
const { result } = renderHook(() => {
|
|
349
|
+
renderCount++;
|
|
350
|
+
return {
|
|
351
|
+
constValue: useService(TestService, () => 'constant'),
|
|
352
|
+
service: useService(TestService, service => service)
|
|
353
|
+
};
|
|
354
|
+
}, { wrapper: TestProvider });
|
|
355
|
+
const initialRenderCount = renderCount;
|
|
356
|
+
// 修改服务但选择器返回常量
|
|
357
|
+
act(() => {
|
|
358
|
+
result.current.service.increment();
|
|
359
|
+
});
|
|
360
|
+
// 等待一段时间确保没有额外渲染
|
|
361
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
362
|
+
// 由于选择器返回常量,理论上不应该有额外渲染
|
|
363
|
+
// 但 Mobx reaction 的具体行为可能会有差异
|
|
364
|
+
expect(renderCount).toBeGreaterThanOrEqual(initialRenderCount);
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/side-cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SideCache } from './side-cache';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class SideCache<T> {
|
|
2
|
+
private accessor emptyFlag;
|
|
3
|
+
private accessor currentKey;
|
|
4
|
+
private accessor cache;
|
|
5
|
+
get value(): T | undefined;
|
|
6
|
+
get empty(): boolean;
|
|
7
|
+
handle<F extends ((...args: unknown[]) => T)>(key: unknown, func: F): T;
|
|
8
|
+
handle<F extends ((...args: unknown[]) => Promise<T> | T)>(key: unknown, func: F): Promise<T> | T;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=side-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"side-cache.d.ts","sourceRoot":"","sources":["../../source/side-cache/side-cache.ts"],"names":[],"mappings":"AAWA,qBAAa,SAAS,CAAC,CAAC;IAEtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAG1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAG/C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoC;IAE1D,IACW,KAAK,IAAI,CAAC,GAAG,SAAS,CAIhC;IAED,IACW,KAAK,IAAI,OAAO,CAE1B;IAED,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC;IACvE,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;CAuClG"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
35
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
36
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
37
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
38
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
39
|
+
};
|
|
40
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
41
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
42
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
43
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
44
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
45
|
+
};
|
|
46
|
+
import { action, computed, observable, runInAction } from 'mobx';
|
|
47
|
+
function isPromiseLike(value) {
|
|
48
|
+
return value != null && typeof value.then === 'function';
|
|
49
|
+
}
|
|
50
|
+
let SideCache = (() => {
|
|
51
|
+
var _a, _SideCache_emptyFlag_accessor_storage, _SideCache_currentKey_accessor_storage, _SideCache_cache_accessor_storage;
|
|
52
|
+
var _b, _c;
|
|
53
|
+
let _instanceExtraInitializers = [];
|
|
54
|
+
let _emptyFlag_decorators;
|
|
55
|
+
let _emptyFlag_initializers = [];
|
|
56
|
+
let _emptyFlag_extraInitializers = [];
|
|
57
|
+
let _currentKey_decorators;
|
|
58
|
+
let _currentKey_initializers = [];
|
|
59
|
+
let _currentKey_extraInitializers = [];
|
|
60
|
+
let _cache_decorators;
|
|
61
|
+
let _cache_initializers = [];
|
|
62
|
+
let _cache_extraInitializers = [];
|
|
63
|
+
let _get_value_decorators;
|
|
64
|
+
let _get_empty_decorators;
|
|
65
|
+
let _handle_decorators;
|
|
66
|
+
return _a = class SideCache {
|
|
67
|
+
get emptyFlag() { return __classPrivateFieldGet(this, _SideCache_emptyFlag_accessor_storage, "f"); }
|
|
68
|
+
set emptyFlag(value) { __classPrivateFieldSet(this, _SideCache_emptyFlag_accessor_storage, value, "f"); }
|
|
69
|
+
get currentKey() { return __classPrivateFieldGet(this, _SideCache_currentKey_accessor_storage, "f"); }
|
|
70
|
+
set currentKey(value) { __classPrivateFieldSet(this, _SideCache_currentKey_accessor_storage, value, "f"); }
|
|
71
|
+
get cache() { return __classPrivateFieldGet(this, _SideCache_cache_accessor_storage, "f"); }
|
|
72
|
+
set cache(value) { __classPrivateFieldSet(this, _SideCache_cache_accessor_storage, value, "f"); }
|
|
73
|
+
get value() {
|
|
74
|
+
if (this.currentKey == null)
|
|
75
|
+
return undefined;
|
|
76
|
+
const cacheItem = this.cache[this.currentKey];
|
|
77
|
+
return cacheItem ? cacheItem.data : undefined;
|
|
78
|
+
}
|
|
79
|
+
get empty() {
|
|
80
|
+
return this.emptyFlag;
|
|
81
|
+
}
|
|
82
|
+
handle(key, func) {
|
|
83
|
+
var _b;
|
|
84
|
+
// 不需要跨设备、软件一致,所以直接用 JSON.stringify
|
|
85
|
+
// 但需要处理 JSON.stringify(undefined) 返回 undefined 的情况
|
|
86
|
+
const keyStringify = (_b = JSON.stringify(key)) !== null && _b !== void 0 ? _b : 'undefined';
|
|
87
|
+
// 这会让外面立马可以拿到之前缓存的值,如果存在的话
|
|
88
|
+
runInAction(() => this.currentKey = keyStringify);
|
|
89
|
+
const funcReturn = func();
|
|
90
|
+
if (isPromiseLike(funcReturn)) {
|
|
91
|
+
return funcReturn.then(result => runInAction(() => {
|
|
92
|
+
this.emptyFlag = false;
|
|
93
|
+
this.cache = Object.assign(Object.assign({}, this.cache), { [keyStringify]: {
|
|
94
|
+
data: result,
|
|
95
|
+
createTime: new Date().toISOString()
|
|
96
|
+
} });
|
|
97
|
+
return result;
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
runInAction(() => {
|
|
101
|
+
this.emptyFlag = false;
|
|
102
|
+
this.cache = Object.assign(Object.assign({}, this.cache), { [keyStringify]: {
|
|
103
|
+
data: funcReturn,
|
|
104
|
+
createTime: new Date().toISOString()
|
|
105
|
+
} });
|
|
106
|
+
});
|
|
107
|
+
return funcReturn;
|
|
108
|
+
}
|
|
109
|
+
constructor() {
|
|
110
|
+
_SideCache_emptyFlag_accessor_storage.set(this, (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _emptyFlag_initializers, true)));
|
|
111
|
+
_SideCache_currentKey_accessor_storage.set(this, (__runInitializers(this, _emptyFlag_extraInitializers), __runInitializers(this, _currentKey_initializers, void 0)));
|
|
112
|
+
_SideCache_cache_accessor_storage.set(this, (__runInitializers(this, _currentKey_extraInitializers), __runInitializers(this, _cache_initializers, {})));
|
|
113
|
+
__runInitializers(this, _cache_extraInitializers);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
_SideCache_emptyFlag_accessor_storage = new WeakMap(),
|
|
117
|
+
_SideCache_currentKey_accessor_storage = new WeakMap(),
|
|
118
|
+
_SideCache_cache_accessor_storage = new WeakMap(),
|
|
119
|
+
(() => {
|
|
120
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
121
|
+
_emptyFlag_decorators = [observable];
|
|
122
|
+
_currentKey_decorators = [observable];
|
|
123
|
+
_cache_decorators = [(_b = observable).ref.bind(_b)];
|
|
124
|
+
_get_value_decorators = [computed];
|
|
125
|
+
_get_empty_decorators = [computed];
|
|
126
|
+
_handle_decorators = [(_c = action).bound.bind(_c)];
|
|
127
|
+
__esDecorate(_a, null, _emptyFlag_decorators, { kind: "accessor", name: "emptyFlag", static: false, private: false, access: { has: obj => "emptyFlag" in obj, get: obj => obj.emptyFlag, set: (obj, value) => { obj.emptyFlag = value; } }, metadata: _metadata }, _emptyFlag_initializers, _emptyFlag_extraInitializers);
|
|
128
|
+
__esDecorate(_a, null, _currentKey_decorators, { kind: "accessor", name: "currentKey", static: false, private: false, access: { has: obj => "currentKey" in obj, get: obj => obj.currentKey, set: (obj, value) => { obj.currentKey = value; } }, metadata: _metadata }, _currentKey_initializers, _currentKey_extraInitializers);
|
|
129
|
+
__esDecorate(_a, null, _cache_decorators, { kind: "accessor", name: "cache", static: false, private: false, access: { has: obj => "cache" in obj, get: obj => obj.cache, set: (obj, value) => { obj.cache = value; } }, metadata: _metadata }, _cache_initializers, _cache_extraInitializers);
|
|
130
|
+
__esDecorate(_a, null, _get_value_decorators, { kind: "getter", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
131
|
+
__esDecorate(_a, null, _get_empty_decorators, { kind: "getter", name: "empty", static: false, private: false, access: { has: obj => "empty" in obj, get: obj => obj.empty }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
132
|
+
__esDecorate(_a, null, _handle_decorators, { kind: "method", name: "handle", static: false, private: false, access: { has: obj => "handle" in obj, get: obj => obj.handle }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
133
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
134
|
+
})(),
|
|
135
|
+
_a;
|
|
136
|
+
})();
|
|
137
|
+
export { SideCache };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"side-cache.test.d.ts","sourceRoot":"","sources":["../../source/side-cache/side-cache.test.ts"],"names":[],"mappings":""}
|