diff --git a/ReadMe.md b/ReadMe.md
index bdbbc53..e242261 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -22,6 +22,8 @@ https://web-cell.dev/scaffold/
- [x] **Path Mode**: `location.hash` (default) & `history.pushState()`
+- [x] **Async Loading** (recommend to use with `import()` ECMAScript proposal)
+
- [x] (experimental) [Nested Router][5] support
## Installation
@@ -46,6 +48,8 @@ npm install parcel-bundler -D
## Usage
+### Sync Pages
+
`source/model/index.ts`
```typescript
@@ -56,7 +60,7 @@ export const history = new History();
`source/page/PageRouter.tsx`
-```jsx
+```javascript
import { createCell, component } from 'web-cell';
import { observer } from 'mobx-web-cell';
import { HTMLRouter } from 'cell-router/source';
@@ -78,6 +82,10 @@ function Example({ path }) {
})
export default class PageRouter extends HTMLRouter {
protected history = history;
+ protected routes = [
+ { paths: ['test'], component: Test },
+ { paths: ['example'], component: Example }
+ ];
render() {
return (
@@ -90,15 +98,73 @@ export default class PageRouter extends HTMLRouter {
Example
-
- {matchRoutes(
- [
- { paths: ['test'], component: Test },
- { paths: ['example'], component: Example }
- ],
- history.path
- )}
-
+ {super.render()}
+
+ );
+ }
+}
+```
+
+### Async Pages
+
+`tsconfig.json`
+
+```json
+{
+ "compilerOptions": {
+ "module": "ESNext"
+ }
+}
+```
+
+`source/page/index.ts`
+
+```javascript
+export default [
+ {
+ paths: ['', 'home'],
+ component: async () => (await import('./Home.tsx')).default,
+ async: true
+ },
+ {
+ paths: ['list'],
+ component: async () => (await import('./List.tsx')).default,
+ async: true
+ }
+];
+```
+
+`source/component/PageRouter.tsx`
+
+```javascript
+import { component, createCell } from 'web-cell';
+import { observer } from 'mobx-web-cell';
+import { HTMLRouter } from 'cell-router/source';
+
+import { history } from '../model';
+import routes from '../page';
+
+@observer
+@component({
+ tagName: 'page-router',
+ renderTarget: 'children'
+})
+export default class PageRouter extends HTMLRouter {
+ protected history = history;
+ protected routes = routes;
+
+ render() {
+ return (
+
+
+ {super.render()}
);
}
diff --git a/package.json b/package.json
index 5db57a4..f881a57 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cell-router",
- "version": "2.0.0-rc.3",
+ "version": "2.0.0-rc.4",
"license": "LGPL-3.0",
"description": "Web Component Router based on WebCell & MobX",
"keywords": [
@@ -26,7 +26,7 @@
"peerDependencies": {
"mobx": "^5.15.1",
"mobx-web-cell": "^0.2.5",
- "web-cell": "^2.0.0-rc.12"
+ "web-cell": "^2.0.0-rc.13"
},
"devDependencies": {
"@types/jest": "^24.0.25",
@@ -48,10 +48,10 @@
"ts-jest": "^24.2.0",
"typedoc": "^0.15.6",
"typescript": "^3.7.4",
- "web-cell": "^2.0.0-rc.12"
+ "web-cell": "^2.0.0-rc.13"
},
"scripts": {
- "debug": "cd test/ && parcel source/index.html",
+ "start": "cd test/ && parcel source/index.html --open",
"lint": "lint-staged",
"pack-test": "cd test/ && parcel build source/index.html",
"set-chrome": "app-find chrome -c",
diff --git a/source/HTMLRouter.ts b/source/HTMLRouter.tsx
similarity index 65%
rename from source/HTMLRouter.ts
rename to source/HTMLRouter.tsx
index dfe3558..3e3a581 100644
--- a/source/HTMLRouter.ts
+++ b/source/HTMLRouter.tsx
@@ -1,7 +1,7 @@
-import { mixin, delegate } from 'web-cell';
+import { mixin, delegate, createCell } from 'web-cell';
import { History, HistoryMode } from './History';
-import { scrollTo } from './utility';
+import { Route, scrollTo, matchRoutes } from './utility';
type LinkElement = HTMLAnchorElement | HTMLAreaElement;
@@ -25,6 +25,8 @@ export abstract class HTMLRouter extends mixin() {
}
protected abstract history: History;
+ protected abstract routes: Route[];
+ private currentPage: Function;
handleLink = delegate('a[href]', (event: MouseEvent) => {
const link = event.target as LinkElement;
@@ -63,6 +65,15 @@ export abstract class HTMLRouter extends mixin() {
this.history.reset(!!this.parentRouter);
+ this.routes = this.routes
+ .map(({ paths, ...rest }) =>
+ paths.map(path => ({ paths: [path], ...rest }))
+ )
+ .flat()
+ .sort(({ paths: [A] }, { paths: [B] }) =>
+ (B as string).localeCompare(A as string)
+ );
+
this.addEventListener('click', this.handleLink);
window.addEventListener('popstate', this.handleBack);
@@ -77,4 +88,28 @@ export abstract class HTMLRouter extends mixin() {
if (this.history.mode === HistoryMode.hash)
window.removeEventListener('hashchange', this.handleHash);
}
+
+ render() {
+ const { component, async, path, params } =
+ matchRoutes(this.routes, this.history.path) || {};
+
+ if (!component) return;
+
+ if (async)
+ component().then((func: Function) => {
+ const route = this.routes.find(
+ route => route.component === component
+ );
+ if (!route) return;
+
+ route.component = func;
+ delete route.async;
+ // @ts-ignore
+ this.update();
+ });
+ else this.currentPage = component;
+
+ if (this.currentPage)
+ return ;
+ }
}
diff --git a/source/utility.tsx b/source/utility.ts
similarity index 78%
rename from source/utility.tsx
rename to source/utility.ts
index 10daf64..c19c417 100644
--- a/source/utility.tsx
+++ b/source/utility.ts
@@ -1,5 +1,3 @@
-import { createCell } from 'web-cell';
-
export function parsePathData(URI: string) {
const params = {},
[path, data] = URI.split('?');
@@ -31,21 +29,19 @@ export function scrollTo(selector: string, root?: Element) {
if (anchor) anchor.scrollIntoView({ behavior: 'smooth' });
}
-interface Route {
+export interface Route {
paths: (string | RegExp)[];
- component: Function;
+ component: Function | (() => Promise);
+ async?: boolean;
}
export function matchRoutes(list: Route[], path: string) {
- for (const { paths, component: Component } of list)
+ for (const { paths, ...rest } of list)
for (const item of paths)
if (
typeof item === 'string'
? path.startsWith(item)
: item.exec(path)
- ) {
- const data = parsePathData(path);
-
- return ;
- }
+ )
+ return { ...rest, ...parsePathData(path) };
}
diff --git a/test/source/page/SubRouter.tsx b/test/source/page/SubRouter.tsx
index eac8c57..f1f83bf 100644
--- a/test/source/page/SubRouter.tsx
+++ b/test/source/page/SubRouter.tsx
@@ -1,6 +1,6 @@
import { createCell, component } from 'web-cell';
import { observer } from 'mobx-web-cell';
-import { HTMLRouter, matchRoutes } from '../../../source';
+import { HTMLRouter } from '../../../source';
import { subHistory } from '../model';
@@ -19,6 +19,10 @@ function Temp({ path }) {
})
export default class SubRouter extends HTMLRouter {
protected history = subHistory;
+ protected routes = [
+ { paths: ['sample'], component: Sample },
+ { paths: ['temp'], component: Temp }
+ ];
render() {
return (
@@ -31,15 +35,7 @@ export default class SubRouter extends HTMLRouter {
Temp
-
- {matchRoutes(
- [
- { paths: ['sample'], component: Sample },
- { paths: ['temp'], component: Temp }
- ],
- subHistory.path
- )}
-
+ {super.render()}
);
}
diff --git a/test/source/page/TopRouter.tsx b/test/source/page/TopRouter.tsx
index 6ed913d..442e276 100644
--- a/test/source/page/TopRouter.tsx
+++ b/test/source/page/TopRouter.tsx
@@ -1,6 +1,6 @@
import { createCell, component } from 'web-cell';
import { observer } from 'mobx-web-cell';
-import { HTMLRouter, matchRoutes } from '../../../source';
+import { HTMLRouter } from '../../../source';
import { topHistory } from '../model';
import SubRouter from './SubRouter';
@@ -25,6 +25,14 @@ function Example({ path }) {
})
export default class TopRouter extends HTMLRouter {
protected history = topHistory;
+ protected routes = [
+ { paths: ['test'], component: Test },
+ {
+ paths: ['example'],
+ component: () => Promise.resolve(Example),
+ async: true
+ }
+ ];
render() {
return (
@@ -37,15 +45,7 @@ export default class TopRouter extends HTMLRouter {
Example
-
- {matchRoutes(
- [
- { paths: ['test'], component: Test },
- { paths: ['example'], component: Example }
- ],
- topHistory.path
- )}
-
+ {super.render()}
);
}
diff --git a/tsconfig.json b/tsconfig.json
index 61c17b8..adea1eb 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,11 +2,12 @@
"compilerOptions": {
"module": "ES6",
"moduleResolution": "Node",
+ "esModuleInterop": true,
"downlevelIteration": true,
"experimentalDecorators": true,
"jsx": "react",
"jsxFactory": "createCell",
- "lib": ["DOM", "DOM.Iterable"],
+ "lib": ["ES2019", "DOM", "DOM.Iterable"],
"target": "ES5"
},
"include": ["source/**/*"]