From 78d581c98c48e019dda3ee3ceaafb82829319c88 Mon Sep 17 00:00:00 2001 From: xion Date: Mon, 9 Dec 2024 12:48:13 +0800 Subject: [PATCH 01/15] fix: add env --- demo/src/index.ts | 4 +++ package.json | 3 +- rollup.config.js | 20 ++--------- src/page.ts | 8 ++++- src/web-config.ts | 41 --------------------- src/web-context.ts | 41 --------------------- src/web-env.ts | 90 ++++++++++++++++++++++++++++++++++++++++++++++ src/web.ts | 3 +- tsconfig.json | 1 + 9 files changed, 107 insertions(+), 104 deletions(-) delete mode 100644 src/web-config.ts delete mode 100644 src/web-context.ts create mode 100644 src/web-env.ts diff --git a/demo/src/index.ts b/demo/src/index.ts index 20a8b1f..fbd141d 100644 --- a/demo/src/index.ts +++ b/demo/src/index.ts @@ -4,6 +4,7 @@ import { Page } from '@kevisual/store/page'; const page = new Page({ isListen: true, }); +page.basename = ''; page.addPage('/', 'home'); page.addPage('/:id', 'user'); page.subscribe( @@ -21,3 +22,6 @@ page.subscribe('user', (params, state) => { // page.navigate('/c', { id: 3 }); // page.navigate('/c', { id: 2 }); // page.refresh(); + +// @ts-ignore +window.page = page; \ No newline at end of file diff --git a/package.json b/package.json index 858095a..864b6d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kevisual/store", - "version": "0.0.1-alpha.5", + "version": "0.0.1-alpha.7", "main": "dist/store.js", "module": "dist/store.js", "types": "dist/store.d.ts", @@ -23,6 +23,7 @@ "license": "ISC", "description": "", "devDependencies": { + "@kevisual/types": "link:../types", "@rollup/plugin-commonjs": "^28.0.1", "@rollup/plugin-node-resolve": "^15.3.0", "@rollup/plugin-typescript": "^12.1.1", diff --git a/rollup.config.js b/rollup.config.js index 02eb0c3..7443441 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -30,7 +30,7 @@ export default [ plugins: [dts()], }, { - input: 'src/web-config.ts', + input: 'src/web-env.ts', output: { file: 'dist/web-config.js', format: 'es', @@ -38,29 +38,13 @@ export default [ plugins: [resolve({ browser: true }), commonjs(), typescript()], }, { - input: 'src/web-config.ts', + input: 'src/web-env.ts', output: { file: 'dist/web-config.d.ts', format: 'es', }, plugins: [dts()], }, - { - input: 'src/web-context.ts', - output: { - file: 'dist/web-context.js', - format: 'es', - }, - plugins: [resolve({ browser: true }), commonjs(), typescript()], - }, - { - input: 'src/web-context.ts', - output: { - file: 'dist/web-context.d.ts', - format: 'es', - }, - plugins: [dts()], - }, { input: 'src/page.ts', output: { diff --git a/src/page.ts b/src/page.ts index 70c4fe8..4559a84 100644 --- a/src/page.ts +++ b/src/page.ts @@ -2,6 +2,10 @@ import { getPathKey } from '@/utils/path-key.ts'; import { match } from 'path-to-regexp'; import deepEqual from 'fast-deep-equal'; +const generateRandom = () => { + // return Math.random().toString(36).substring(8); + return crypto.randomUUID(); +}; type PageOptions = { path?: string; key?: string; @@ -19,6 +23,7 @@ type CallbackInfo = { fn: CallFn; id: string; } & PageModule; +let currentUrl = location.href; export class Page { pageModule = new Map(); // pathname的第一个路径 @@ -40,6 +45,7 @@ export class Page { } popstate(event?: PopStateEvent, manualOpts?: { id?: string; type: 'singal' | 'all' }) { const pathname = window.location.pathname; + console.log('popstate', event); if (manualOpts) { if (manualOpts.type === 'singal') { const item = this.callbacks.find((item) => item.id === manualOpts.id); @@ -120,7 +126,7 @@ export class Page { */ subscribe(key: string, fn?: CallFn, opts?: { pathname?: string; runImmediately?: boolean; id?: string }) { const runImmediately = opts?.runImmediately ?? true; // 默认立即执行 - const id = opts?.id ?? Math.random().toString(36).slice(2); + const id = opts?.id ?? generateRandom(); const path = this.pageModule.get(key)?.path; if (!path) { console.error(`PageModule ${key} not found`); diff --git a/src/web-config.ts b/src/web-config.ts deleted file mode 100644 index e8bd5d6..0000000 --- a/src/web-config.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { getPathKey } from './utils/path-key.ts'; - -type GlobalConfig = { - name?: string; - [key: string]: any; -}; -export const useConfig = (initConfig?: GlobalConfig) => { - const config: GlobalConfig = (window as any).config; - const _config = config || initConfig; - !config && ((window as any)['config'] = _config); - return _config; -}; - -export const useConfigKey = (key: string, init?: () => T): T => { - const _config = useConfig({}); - if (key && init) { - _config[key] = init(); - return _config[key] as any; - } - if (key) { - return _config[key]; - } - return _config as any; -}; - -export const useConfigKeySync = async (key: string, init?: () => Promise): Promise => { - const _config = useConfig({}); - if (key && init) { - _config[key] = await init(); - return _config[key] as any; - } - if (key) { - return _config[key]; - } - return _config as any; -}; - -export const usePageConfig = (init?: () => {}) => { - const { id } = getPathKey(); - return useConfigKey(id, init); -}; diff --git a/src/web-context.ts b/src/web-context.ts deleted file mode 100644 index 5e30543..0000000 --- a/src/web-context.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { getPathKey } from './utils/path-key.ts'; - -type GlobalContext = { - name?: string; - [key: string]: any; -}; -export const useContext = (initContext?: GlobalContext) => { - const context: GlobalContext = (window as any).context; - const _context = context || initContext; - !context && ((window as any)['context'] = _context); - return _context; -}; - -export const useContextKey = (key: string, init?: () => T): T => { - const _context = useContext({}); - if (key && init) { - _context[key] = init(); - return _context[key] as any; - } - if (key) { - return _context[key]; - } - return _context as any; -}; - -export const useContextKeySync = async (key: string, init?: () => Promise): Promise => { - const _context = useContext({}); - if (key && init) { - _context[key] = await init(); - return _context[key] as any; - } - if (key) { - return _context[key]; - } - return _context as any; -}; - -export const usePageContext = (init?: () => {}) => { - const { id } = getPathKey(); - return useContextKey(id, init); -}; diff --git a/src/web-env.ts b/src/web-env.ts new file mode 100644 index 0000000..fcb5c0a --- /dev/null +++ b/src/web-env.ts @@ -0,0 +1,90 @@ +import { getPathKey } from './utils/path-key.ts'; + +type GlobalEnv = { + name?: string; + [key: string]: any; +}; +export const useEnv = (initEnv?: GlobalEnv, initKey = 'config') => { + const env: GlobalEnv = (window as any)[initKey]; + const _env = env || initEnv; + if (!env) { + if (_env) { + (window as any)[initKey] = _env; + } else { + (window as any)[initKey] = {}; + } + } + return window[initKey] as GlobalEnv; +}; + +export const useEnvKey = (key: string, init?: () => T | null, initKey = 'config'): T => { + const _env = useEnv({}, initKey); + if (key && _env[key]) { + return _env[key]; + } + if (key && init) { + _env[key] = init(); + return _env[key]; + } + + return _env as any; +}; + +export const useEnvKeySync = async (key: string, init?: () => Promise | null, initKey = 'conifg'): Promise => { + const _env = useEnv({}, initKey); + if (key && init) { + _env[key] = await init(); + return _env[key]; + } + if (key) { + return _env[key]; + } + return _env as any; +}; + +export const usePageEnv = (init?: () => {}, initKey = 'conifg') => { + const { id } = getPathKey(); + return useEnvKey(id, init, initKey); +}; + +type GlobalContext = { + name?: string; + [key: string]: any; +}; +export const useContext = (initContext?: GlobalContext) => { + return useEnv(initContext, 'context'); +}; + +export const useContextKey = (key: string, init?: () => T): T => { + return useEnvKey(key, init, 'context'); +}; + +export const useContextKeySync = async (key: string, init?: () => Promise): Promise => { + return useEnvKeySync(key, init, 'context'); +}; + +export const usePageContext = (init?: () => {}) => { + const { id } = getPathKey(); + return useContextKey(id, init); +}; + +type GlobalConfig = { + name?: string; + [key: string]: any; +}; +export const useConfig = (initConfig?: GlobalConfig) => { + return useEnv(initConfig, 'config'); +}; + +export const useConfigKey = (key: string, init?: () => T): T => { + return useEnvKey(key, init, 'config'); +}; + +export const useConfigKeySync = async (key: string, init?: () => Promise): Promise => { + return useEnvKeySync(key, init, 'config'); +}; + +export const usePageConfig = (init?: () => {}) => { + const { id } = getPathKey(); + return useConfigKey(id, init); +}; diff --git a/src/web.ts b/src/web.ts index 807e402..b17642b 100644 --- a/src/web.ts +++ b/src/web.ts @@ -1,6 +1,5 @@ export * from './page.ts'; -export * from './web-context.ts'; -export * from './web-config.ts'; +export * from './web-env.ts'; export * from 'nanoid'; export * from 'path-to-regexp'; diff --git a/tsconfig.json b/tsconfig.json index e8e7f97..4591ee9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "baseUrl": "./", "typeRoots": [ "node_modules/@types", + "node_modules/@kevisual/types", ], "declaration": false, "noEmit": true, From 3b19cd8581ee4a6ffe1c5f571032a795ec811ace Mon Sep 17 00:00:00 2001 From: xion Date: Wed, 18 Dec 2024 11:10:48 +0800 Subject: [PATCH 02/15] temp --- src/web.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/web.ts b/src/web.ts index b17642b..44b98f4 100644 --- a/src/web.ts +++ b/src/web.ts @@ -1,7 +1,12 @@ -export * from './page.ts'; -export * from './web-env.ts'; +import { Page } from './page.ts'; +import * as WebEnv from './web-env.ts'; -export * from 'nanoid'; -export * from 'path-to-regexp'; +import { nanoid } from 'nanoid'; +import * as PathToREgexp from 'path-to-regexp'; -export * from 'eventemitter3'; +export const WebModule = { + Page, + WebEnv, + nanoid, + PathToREgexp, +}; From 9b3de5eba39d192cad86d5333e62544857cc6c21 Mon Sep 17 00:00:00 2001 From: xion Date: Sun, 22 Dec 2024 14:49:48 +0800 Subject: [PATCH 03/15] feat: add app fof iife --- package.json | 4 +++- rollup.config.js | 17 +++++++++++++++++ src/app.ts | 12 ++++++++++++ src/web-env.ts | 2 ++ 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/app.ts diff --git a/package.json b/package.json index 864b6d5..0d728de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kevisual/store", - "version": "0.0.1-alpha.7", + "version": "0.0.1-alpha.8", "main": "dist/store.js", "module": "dist/store.js", "types": "dist/store.d.ts", @@ -69,6 +69,8 @@ } }, "dependencies": { + "@kevisual/router": "0.0.6-alpha-4", + "@rollup/plugin-terser": "^0.4.4", "eventemitter3": "^5.0.1", "path-to-regexp": "^8.2.0" } diff --git a/rollup.config.js b/rollup.config.js index 7443441..6a3536b 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -4,6 +4,7 @@ import typescript from '@rollup/plugin-typescript'; import resolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import { dts } from 'rollup-plugin-dts'; +import terser from '@rollup/plugin-terser'; /** * @type {import('rollup').RollupOptions} @@ -77,4 +78,20 @@ export default [ }, plugins: [dts()], }, + { + input: 'src/app.ts', + output: { + file: 'dist/app.js', + format: 'iife', + }, + plugins: [resolve({ browser: true }), commonjs(), typescript(), terser()], + }, + { + input: 'src/app.ts', + output: { + file: 'dist/app.d.ts', + format: 'es', + }, + plugins: [dts()], + }, ]; diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..ef8d387 --- /dev/null +++ b/src/app.ts @@ -0,0 +1,12 @@ +// 当前的功能,把所有的模块注入到windows对象当中 +import { useConfigKey, useContextKey } from './web-env.ts'; +import { QueryRouterServer } from '@kevisual/router/browser'; + +// bind to window, 必须要的获取全局的环境变量 +window.useConfigKey = useConfigKey; +window.useContextKey = useContextKey; +window.QueryRouterServer = QueryRouterServer; + +// bind to window, 获取路由对象 +useContextKey('app', () => new QueryRouterServer()); + diff --git a/src/web-env.ts b/src/web-env.ts index fcb5c0a..1841175 100644 --- a/src/web-env.ts +++ b/src/web-env.ts @@ -4,6 +4,7 @@ type GlobalEnv = { name?: string; [key: string]: any; }; +// 从window对象中获取全局的环境变量,如果没有则初始化一个 export const useEnv = (initEnv?: GlobalEnv, initKey = 'config') => { const env: GlobalEnv = (window as any)[initKey]; const _env = env || initEnv; @@ -17,6 +18,7 @@ export const useEnv = (initEnv?: GlobalEnv, initKey = 'config') => { return window[initKey] as GlobalEnv; }; +// 从全局环境变量中获取指定的key值,如果没有则初始化一个, key不存在,返回Env对象 export const useEnvKey = (key: string, init?: () => T | null, initKey = 'config'): T => { const _env = useEnv({}, initKey); if (key && _env[key]) { From 7cb42edc5f07016e1226af8118f1c5f49adc000c Mon Sep 17 00:00:00 2001 From: xion Date: Sun, 22 Dec 2024 21:41:23 +0800 Subject: [PATCH 04/15] fix: add iifi for Page --- src/app.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app.ts b/src/app.ts index ef8d387..bea67e2 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,10 +1,11 @@ // 当前的功能,把所有的模块注入到windows对象当中 import { useConfigKey, useContextKey } from './web-env.ts'; import { QueryRouterServer } from '@kevisual/router/browser'; - +import { Page } from './page.ts'; // bind to window, 必须要的获取全局的环境变量 window.useConfigKey = useConfigKey; window.useContextKey = useContextKey; +window.Page = Page; window.QueryRouterServer = QueryRouterServer; // bind to window, 获取路由对象 From c855c7d3d58d69a8992972287826750d19d10613 Mon Sep 17 00:00:00 2001 From: xion Date: Thu, 2 Jan 2025 23:04:01 +0800 Subject: [PATCH 05/15] feat: add page --- package.json | 2 +- src/app.ts | 5 +++-- src/page.ts | 31 ++++++++++++++++++++++++++----- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 0d728de..1474bb6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kevisual/store", - "version": "0.0.1-alpha.8", + "version": "0.0.1-alpha.9", "main": "dist/store.js", "module": "dist/store.js", "types": "dist/store.d.ts", diff --git a/src/app.ts b/src/app.ts index bea67e2..fae7b43 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,13 +1,14 @@ // 当前的功能,把所有的模块注入到windows对象当中 -import { useConfigKey, useContextKey } from './web-env.ts'; +import { useConfigKey, useConfigKeySync, useContextKeySync, useContextKey } from './web-env.ts'; import { QueryRouterServer } from '@kevisual/router/browser'; import { Page } from './page.ts'; // bind to window, 必须要的获取全局的环境变量 window.useConfigKey = useConfigKey; +window.useConfigKeySync = useConfigKeySync; window.useContextKey = useContextKey; +window.useContextKeySync = useContextKeySync; window.Page = Page; window.QueryRouterServer = QueryRouterServer; // bind to window, 获取路由对象 useContextKey('app', () => new QueryRouterServer()); - diff --git a/src/page.ts b/src/page.ts index 4559a84..ee088ca 100644 --- a/src/page.ts +++ b/src/page.ts @@ -7,8 +7,17 @@ const generateRandom = () => { return crypto.randomUUID(); }; type PageOptions = { + /** + * 路径 + */ path?: string; + /** + * key + */ key?: string; + /** + * basename + */ basename?: string; isListen?: boolean; }; @@ -23,7 +32,9 @@ type CallbackInfo = { fn: CallFn; id: string; } & PageModule; -let currentUrl = location.href; +/** + * 根据basename,其中的path和key,来判断一个应用的模块。 + */ export class Page { pageModule = new Map(); // pathname的第一个路径 @@ -43,9 +54,8 @@ export class Page { this.listen(); } } - popstate(event?: PopStateEvent, manualOpts?: { id?: string; type: 'singal' | 'all' }) { - const pathname = window.location.pathname; - console.log('popstate', event); + popstate(event?: PopStateEvent, manualOpts?: { id?: string; type: 'singal' | 'all'; pathname?: string }) { + const pathname = manualOpts?.pathname ?? window.location.pathname; if (manualOpts) { if (manualOpts.type === 'singal') { const item = this.callbacks.find((item) => item.id === manualOpts.id); @@ -62,7 +72,17 @@ export class Page { result && item.fn?.(result.params, event.state, { event, manualOpts }); }); } + /** + * 调用callback中id或者pathname的函数, 其中id优先级高于pathname,若都没有,则从location.pathname中获取 + * @param opts + */ + call(opts?: { id?: string; pathname?: string }) { + this.popstate({ state: window.history.state } as any, { ...opts, type: 'all' }); + } listen() { + if (this.isListen) { + return; + } this.isListen = true; window.addEventListener('popstate', this.popstate.bind(this), false); } @@ -89,7 +109,7 @@ export class Page { }); } /** - * 检查当前路径是否匹配 + * 检查当前路径是否匹配, 如何提交pathname,则检查pathname * @param key * @param pathname * @returns @@ -138,6 +158,7 @@ export class Page { const pathname = opts?.pathname ?? location.pathname; const result = this.check(key, pathname); if (result) { + // 如果是手动调用,则不需要检查是否相等,直接执行,而且是执行当前的subscribe的函数 this.popstate({ state: window.history.state } as any, { id, type: 'singal' }); } } From 01db9c9ea283436972b14fd0b395c254a446017e Mon Sep 17 00:00:00 2001 From: xion Date: Tue, 7 Jan 2025 02:58:02 +0800 Subject: [PATCH 06/15] doc --- src/page.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/page.ts b/src/page.ts index ee088ca..5dc3546 100644 --- a/src/page.ts +++ b/src/page.ts @@ -54,6 +54,7 @@ export class Page { this.listen(); } } + popstate(event?: PopStateEvent, manualOpts?: { id?: string; type: 'singal' | 'all'; pathname?: string }) { const pathname = manualOpts?.pathname ?? window.location.pathname; if (manualOpts) { @@ -166,6 +167,9 @@ export class Page { this.callbacks = this.callbacks.filter((item) => item.id !== id); }; } + getPathKey() { + return getPathKey(); + } /** * 返回当前路径,不包含basename * @returns @@ -178,12 +182,26 @@ export class Page { getAppPath() { return this.path; } + /** + * 返回当前key + * @returns + */ getAppKey() { return this.key; } + /** + * 解码路径 + * @param path + * @returns + */ decodePath(path: string) { return decodeURIComponent(path); } + /** + * 编码路径 + * @param path + * @returns + */ encodePath(path: string) { return encodeURIComponent(path); } @@ -214,6 +232,12 @@ export class Page { this.popstate({ state } as any, { type: 'all' }); } } + /** + * 替换当前路径 + * @param path + * @param state + * @param check + */ replace(path: string, state?: any, check?: boolean) { let _check = check ?? true; window.history.replaceState(state, '', this.basename + path); @@ -221,6 +245,9 @@ export class Page { this.popstate({ state } as any, { type: 'all' }); } } + /** + * 刷新当前页面 + */ refresh() { const state = window.history.state; this.popstate({ state } as any, { type: 'all' }); From b39c950ef7cab6029a57f842f1c100597821ec53 Mon Sep 17 00:00:00 2001 From: xion Date: Tue, 7 Jan 2025 03:01:09 +0800 Subject: [PATCH 07/15] temp --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1474bb6..c3585e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kevisual/store", - "version": "0.0.1-alpha.9", + "version": "0.0.1-alpha.10", "main": "dist/store.js", "module": "dist/store.js", "types": "dist/store.d.ts", From d8d5073542825bb500e4f8b3ebd8495621d69518 Mon Sep 17 00:00:00 2001 From: xion Date: Wed, 19 Feb 2025 06:52:18 +0800 Subject: [PATCH 08/15] =?UTF-8?q?store=20=E6=B7=BB=E5=8A=A0Load=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 ++- src/web.ts | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index c3585e1..438b951 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kevisual/store", - "version": "0.0.1-alpha.10", + "version": "0.0.1", "main": "dist/store.js", "module": "dist/store.js", "types": "dist/store.d.ts", @@ -23,6 +23,7 @@ "license": "ISC", "description": "", "devDependencies": { + "@kevisual/load": "workspace:*", "@kevisual/types": "link:../types", "@rollup/plugin-commonjs": "^28.0.1", "@rollup/plugin-node-resolve": "^15.3.0", diff --git a/src/web.ts b/src/web.ts index 44b98f4..b659f00 100644 --- a/src/web.ts +++ b/src/web.ts @@ -3,10 +3,12 @@ import * as WebEnv from './web-env.ts'; import { nanoid } from 'nanoid'; import * as PathToREgexp from 'path-to-regexp'; +import * as Load from '@kevisual/load/browser'; export const WebModule = { Page, WebEnv, nanoid, PathToREgexp, + Load, }; From 3a027efb794db687bf9417d8c76942f8580c12ce Mon Sep 17 00:00:00 2001 From: xion Date: Thu, 27 Feb 2025 00:57:05 +0800 Subject: [PATCH 09/15] feat: add Load --- package.json | 16 ++++++++-------- src/app.ts | 9 ++++++++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 438b951..18d7d73 100644 --- a/package.json +++ b/package.json @@ -25,20 +25,20 @@ "devDependencies": { "@kevisual/load": "workspace:*", "@kevisual/types": "link:../types", - "@rollup/plugin-commonjs": "^28.0.1", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.1", + "@rollup/plugin-commonjs": "^28.0.2", + "@rollup/plugin-node-resolve": "^16.0.0", + "@rollup/plugin-typescript": "^12.1.2", "@types/lodash-es": "^4.17.12", "fast-deep-equal": "^3.1.3", "immer": "^10.1.1", "lodash-es": "^4.17.21", - "nanoid": "^5.0.9", - "rollup": "^4.27.4", + "nanoid": "^5.1.2", + "rollup": "^4.34.8", "rollup-plugin-dts": "^6.1.1", "ts-node": "^10.9.2", "tslib": "^2.8.1", - "typescript": "^5.7.2", - "zustand": "^5.0.1" + "typescript": "^5.7.3", + "zustand": "^5.0.3" }, "publishConfig": { "access": "public" @@ -70,7 +70,7 @@ } }, "dependencies": { - "@kevisual/router": "0.0.6-alpha-4", + "@kevisual/router": "0.0.7", "@rollup/plugin-terser": "^0.4.4", "eventemitter3": "^5.0.1", "path-to-regexp": "^8.2.0" diff --git a/src/app.ts b/src/app.ts index fae7b43..652250d 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,12 +1,19 @@ // 当前的功能,把所有的模块注入到windows对象当中 -import { useConfigKey, useConfigKeySync, useContextKeySync, useContextKey } from './web-env.ts'; +import * as WebEnv from './web-env.ts'; import { QueryRouterServer } from '@kevisual/router/browser'; +import * as Load from '@kevisual/load/browser'; import { Page } from './page.ts'; + // bind to window, 必须要的获取全局的环境变量 +const { useConfigKey, useConfigKeySync, useContextKey, useContextKeySync } = WebEnv; window.useConfigKey = useConfigKey; window.useConfigKeySync = useConfigKeySync; window.useContextKey = useContextKey; window.useContextKeySync = useContextKeySync; +// @ts-ignore +window.webEnv = WebEnv; +// @ts-ignore +window.Load = Load; window.Page = Page; window.QueryRouterServer = QueryRouterServer; From 86ae2c7f70b021d648d33a352a0facc122b27efe Mon Sep 17 00:00:00 2001 From: xion Date: Thu, 27 Feb 2025 18:26:56 +0800 Subject: [PATCH 10/15] =?UTF-8?q?fix:=20basename=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=EF=BC=8C=E5=90=8E=E7=BC=80=E4=B8=BA/=E7=9A=84=E5=8E=BB?= =?UTF-8?q?=E6=8E=89/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- src/page.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 18d7d73..ff87f7c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kevisual/store", - "version": "0.0.1", + "version": "0.0.2", "main": "dist/store.js", "module": "dist/store.js", "types": "dist/store.d.ts", diff --git a/src/page.ts b/src/page.ts index 5dc3546..adbeaac 100644 --- a/src/page.ts +++ b/src/page.ts @@ -49,6 +49,9 @@ export class Page { this.path = opts?.path ?? pathKey.path; this.key = opts?.key ?? pathKey.key; this.basename = opts?.basename ?? `/${this.path}/${this.key}`; + if(this.basename.endsWith('/')) { + this.basename = this.basename.slice(0, -1); + } const isListen = opts?.isListen ?? true; if (isListen) { this.listen(); From 3ee69a4f81bdcd425820f05ab20a49414a4b1c73 Mon Sep 17 00:00:00 2001 From: xion Date: Sat, 1 Mar 2025 17:35:43 +0800 Subject: [PATCH 11/15] perf: for useConfigKey --- package.json | 2 +- src/app.ts | 4 +--- src/web-env.ts | 48 ++++++++++++++++++++++++++++-------------------- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index ff87f7c..929f0a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kevisual/store", - "version": "0.0.2", + "version": "0.0.3", "main": "dist/store.js", "module": "dist/store.js", "types": "dist/store.d.ts", diff --git a/src/app.ts b/src/app.ts index 652250d..4c7062f 100644 --- a/src/app.ts +++ b/src/app.ts @@ -5,11 +5,9 @@ import * as Load from '@kevisual/load/browser'; import { Page } from './page.ts'; // bind to window, 必须要的获取全局的环境变量 -const { useConfigKey, useConfigKeySync, useContextKey, useContextKeySync } = WebEnv; +const { useConfigKey, useContextKey } = WebEnv; window.useConfigKey = useConfigKey; -window.useConfigKeySync = useConfigKeySync; window.useContextKey = useContextKey; -window.useContextKeySync = useContextKeySync; // @ts-ignore window.webEnv = WebEnv; // @ts-ignore diff --git a/src/web-env.ts b/src/web-env.ts index 1841175..aae2ad2 100644 --- a/src/web-env.ts +++ b/src/web-env.ts @@ -1,4 +1,5 @@ import { getPathKey } from './utils/path-key.ts'; +import { BaseLoad } from '@kevisual/load'; type GlobalEnv = { name?: string; @@ -21,27 +22,41 @@ export const useEnv = (initEnv?: GlobalEnv, initKey = 'config') => { // 从全局环境变量中获取指定的key值,如果没有则初始化一个, key不存在,返回Env对象 export const useEnvKey = (key: string, init?: () => T | null, initKey = 'config'): T => { const _env = useEnv({}, initKey); - if (key && _env[key]) { + // 已经存在,直接返回 + if (key && typeof _env[key] !== 'undefined') { return _env[key]; } + // 不存在,但是有初始化函数,初始化的返回,同步函数,删除了重新加载? if (key && init) { _env[key] = init(); return _env[key]; } - - return _env as any; -}; - -export const useEnvKeySync = async (key: string, init?: () => Promise | null, initKey = 'conifg'): Promise => { - const _env = useEnv({}, initKey); - if (key && init) { - _env[key] = await init(); - return _env[key]; - } if (key) { - return _env[key]; + // 加载 + const baseLoad = new BaseLoad(); + const voidFn = async () => { + return _env[key]; + }; + const checkFn = async () => { + const loadRes = await baseLoad.load(voidFn, { + key, + isReRun: true, + checkSuccess: () => _env[key], + timeout: 5 * 60 * 1000, + interval: 1000, + // + }); + if (loadRes.code !== 200) { + console.error('load key error'); + return null; + } + return _env[key]; + }; + return checkFn() as T; } - return _env as any; + // 不存在,没有初始化函数 + console.error('key is empty '); + return null; }; export const usePageEnv = (init?: () => {}, initKey = 'conifg') => { @@ -61,9 +76,6 @@ export const useContextKey = (key: string, init?: () => T): T => { return useEnvKey(key, init, 'context'); }; -export const useContextKeySync = async (key: string, init?: () => Promise): Promise => { - return useEnvKeySync(key, init, 'context'); -}; export const usePageContext = (init?: () => {}) => { const { id } = getPathKey(); @@ -82,10 +94,6 @@ export const useConfigKey = (key: string, init?: () => T): T => { return useEnvKey(key, init, 'config'); }; -export const useConfigKeySync = async (key: string, init?: () => Promise): Promise => { - return useEnvKeySync(key, init, 'config'); -}; - export const usePageConfig = (init?: () => {}) => { const { id } = getPathKey(); return useConfigKey(id, init); From abdc5c79239e63557635ed47291f91f38a74e1fa Mon Sep 17 00:00:00 2001 From: xion Date: Sat, 1 Mar 2025 23:14:44 +0800 Subject: [PATCH 12/15] bump --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 929f0a9..b12f965 100644 --- a/package.json +++ b/package.json @@ -33,11 +33,11 @@ "immer": "^10.1.1", "lodash-es": "^4.17.21", "nanoid": "^5.1.2", - "rollup": "^4.34.8", + "rollup": "^4.34.9", "rollup-plugin-dts": "^6.1.1", "ts-node": "^10.9.2", "tslib": "^2.8.1", - "typescript": "^5.7.3", + "typescript": "^5.8.2", "zustand": "^5.0.3" }, "publishConfig": { From 077e99cbc8350aa97ec9e5f8980923c7eaa685b4 Mon Sep 17 00:00:00 2001 From: xion Date: Mon, 3 Mar 2025 23:31:05 +0800 Subject: [PATCH 13/15] =?UTF-8?q?perf:=20app=20and=20page=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.ts | 4 ++- src/page.ts | 65 +++++++++++++++++++++++++++++++++++++------ src/utils/path-key.ts | 4 ++- src/web-env.ts | 23 ++++++++++++--- 4 files changed, 81 insertions(+), 15 deletions(-) diff --git a/src/app.ts b/src/app.ts index 4c7062f..ce7e9cb 100644 --- a/src/app.ts +++ b/src/app.ts @@ -12,8 +12,10 @@ window.useContextKey = useContextKey; window.webEnv = WebEnv; // @ts-ignore window.Load = Load; -window.Page = Page; window.QueryRouterServer = QueryRouterServer; // bind to window, 获取路由对象 useContextKey('app', () => new QueryRouterServer()); +useContextKey('page', () => { + return new Page(); +}); diff --git a/src/page.ts b/src/page.ts index adbeaac..87451a9 100644 --- a/src/page.ts +++ b/src/page.ts @@ -1,5 +1,5 @@ import { getPathKey } from '@/utils/path-key.ts'; -import { match } from 'path-to-regexp'; +import * as pathToRegexp from 'path-to-regexp'; import deepEqual from 'fast-deep-equal'; const generateRandom = () => { @@ -44,18 +44,47 @@ export class Page { basename: string; isListen: boolean; callbacks = [] as CallbackInfo[]; + ok = false; constructor(opts?: PageOptions) { const pathKey = getPathKey(); this.path = opts?.path ?? pathKey.path; this.key = opts?.key ?? pathKey.key; - this.basename = opts?.basename ?? `/${this.path}/${this.key}`; - if(this.basename.endsWith('/')) { - this.basename = this.basename.slice(0, -1); + if (opts?.basename) { + this.basename = opts?.basename; + } else { + if (this.key) { + this.basename = `/${this.path}/${this.key}`; + } else { + const location = window.location; + this.basename = location.pathname; + } } + this.clearEndSlash(); const isListen = opts?.isListen ?? true; if (isListen) { this.listen(); } + this.ok = !!this.key; + } + /** + * 清除路径的结尾斜杠,所有的最后的斜杠都删除 + */ + clearEndSlash() { + this.basename = this.basename.replace(/\/+$/, ''); + return this; + } + /** + * 检查路径 + */ + checkPath() { + const pathKey = getPathKey(); + const { path, key } = pathKey; + this.path = path || ''; + this.key = key || ''; + this.ok = !!this.key; + this.basename = `/${this.path}/${this.key}`; + this.clearEndSlash(); + return this; } popstate(event?: PopStateEvent, manualOpts?: { id?: string; type: 'singal' | 'all'; pathname?: string }) { @@ -130,8 +159,12 @@ export class Page { } const location = window.location; const _pathname = pathname || location.pathname; + if (!_pathname.includes(this.basename)) { + // console.error(`PageModule ${key} not found`); + return false; + } const cur = _pathname.replace(this.basename, ''); - const routeMatch = match(pageModule.path, { decode: decodeURIComponent }); + const routeMatch = pathToRegexp.match(pageModule.path, { decode: decodeURIComponent }); const result = routeMatch(cur); let params = {}; if (result) { @@ -148,14 +181,15 @@ export class Page { * @param opts * @returns */ - subscribe(key: string, fn?: CallFn, opts?: { pathname?: string; runImmediately?: boolean; id?: string }) { + async subscribe(key: string, fn?: CallFn, opts?: { pathname?: string; runImmediately?: boolean; id?: string }) { const runImmediately = opts?.runImmediately ?? true; // 默认立即执行 const id = opts?.id ?? generateRandom(); - const path = this.pageModule.get(key)?.path; - if (!path) { + const pageModule = this.pageModule.get(key); + if (!pageModule) { console.error(`PageModule ${key} not found`); return () => {}; } + const path = pageModule?.path || ''; this.callbacks.push({ key, fn, id: id, path }); if (runImmediately) { const location = window.location; @@ -212,7 +246,7 @@ export class Page { * 如果state 和 pathname都相等,则不执行popstate * @param path * @param state - * @param check + * @param check 是否检查, 默认检查 * @returns */ navigate(path: string | number, state?: any, check?: boolean) { @@ -255,4 +289,17 @@ export class Page { const state = window.history.state; this.popstate({ state } as any, { type: 'all' }); } + /** + * 检查路径是否匹配 + * @param path + * @param checkPath + * @returns + */ + pathMatch(regexpPath: string, checkPath: string) { + return pathToRegexp.match(regexpPath, { decode: decodeURIComponent })(checkPath); + } + pathToRegexp = pathToRegexp; + static match(regexpPath: string, checkPath: string) { + return pathToRegexp.match(regexpPath, { decode: decodeURIComponent })(checkPath); + } } diff --git a/src/utils/path-key.ts b/src/utils/path-key.ts index 76b87bc..8063da2 100644 --- a/src/utils/path-key.ts +++ b/src/utils/path-key.ts @@ -2,6 +2,8 @@ export const getPathKey = () => { // 从localtion.href的路径中,/a/b 中 a为path,b为key const pathname = location.pathname; const paths = pathname.split('/'); - const [path, key] = paths.slice(1); + let [path, key] = paths.slice(1); + path = path || ''; + key = key || ''; return { path, key, id: path + '---' + key, prefix: `/${path}/${key}` }; }; diff --git a/src/web-env.ts b/src/web-env.ts index aae2ad2..9d81e1f 100644 --- a/src/web-env.ts +++ b/src/web-env.ts @@ -63,7 +63,17 @@ export const usePageEnv = (init?: () => {}, initKey = 'conifg') => { const { id } = getPathKey(); return useEnvKey(id, init, initKey); }; - +export const useEnvKeyNew = (key: string, initKey = 'conifg', opts?: { getNew?: boolean; init?: () => {} }) => { + const _env = useEnv({}, initKey); + if (key) { + delete _env[key]; + } + if (opts?.getNew && opts.init) { + return useEnvKey(key, opts.init, initKey); + } else if (opts?.getNew) { + return useEnvKey(key, null, initKey); + } +}; type GlobalContext = { name?: string; [key: string]: any; @@ -72,11 +82,13 @@ export const useContext = (initContext?: GlobalContext) => { return useEnv(initContext, 'context'); }; -export const useContextKey = (key: string, init?: () => T): T => { +export const useContextKey = (key: string, init?: () => T, isNew?: boolean): T => { + if (isNew) { + return useEnvKeyNew(key, 'context', { getNew: true, init }); + } return useEnvKey(key, init, 'context'); }; - export const usePageContext = (init?: () => {}) => { const { id } = getPathKey(); return useContextKey(id, init); @@ -90,7 +102,10 @@ export const useConfig = (initConfig?: GlobalConfig) => { return useEnv(initConfig, 'config'); }; -export const useConfigKey = (key: string, init?: () => T): T => { +export const useConfigKey = (key: string, init?: () => T, isNew?: boolean): T => { + if (isNew) { + return useEnvKeyNew(key, 'config', { getNew: true, init }); + } return useEnvKey(key, init, 'config'); }; From 3d50b645431c87def1fcb5b5ab539c7cfbb897b1 Mon Sep 17 00:00:00 2001 From: xion Date: Thu, 6 Mar 2025 22:45:17 +0800 Subject: [PATCH 14/15] fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b12f965..fa7a114 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ } }, "dependencies": { - "@kevisual/router": "0.0.7", + "@kevisual/router": "workspace:*", "@rollup/plugin-terser": "^0.4.4", "eventemitter3": "^5.0.1", "path-to-regexp": "^8.2.0" From 02446fd60fbef2d7f04e2114b327fe7fd0b03257 Mon Sep 17 00:00:00 2001 From: xion Date: Sat, 24 May 2025 16:58:59 +0800 Subject: [PATCH 15/15] temp --- package.json | 31 +++++++++++++++---------------- src/app.ts | 39 +++++++++++++++++++++++---------------- src/web-env.ts | 31 +++++++++++++++++++++++++++---- 3 files changed, 65 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index fa7a114..daaea97 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kevisual/store", - "version": "0.0.3", + "version": "0.0.6", "main": "dist/store.js", "module": "dist/store.js", "types": "dist/store.d.ts", @@ -25,20 +25,24 @@ "devDependencies": { "@kevisual/load": "workspace:*", "@kevisual/types": "link:../types", - "@rollup/plugin-commonjs": "^28.0.2", - "@rollup/plugin-node-resolve": "^16.0.0", + "@rollup/plugin-commonjs": "^28.0.3", + "@rollup/plugin-node-resolve": "^16.0.1", "@rollup/plugin-typescript": "^12.1.2", "@types/lodash-es": "^4.17.12", "fast-deep-equal": "^3.1.3", "immer": "^10.1.1", "lodash-es": "^4.17.21", - "nanoid": "^5.1.2", - "rollup": "^4.34.9", - "rollup-plugin-dts": "^6.1.1", + "nanoid": "^5.1.5", + "rollup": "^4.41.1", + "rollup-plugin-dts": "^6.2.1", "ts-node": "^10.9.2", "tslib": "^2.8.1", - "typescript": "^5.8.2", - "zustand": "^5.0.3" + "typescript": "^5.8.3", + "zustand": "^5.0.5", + "@kevisual/router": "^0.0.21", + "@rollup/plugin-terser": "^0.4.4", + "eventemitter3": "^5.0.1", + "path-to-regexp": "^8.2.0" }, "publishConfig": { "access": "public" @@ -57,8 +61,8 @@ "require": "./dist/web-config.js" }, "./context": { - "import": "./dist/web-context.js", - "require": "./dist/web-context.js" + "import": "./dist/web-config.js", + "require": "./dist/web-config.js" }, "./page": { "import": "./dist/web-page.js", @@ -69,10 +73,5 @@ "require": "./dist/web.js" } }, - "dependencies": { - "@kevisual/router": "workspace:*", - "@rollup/plugin-terser": "^0.4.4", - "eventemitter3": "^5.0.1", - "path-to-regexp": "^8.2.0" - } + "dependencies": {} } \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index ce7e9cb..9b9c98a 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,21 +1,28 @@ // 当前的功能,把所有的模块注入到windows对象当中 import * as WebEnv from './web-env.ts'; -import { QueryRouterServer } from '@kevisual/router/browser'; import * as Load from '@kevisual/load/browser'; import { Page } from './page.ts'; -// bind to window, 必须要的获取全局的环境变量 -const { useConfigKey, useContextKey } = WebEnv; -window.useConfigKey = useConfigKey; -window.useContextKey = useContextKey; -// @ts-ignore -window.webEnv = WebEnv; -// @ts-ignore -window.Load = Load; -window.QueryRouterServer = QueryRouterServer; - -// bind to window, 获取路由对象 -useContextKey('app', () => new QueryRouterServer()); -useContextKey('page', () => { - return new Page(); -}); +export class PageInit { + static isInit = false; + static init(opts?: { load?: boolean; page?: boolean }) { + if (PageInit.isInit) { + return; + } + const { load = true, page = false } = opts || {}; + PageInit.isInit = true; + // bind to window, 必须要的获取全局的环境变量 + const { useConfigKey, useContextKey } = WebEnv; + window.useConfigKey = useConfigKey; + window.useContextKey = useContextKey; + // @ts-ignore + window.webEnv = WebEnv; + // @ts-ignore + load && (window.Load = Load); + page && + useContextKey('page', () => { + return new Page(); + }); + } +} +PageInit.init(); diff --git a/src/web-env.ts b/src/web-env.ts index 9d81e1f..0b46bd7 100644 --- a/src/web-env.ts +++ b/src/web-env.ts @@ -1,22 +1,23 @@ import { getPathKey } from './utils/path-key.ts'; import { BaseLoad } from '@kevisual/load'; +const gt = (globalThis as any) || window || self; type GlobalEnv = { name?: string; [key: string]: any; }; // 从window对象中获取全局的环境变量,如果没有则初始化一个 export const useEnv = (initEnv?: GlobalEnv, initKey = 'config') => { - const env: GlobalEnv = (window as any)[initKey]; + const env: GlobalEnv = gt[initKey]; const _env = env || initEnv; if (!env) { if (_env) { - (window as any)[initKey] = _env; + gt[initKey] = _env; } else { - (window as any)[initKey] = {}; + gt[initKey] = {}; } } - return window[initKey] as GlobalEnv; + return gt[initKey] as GlobalEnv; }; // 从全局环境变量中获取指定的key值,如果没有则初始化一个, key不存在,返回Env对象 @@ -113,3 +114,25 @@ export const usePageConfig = (init?: () => {}) => { const { id } = getPathKey(); return useConfigKey(id, init); }; + +class InitEnv { + static isInit = false; + + static init(opts?: { load?: boolean; page?: boolean }) { + if (InitEnv.isInit) { + return; + } + const { load = true, page = false } = opts || {}; + InitEnv.isInit = true; + // bind to window, 必须要的获取全局的环境变量 + // @ts-ignore + gt.useConfigKey = useConfigKey; + // @ts-ignore + gt.useContextKey = useContextKey; + // @ts-ignore + gt.webEnv = { useConfigKey, useContextKey }; + // @ts-ignore + load && (gt.Load = BaseLoad); + } +} +InitEnv.init();