diff --git a/packages/gem-examples/src/life-cycle/children.ts b/packages/gem-examples/src/life-cycle/children.ts
index eef4a995..ae31569a 100644
--- a/packages/gem-examples/src/life-cycle/children.ts
+++ b/packages/gem-examples/src/life-cycle/children.ts
@@ -17,6 +17,27 @@ import {
export type Message = number[];
+@customElement('app-descendant')
+@shadow()
+export class Descendant extends GemElement {
+ key = 0;
+
+ constructor() {
+ super();
+ console.log(`descendant${this.key} cons`);
+ }
+
+ @mounted()
+ #init = () => {
+ console.log(`descendant${this.key} mounted`);
+ };
+
+ render() {
+ console.log(`descendant${this.key} render`);
+ return html``;
+ }
+}
+
/**
* @attr first-name
* @attr last-name
@@ -70,6 +91,8 @@ export class Children extends GemElement {
properties: ${JSON.stringify(this.message)}
+
+
`;
}
}
diff --git a/packages/gem-examples/src/multi-page/index.ts b/packages/gem-examples/src/multi-page/index.ts
index 4abf84bf..6fb2d1af 100644
--- a/packages/gem-examples/src/multi-page/index.ts
+++ b/packages/gem-examples/src/multi-page/index.ts
@@ -8,6 +8,8 @@ import '../elements/layout';
import './page-b';
import './page-c';
+// 没写 `basePath`
+
const routes = [
{
pattern: '/',
@@ -32,8 +34,21 @@ const routes = [
content: html`C: `,
},
{
- pattern: '/',
- content: html`hello
`,
+ pattern: '/empty',
+ content: html``,
+ },
+ {
+ pattern: '/*',
+ content: html`
+
+
`,
},
];
@@ -62,6 +77,8 @@ class _App extends GemElement {
PageA
PageB
PageC
+ Empty
+ Test
`;
diff --git a/packages/gem/docs/en/003-api/008-utils.md b/packages/gem/docs/en/003-api/008-utils.md
index 7385d0fd..89e4a763 100644
--- a/packages/gem/docs/en/003-api/008-utils.md
+++ b/packages/gem/docs/en/003-api/008-utils.md
@@ -2,13 +2,12 @@
Some frequently used functions
-| name | description |
-| --------------------- | -------------------------------------------------------------------------------- |
-| `css` | Template string label function, only use for CSS text syntax highlighting |
-| `raw` | Template string tag function, only use for HTML text syntax highlighting |
-| `styled` | A collection of template string label functions, provide CSS highlighting |
-| `styleMap` | Convert objects into `style` attribute |
-| `classMap` | Convert objects into `class` attribute |
-| `partMap` | Convert objects into `part` attribute |
-| `addMicrotask` | Add microtask, avoid repetitive |
-| `addMicrotaskToStack` | Similar to `addMicrotask`, but the microtasks added later will be executed first |
+| name | description |
+| -------------- | ------------------------------------------------------------------------- |
+| `css` | Template string label function, only use for CSS text syntax highlighting |
+| `raw` | Template string tag function, only use for HTML text syntax highlighting |
+| `styled` | A collection of template string label functions, provide CSS highlighting |
+| `styleMap` | Convert objects into `style` attribute |
+| `classMap` | Convert objects into `class` attribute |
+| `partMap` | Convert objects into `part` attribute |
+| `addMicrotask` | Add microtask, avoid repetitive |
diff --git a/packages/gem/docs/zh/003-api/008-utils.md b/packages/gem/docs/zh/003-api/008-utils.md
index 8c6727cf..194baa7b 100644
--- a/packages/gem/docs/zh/003-api/008-utils.md
+++ b/packages/gem/docs/zh/003-api/008-utils.md
@@ -2,13 +2,12 @@
一些经常被用到的函数
-| 名称 | 描述 |
-| --------------------- | --------------------------------------------------------- |
-| `css` | 模版字符串标签函数,仅用于 CSS 文本语法高亮 |
-| `raw` | 模版字符串标签函数,仅用于 HTML 文本语法高亮 |
-| `styled` | 模版字符串标签函数的集合,用来提供 CSS 规则高亮和 JS 引用 |
-| `styleMap` | 将对象构建成 `style` 属性 |
-| `classMap` | 将对象构建成 `class` 属性 |
-| `partMap` | 将对象构建成 `part` 属性 |
-| `addMicrotask` | 添加微任务,会避免重复 |
-| `addMicrotaskToStack` | 类似 `addMicrotask`, 但先添加的微任务后执行 |
+| 名称 | 描述 |
+| -------------- | --------------------------------------------------------- |
+| `css` | 模版字符串标签函数,仅用于 CSS 文本语法高亮 |
+| `raw` | 模版字符串标签函数,仅用于 HTML 文本语法高亮 |
+| `styled` | 模版字符串标签函数的集合,用来提供 CSS 规则高亮和 JS 引用 |
+| `styleMap` | 将对象构建成 `style` 属性 |
+| `classMap` | 将对象构建成 `class` 属性 |
+| `partMap` | 将对象构建成 `part` 属性 |
+| `addMicrotask` | 添加微任务,会避免重复 |
diff --git a/packages/gem/src/elements/base/link.ts b/packages/gem/src/elements/base/link.ts
index 98530632..1c04e63a 100644
--- a/packages/gem/src/elements/base/link.ts
+++ b/packages/gem/src/elements/base/link.ts
@@ -10,6 +10,7 @@ import {
adoptedStyle,
mounted,
effect,
+ boolattribute,
} from '../../lib/decorators';
import { history, basePathStore } from '../../lib/history';
import { absoluteLocation, css } from '../../lib/utils';
@@ -61,8 +62,9 @@ export class GemLinkElement extends GemElement {
@attribute path: string;
@attribute query: string;
@attribute hash: string;
- @attribute docTitle: string;
@attribute hint: 'on' | 'off';
+ /** Preload route content when point over */
+ @boolattribute preload: boolean;
// 动态路由,根据 route.pattern 和 options.params 计算出 path
@property route?: RouteItem;
@@ -113,24 +115,19 @@ export class GemLinkElement extends GemElement {
await this.prepare?.();
if (this.route) {
- history.pushIgnoreCloseHandle({
- ...createHistoryParams(this.route, this.#routeOptions),
- title: this.route.title || this.docTitle,
- });
+ history.pushIgnoreCloseHandle(createHistoryParams(this.route, this.#routeOptions));
} else if (this.href) {
const url = new URL(locationString, location.origin);
history.pushIgnoreCloseHandle({
path: url.pathname,
query: url.search,
hash: url.hash,
- title: this.docTitle,
});
} else {
history.pushIgnoreCloseHandle({
path: this.path,
query: this.query,
hash: this.hash,
- title: this.docTitle,
});
}
};
@@ -146,9 +143,17 @@ export class GemLinkElement extends GemElement {
: new URL(history.basePath + locationString, location.origin).toString();
};
+ #preload = () => {
+ // 也许是 `getter`
+ this.route?.content;
+ // 只触发 `getContent` 调用,参数不使用
+ this.route?.getContent?.({}, this.shadowRoot!);
+ };
+
@mounted()
#init() {
this.addEventListener('click', this.#onClick);
+ this.addEventListener('pointerenter', this.#preload);
}
render() {
diff --git a/packages/gem/src/elements/base/route.ts b/packages/gem/src/elements/base/route.ts
index df462acc..86fd9804 100644
--- a/packages/gem/src/elements/base/route.ts
+++ b/packages/gem/src/elements/base/route.ts
@@ -314,7 +314,10 @@ export class GemLightRouteElement extends GemElement {
return;
}
if (this.trigger === history) {
- updateStore(titleStore, { title: route?.title });
+ // 嵌套路由执行顺序也是从父到子
+ if (location.href !== titleStore.url || route?.title) {
+ updateStore(titleStore, { url: location.href, title: route?.title });
+ }
}
const contentOrLoader = content || getContent?.(params, this.shadowRoot || this);
this.loading(route);
diff --git a/packages/gem/src/elements/base/title.ts b/packages/gem/src/elements/base/title.ts
index a36d4f5c..72938b47 100644
--- a/packages/gem/src/elements/base/title.ts
+++ b/packages/gem/src/elements/base/title.ts
@@ -7,41 +7,34 @@
* 修改标题:titleStore 的 title 优先级高,比如 history 添加的 dialog Title
*
* - `` 匹配路由时自动设置 `route.title`
- * - `` 的 `doc-title` 属性和 `route.title`
- * - 修改路径时,`history` 默认设置 `title` 为空
+ * - 修改路径(查询参数)时,`history` 默认设置 `title` 为空,运行 Modal 设置临时标题
* - 数据获取后,手动调用 `Title.setTitle`
* - `` 作为默认值设置
*/
import { GemElement, html } from '../../lib/element';
-import { attribute, boolattribute, effect, shadow } from '../../lib/decorators';
+import { attribute, boolattribute, effect, mounted, shadow } from '../../lib/decorators';
import { updateStore, connect } from '../../lib/store';
import { titleStore } from '../../lib/history';
-import { addMicrotask } from '../../lib/utils';
-const defaultTitle = document.title;
+// 避免重定向时的中间状态标题
+let timer = 0;
+const setTitle = (documentTitle: string) => {
+ clearTimeout(timer);
+ timer = window.setTimeout(() => (document.title = documentTitle));
+};
-let documentTitle = '';
-const setTitle = () => (document.title = documentTitle);
-
-function setDocumentTitle(str?: string | null, prefix = '', suffix = '') {
- const title = titleStore.title || str;
- if (title && title !== defaultTitle) {
+function setDocumentTitle(defaultTitle?: string | null, prefix = '', suffix = '') {
+ const title = titleStore.title || defaultTitle;
+ if (title && title !== titleStore.defaultTitle) {
GemTitleElement.title = title;
- documentTitle = prefix + GemTitleElement.title + suffix;
+ setTitle(prefix + GemTitleElement.title + suffix);
} else {
- GemTitleElement.title = defaultTitle;
- documentTitle = GemTitleElement.title;
+ GemTitleElement.title = titleStore.defaultTitle;
+ setTitle(GemTitleElement.title);
}
- // 避免重定向时的中间状态
- addMicrotask(setTitle);
}
-connect(titleStore, setDocumentTitle);
-
-export const PREFIX = `${defaultTitle} | `;
-export const SUFFIX = ` - ${defaultTitle}`;
-
/**
* @customElement gem-title
* @attr prefix
@@ -60,21 +53,19 @@ export class GemTitleElement extends GemElement {
updateStore(titleStore, { title });
}
- @effect(() => [])
- #mounted = () => {
- new MutationObserver(() => this.update()).observe(this, {
- characterData: true,
- childList: true,
- subtree: true,
- });
- };
-
@effect((i) => [i.inert])
#connectStore = () => !this.inert && connect(titleStore, this.update);
- render() {
- // 多个 时,最终 document.title 按执行顺序决定
+ #ob = new MutationObserver(() => this.update());
+
+ @mounted()
+ #obTextContent = () => {
+ this.#ob.observe(this, { characterData: true, childList: true, subtree: true });
+ };
+
+ render = () => {
+ // 多个 时,最终 document.title 按渲染顺序决定
setDocumentTitle(this.textContent?.trim(), this.prefix, this.suffix);
return html`${GemTitleElement.title}`;
- }
+ };
}
diff --git a/packages/gem/src/lib/history.ts b/packages/gem/src/lib/history.ts
index 6383ea5a..d00a4eb8 100644
--- a/packages/gem/src/lib/history.ts
+++ b/packages/gem/src/lib/history.ts
@@ -183,7 +183,7 @@ class GemHistory extends EventTarget {
// `` 只读写父应用 `basePath`
const gemHistory = new GemHistory();
-const [gemTitleStore, updateTitleStore] = useStore({ title: '' });
+const [gemTitleStore, updateTitleStore] = useStore({ defaultTitle: document.title, url: '', title: '' });
const _GEMHISTORY = { history: gemHistory, titleStore: gemTitleStore, basePathStore: gemBasePathStore };
diff --git a/packages/gem/src/lib/utils.ts b/packages/gem/src/lib/utils.ts
index 83e4bc13..d7632a1e 100644
--- a/packages/gem/src/lib/utils.ts
+++ b/packages/gem/src/lib/utils.ts
@@ -12,26 +12,6 @@ export function addMicrotask(func: () => void) {
microtaskSet.add(func);
}
-const microtaskStack: (() => void)[] = [];
-
-function execMicrotaskStack() {
- for (let i = microtaskStack.length - 1; i >= 0; i--) {
- microtaskStack[i]();
- }
- microtaskStack.length = 0;
-}
-
-/**
- * 添加回调函数到微任务队列栈;
- * 先进后执行,使用这个函数可以改变嵌套的元素 `mounted` 的顺序;
- */
-export function addMicrotaskToStack(func: () => void) {
- if (!microtaskStack.length) {
- addMicrotask(execMicrotaskStack);
- }
- microtaskStack.push(func);
-}
-
// 不编码 hash 用于比较
export function absoluteLocation(currentPath = '', relativePath = '') {
const { pathname, search, hash } = new URL(relativePath, location.origin + currentPath);