@mantou/gem 1.6.17 → 1.6.19

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.
@@ -68,12 +68,14 @@ type GetDepFun<T> = () => T;
68
68
  type EffectCallback<T> = (depValues: T, oldDepValues?: T) => any;
69
69
  type EffectItem<T> = {
70
70
  callback: EffectCallback<T>;
71
- initialized: boolean;
71
+ initialized?: boolean;
72
+ inConstructor?: boolean;
72
73
  values?: T;
73
74
  getDep?: GetDepFun<T>;
74
75
  preCallback?: () => void;
75
76
  };
76
77
 
78
+ const constructorSymbol = Symbol('constructor');
77
79
  const initSymbol = Symbol('init');
78
80
  const updateSymbol = Symbol('update');
79
81
 
@@ -106,19 +108,22 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
106
108
  #renderRoot: HTMLElement | ShadowRoot;
107
109
  #internals?: ElementInternals;
108
110
  #isAppendReason?: boolean;
111
+ // 和 isConnected 有区别
112
+ #isMounted?: boolean;
109
113
  #isAsync?: boolean;
110
114
  #effectList?: EffectItem<any>[];
111
- #momeList?: EffectItem<any>[];
115
+ #memoList?: EffectItem<any>[];
112
116
  #unmountCallback?: any;
113
117
 
114
118
  constructor({ isAsync, isLight, delegatesFocus }: GemElementOptions = {}) {
115
119
  super();
120
+ (this as any)[constructorSymbol] = true;
116
121
 
117
122
  // 外部不可见,但允许类外面使用
118
123
  addMicrotask(() => ((this as any)[initSymbol] = false));
119
124
  (this as any)[initSymbol] = true;
120
125
  (this as any)[updateSymbol] = () => {
121
- if (this.isConnected) {
126
+ if (this.#isMounted) {
122
127
  addMicrotask(this.#update);
123
128
  }
124
129
  };
@@ -236,7 +241,7 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
236
241
  };
237
242
 
238
243
  #execMemo = () => {
239
- this.#exec(this.#momeList);
244
+ this.#exec(this.#memoList);
240
245
  };
241
246
 
242
247
  /**
@@ -252,19 +257,17 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
252
257
  * }
253
258
  * ```
254
259
  * */
255
- effect = <T = any[] | undefined>(
256
- callback: EffectCallback<T>,
257
- getDep?: T extends any[] ? () => [...T] : undefined,
258
- ) => {
260
+ effect = <T = any[] | undefined>(callback: EffectCallback<T>, getDep?: T extends any[] ? () => T : undefined) => {
259
261
  if (!this.#effectList) this.#effectList = [];
260
262
  const effectItem: EffectItem<T> = {
261
263
  callback,
262
264
  getDep,
263
265
  values: undefined,
264
- initialized: !!this.isConnected,
266
+ initialized: this.#isMounted,
267
+ inConstructor: (this as any)[constructorSymbol],
265
268
  };
266
269
  // 以挂载时立即执行副作用,未挂载时等挂载后执行
267
- if (this.isConnected) {
270
+ if (this.#isMounted) {
268
271
  effectItem.values = getDep?.() as T;
269
272
  effectItem.preCallback = callback(effectItem.values);
270
273
  }
@@ -285,14 +288,13 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
285
288
  * }
286
289
  * ```
287
290
  * */
288
- memo = <T = any[] | undefined>(callback: EffectCallback<T>, getDep?: T extends any[] ? () => [...T] : undefined) => {
289
- if (!this.#momeList) this.#momeList = [];
290
- this.#momeList.push({
291
+ memo = <T = any[] | undefined>(callback: EffectCallback<T>, getDep?: T extends any[] ? () => T : undefined) => {
292
+ if (!this.#memoList) this.#memoList = [];
293
+ this.#memoList.push({
291
294
  callback,
292
295
  getDep,
293
296
  values: [Symbol()],
294
- // 是否是 `willMount` 中定义
295
- initialized: this.isConnected,
297
+ inConstructor: (this as any)[constructorSymbol],
296
298
  });
297
299
  };
298
300
 
@@ -349,7 +351,7 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
349
351
  };
350
352
 
351
353
  #updateCallback = () => {
352
- if (this.isConnected && this.#shouldUpdate()) {
354
+ if (this.#isMounted && this.#shouldUpdate()) {
353
355
  const temp = this.#render();
354
356
  temp !== undefined && render(temp, this.#renderRoot);
355
357
  addMicrotask(this.#updated);
@@ -392,12 +394,14 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
392
394
  * use `effect`
393
395
  */
394
396
  attributeChangedCallback() {
395
- if (this.isConnected) {
397
+ if (this.#isMounted) {
396
398
  addMicrotask(this.#update);
397
399
  }
398
400
  }
399
401
 
400
402
  #connectedCallback = () => {
403
+ // 似乎这是最早的判断不在 `constructor` 中的地方
404
+ (this as any)[constructorSymbol] = false;
401
405
  if (this.#isAppendReason) {
402
406
  this.#isAppendReason = false;
403
407
  return;
@@ -409,6 +413,7 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
409
413
  });
410
414
  const temp = this.#render();
411
415
  temp !== undefined && render(temp, this.#renderRoot);
416
+ this.#isMounted = true;
412
417
  this.#unmountCallback = this.mounted?.();
413
418
  this.#initEffect();
414
419
  if (rootElement && (this.getRootNode() as ShadowRoot).host?.tagName !== rootElement.toUpperCase()) {
@@ -465,6 +470,7 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
465
470
  this.#isAppendReason = true;
466
471
  return;
467
472
  }
473
+ this.#isMounted = false;
468
474
  const { observedStores } = this.constructor as typeof GemElement;
469
475
  observedStores?.forEach((store) => {
470
476
  disconnect(store, this.#update);
@@ -474,7 +480,8 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
474
480
  this.#effectList?.forEach((effectItem) => {
475
481
  execCallback(effectItem.preCallback);
476
482
  });
477
- this.#momeList = this.#momeList?.filter(({ initialized }) => !initialized);
483
+ this.#effectList = this.#effectList?.filter(({ inConstructor }) => inConstructor);
484
+ this.#memoList = this.#memoList?.filter(({ inConstructor }) => inConstructor);
478
485
  }
479
486
  }
480
487
 
@@ -50,13 +50,13 @@ function validData(data: any) {
50
50
  if ($hasShouldCloseHandle) throw new GemError('`$hasShouldCloseHandle` is not allowed');
51
51
  }
52
52
 
53
- function getUrlbarPath(internalPath: string) {
53
+ function getUrlBarPath(internalPath: string) {
54
54
  return gemHistory.basePath ? gemHistory.basePath + internalPath : internalPath;
55
55
  }
56
56
 
57
- function getInternalPath(urlbarPath: string) {
58
- if (urlbarPath === gemHistory.basePath) return '/';
59
- return urlbarPath.replace(new RegExp(`^${gemHistory.basePath}/`), '/');
57
+ function getInternalPath(urlBarPath: string) {
58
+ if (urlBarPath === gemHistory.basePath) return '/';
59
+ return urlBarPath.replace(new RegExp(`^${gemHistory.basePath}/`), '/');
60
60
  }
61
61
 
62
62
  function normalizeParams(params: UpdateHistoryParams): HistoryParams {
@@ -88,7 +88,7 @@ function updateHistory(p: UpdateHistoryParams, native: typeof nativeHistory.push
88
88
  };
89
89
  paramsMap.set(state.$key, params);
90
90
  updateStore(cleanObject(store), state);
91
- const url = getUrlbarPath(path) + new QueryString(query) + hash;
91
+ const url = getUrlBarPath(path) + new QueryString(query) + hash;
92
92
  const prevHash = decodeURIComponent(location.hash);
93
93
  native(state, title, url);
94
94
  if (prevHash !== hash) window.dispatchEvent(new CustomEvent('hashchange'));
@@ -105,7 +105,7 @@ function updateHistoryByNative(data: any, title: string, originUrl: string, nati
105
105
  const params = normalizeParams({ path: pathname, query: new QueryString(search), hash, title, data });
106
106
  paramsMap.set(state.$key, params);
107
107
  updateStore(cleanObject(store), state);
108
- const url = getUrlbarPath(pathname) + params.query + hash;
108
+ const url = getUrlBarPath(pathname) + params.query + hash;
109
109
  const prevHash = location.hash;
110
110
  native(state, title, url);
111
111
  // `location.hash` 和 `hash` 都已经进行了 url 编码,可以直接进行相等判断
@@ -127,13 +127,13 @@ class GemHistory {
127
127
  // 应用初始化的时候设置
128
128
  updateStore(gemBasePathStore, { basePath });
129
129
  // paramsMap 更新后 ui 才会更新
130
- Object.assign(paramsMap.get(store.$key), { path: getInternalPath(location.pathname) });
130
+ Object.assign(paramsMap.get(store.$key)!, { path: getInternalPath(location.pathname) });
131
131
  }
132
132
  getParams() {
133
133
  return paramsMap.get(store.$key)!;
134
134
  }
135
135
  updateParams(params: UpdateHistoryParams) {
136
- Object.assign(paramsMap.get(store.$key), params);
136
+ Object.assign(paramsMap.get(store.$key)!, params);
137
137
  updateStore(store, {});
138
138
  }
139
139
  push(params: UpdateHistoryParams) {
@@ -257,8 +257,8 @@ if (!window._GEMHISTORY) {
257
257
 
258
258
  // url 变化前 historyItem
259
259
  const prevState = store;
260
-
261
- if (parseFloat(newState.$key) > parseFloat(prevState.$key) && newState.$hasOpenHandle) {
260
+ const isForward = parseFloat(newState.$key) > parseFloat(prevState.$key);
261
+ if (isForward && newState.$hasOpenHandle) {
262
262
  // 返回键关闭的 modal 能前进键重新打开
263
263
  // 刷新后不能工作:刷新后 historyItem 中只有 url
264
264
  // 能在子 app 中工作
package/src/lib/utils.ts CHANGED
@@ -272,7 +272,7 @@ export function createCSSSheet<T extends StyledKeyValuePair>(rules: T | string,
272
272
  style = rules;
273
273
  } else {
274
274
  Object.keys(rules).forEach((key: keyof T) => {
275
- sheet[key] = `${key}-${randomStr()}`;
275
+ sheet[key] = `${key as string}-${randomStr()}`;
276
276
  style += rules[key].styledContent.replace(/&/g, sheet[key]);
277
277
  });
278
278
  }
@@ -1 +1 @@
1
- export const version = '1.6.17';
1
+ export const version = '1.6.19';
@@ -2,7 +2,7 @@ import { fixture, expect, nextFrame } from '@open-wc/testing';
2
2
 
3
3
  import { GemElement, html } from '../../lib/element';
4
4
  import { createStore, updateStore } from '../../lib/store';
5
- import { attribute, property, customElement, emitter, adoptedStyle } from '../../lib/decorators';
5
+ import { attribute, property, customElement, emitter, adoptedStyle, refobject, RefObject } from '../../lib/decorators';
6
6
  import { createCSSSheet, css } from '../../lib/utils';
7
7
 
8
8
  const store = createStore({
@@ -66,14 +66,22 @@ describe('没有 Shadow DOM 的 gem 元素', () => {
66
66
  });
67
67
  });
68
68
 
69
- @customElement('lifecycle-gem-demo')
70
69
  class LifecycleGemElement extends GemElement {
71
70
  @attribute appTitle = 'default';
72
71
  @attribute appTitle2 = 'default2';
72
+ @refobject divRef: RefObject<HTMLElement>;
73
+ refInConstructor?: HTMLElement;
74
+
73
75
  constructor(appTitle: string, appTitle2: string) {
74
76
  super();
75
77
  this.appTitle = appTitle;
76
78
  this.appTitle2 = appTitle2;
79
+ this.effect(
80
+ () => {
81
+ this.refInConstructor = this.divRef.element;
82
+ },
83
+ () => [],
84
+ );
77
85
  }
78
86
  renderCount = 0;
79
87
  mountedCount = 0;
@@ -92,12 +100,15 @@ class LifecycleGemElement extends GemElement {
92
100
 
93
101
  render() {
94
102
  this.renderCount++;
95
- return html``;
103
+ return html`<div ref=${this.divRef.ref}></div>`;
96
104
  }
97
105
  }
98
106
  describe('gem element 生命周期', () => {
99
107
  it('mounted/unmounted', async () => {
100
108
  const el: LifecycleGemElement = await fixture(html`<lifecycle-gem-demo></lifecycle-gem-demo>`);
109
+ // test #isMounted
110
+ customElements.define('lifecycle-gem-demo', LifecycleGemElement);
111
+ expect(!!el.refInConstructor).to.equal(true);
101
112
  expect(el.renderCount).to.equal(1);
102
113
  el.remove();
103
114
  expect(el.renderCount).to.equal(0);
@@ -211,19 +222,19 @@ class MemoGemDemo extends GemElement {
211
222
  constructor() {
212
223
  super();
213
224
  this.memo(
214
- () => this.memoCount++,
225
+ () => (this.memoCount += 1),
215
226
  () => [this.attr, this.prop],
216
227
  );
217
228
  }
218
229
  willMount() {
219
230
  this.memo(
220
231
  (_arr) => {
221
- this.memoCount++;
232
+ this.memoCount += 2;
222
233
  },
223
234
  () => [],
224
235
  );
225
236
  this.memo(() => {
226
- this.memoCount++;
237
+ this.memoCount += 4;
227
238
  });
228
239
  }
229
240
  }
@@ -231,23 +242,23 @@ customElements.define('memo-gem-demo', MemoGemDemo);
231
242
  describe('gem element Memo', () => {
232
243
  it('依赖当前值', async () => {
233
244
  const el: MemoGemDemo = await fixture(html`<memo-gem-demo></memo-gem-demo>`);
234
- expect(el.memoCount).to.equal(3);
245
+ expect(el.memoCount).to.equal(7);
235
246
  el.attr = 'b';
236
247
  el.prop = {};
237
248
  await nextFrame();
238
- expect(el.memoCount).to.equal(5);
249
+ expect(el.memoCount).to.equal(12);
239
250
 
240
251
  el.update();
241
252
  await Promise.resolve();
242
- expect(el.memoCount).to.equal(6);
253
+ expect(el.memoCount).to.equal(16);
243
254
 
244
255
  document.body.append(el);
245
- expect(el.memoCount).to.equal(6);
256
+ expect(el.memoCount).to.equal(16);
246
257
 
247
258
  el.remove();
248
259
  await Promise.resolve();
249
260
  document.body.append(el);
250
- expect(el.memoCount).to.equal(8);
261
+ expect(el.memoCount).to.equal(22);
251
262
  });
252
263
  });
253
264
 
@@ -269,6 +280,7 @@ describe('gem element 继承', () => {
269
280
  expect(InheritGem.observedAttributes).to.eql(['app-title', 'app-title', 'app-title2']);
270
281
  });
271
282
  it('attr/prop/emitter 继承', async () => {
283
+ const name = window.name;
272
284
  const el: InheritGem = await fixture(html`<inherit-gem></inherit-gem>`);
273
285
  expect(el.appTitle).to.equal('1');
274
286
  expect(el.appData.a).to.equal(1);
@@ -280,7 +292,7 @@ describe('gem element 继承', () => {
280
292
  window.name += '2';
281
293
  });
282
294
  el.sayHi();
283
- expect(window.name).to.equal('21');
295
+ expect(window.name).to.equal(name + '21');
284
296
  });
285
297
  });
286
298
  @customElement('render-empty')
@@ -10,7 +10,7 @@ describe('history 测试', () => {
10
10
  await aTimeout(10);
11
11
  expect(window.location.hash).to.equal('#a');
12
12
  history.back();
13
- await aTimeout(10);
13
+ await aTimeout(600);
14
14
  expect(window.location.hash).to.equal(hash);
15
15
  history.push({ path: '/a', hash: '#b' });
16
16
  await aTimeout(10);