Compare commits
	
		
			19 Commits
		
	
	
		
			351b2e3366
			...
			9d22faa8ba
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 9d22faa8ba | |||
| e4768b6360 | |||
| 598e29cf5a | |||
| 4152133951 | |||
| 02446fd60f | |||
| 3d50b64543 | |||
| 077e99cbc8 | |||
| abdc5c7923 | |||
| 3ee69a4f81 | |||
| 86ae2c7f70 | |||
| 3a027efb79 | |||
| d8d5073542 | |||
| b39c950ef7 | |||
| 01db9c9ea2 | |||
| c855c7d3d5 | |||
| 7cb42edc5f | |||
| 9b3de5eba3 | |||
| 3b19cd8581 | |||
| 78d581c98c | 
| @@ -1,24 +1,28 @@ | |||||||
| // console.log('index.js'); | // console.log('index.js'); | ||||||
| // //@ts-ignore | // //@ts-ignore | ||||||
| // import { Page } from '@kevisual/store/page'; | import { Page } from '@kevisual/store/page'; | ||||||
|  |  | ||||||
| // const page = new Page({ | const page = new Page({ | ||||||
| //   isListen: true, |   isListen: true, | ||||||
| // }); | }); | ||||||
| // page.addPage('/', 'home'); | page.basename = ''; | ||||||
| // page.addPage('/:id', 'user'); | page.addPage('/', 'home'); | ||||||
| // page.subscribe( | page.addPage('/:id', 'user'); | ||||||
| //   'home', | page.subscribe( | ||||||
| //   (params, state) => { |   'home', | ||||||
| //     console.log('home', params, 'state', state); |   (params, state) => { | ||||||
| //     return; |     console.log('home', params, 'state', state); | ||||||
| //   }, |     return; | ||||||
| // ); |   }, | ||||||
| // page.subscribe('user', (params, state) => { | ); | ||||||
| //   console.log('user', params, 'state', state); | page.subscribe('user', (params, state) => { | ||||||
| //   return; |   console.log('user', params, 'state', state); | ||||||
| // }); |   return; | ||||||
|  | }); | ||||||
|  |  | ||||||
| // // page.navigate('/c', { id: 3 }); | // page.navigate('/c', { id: 3 }); | ||||||
| // // page.navigate('/c', { id: 2 }); | // page.navigate('/c', { id: 2 }); | ||||||
| // // page.refresh(); | // page.refresh(); | ||||||
|  |  | ||||||
|  | // @ts-ignore | ||||||
|  | window.page = page; | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "@kevisual/store", |   "name": "@kevisual/store", | ||||||
|   "version": "0.0.4", |   "version": "0.0.8", | ||||||
|   "main": "dist/store.js", |   "main": "dist/store.js", | ||||||
|   "module": "dist/store.js", |   "module": "dist/store.js", | ||||||
|   "types": "dist/store.d.ts", |   "types": "dist/store.d.ts", | ||||||
| @@ -26,6 +26,8 @@ | |||||||
|   "license": "ISC", |   "license": "ISC", | ||||||
|   "description": "", |   "description": "", | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|  |     "@kevisual/load": "^0.0.6", | ||||||
|  |     "@kevisual/types": "link:../types", | ||||||
|     "@rollup/plugin-commonjs": "^28.0.3", |     "@rollup/plugin-commonjs": "^28.0.3", | ||||||
|     "@rollup/plugin-node-resolve": "^16.0.1", |     "@rollup/plugin-node-resolve": "^16.0.1", | ||||||
|     "@rollup/plugin-typescript": "^12.1.2", |     "@rollup/plugin-typescript": "^12.1.2", | ||||||
| @@ -34,14 +36,16 @@ | |||||||
|     "immer": "^10.1.1", |     "immer": "^10.1.1", | ||||||
|     "lodash-es": "^4.17.21", |     "lodash-es": "^4.17.21", | ||||||
|     "nanoid": "^5.1.5", |     "nanoid": "^5.1.5", | ||||||
|     "rollup": "^4.37.0", |     "rollup": "^4.41.1", | ||||||
|     "rollup-plugin-dts": "^6.2.1", |     "rollup-plugin-dts": "^6.2.1", | ||||||
|     "ts-node": "^10.9.2", |     "ts-node": "^10.9.2", | ||||||
|     "tslib": "^2.8.1", |     "tslib": "^2.8.1", | ||||||
|     "tsup": "^8.4.0", |     "typescript": "^5.8.3", | ||||||
|     "typescript": "^5.8.2", |     "zustand": "^5.0.5", | ||||||
|     "vite-plugin-dts": "^4.5.3", |     "@kevisual/router": "^0.0.21", | ||||||
|     "zustand": "^5.0.3" |     "@rollup/plugin-terser": "^0.4.4", | ||||||
|  |     "eventemitter3": "^5.0.1", | ||||||
|  |     "path-to-regexp": "^8.2.0" | ||||||
|   }, |   }, | ||||||
|   "publishConfig": { |   "publishConfig": { | ||||||
|     "access": "public" |     "access": "public" | ||||||
| @@ -60,24 +64,17 @@ | |||||||
|       "require": "./dist/web-config.js" |       "require": "./dist/web-config.js" | ||||||
|     }, |     }, | ||||||
|     "./context": { |     "./context": { | ||||||
|       "import": "./dist/web-context.js", |       "import": "./dist/web-config.js", | ||||||
|       "require": "./dist/web-context.js" |       "require": "./dist/web-config.js" | ||||||
|     }, |     }, | ||||||
|     "./web-page.js": { |     "./web-page.js": { | ||||||
|       "import": "./dist/web-page.js", |       "import": "./dist/web-page.js", | ||||||
|       "require": "./dist/web-page.js" |       "require": "./dist/web-page.js" | ||||||
|     }, |     }, | ||||||
|     "./web": { |  | ||||||
|       "import": "./dist/web.js", |  | ||||||
|       "require": "./dist/web.js" |  | ||||||
|     }, |  | ||||||
|     "./react": { |     "./react": { | ||||||
|       "import": "./dist-react/store-react.js", |       "import": "./dist-react/store-react.js", | ||||||
|       "types": "./dist-react/index.d.ts" |       "types": "./dist-react/index.d.ts" | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": {} | ||||||
|     "eventemitter3": "^5.0.1", |  | ||||||
|     "path-to-regexp": "^8.2.0" |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| @@ -4,6 +4,7 @@ import typescript from '@rollup/plugin-typescript'; | |||||||
| import resolve from '@rollup/plugin-node-resolve'; | import resolve from '@rollup/plugin-node-resolve'; | ||||||
| import commonjs from '@rollup/plugin-commonjs'; | import commonjs from '@rollup/plugin-commonjs'; | ||||||
| import { dts } from 'rollup-plugin-dts'; | import { dts } from 'rollup-plugin-dts'; | ||||||
|  | import terser from '@rollup/plugin-terser'; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @type {import('rollup').RollupOptions} |  * @type {import('rollup').RollupOptions} | ||||||
| @@ -30,7 +31,7 @@ export default [ | |||||||
|     plugins: [dts()], |     plugins: [dts()], | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     input: 'src/web-config.ts', |     input: 'src/web-env.ts', | ||||||
|     output: { |     output: { | ||||||
|       file: 'dist/web-config.js', |       file: 'dist/web-config.js', | ||||||
|       format: 'es', |       format: 'es', | ||||||
| @@ -38,29 +39,13 @@ export default [ | |||||||
|     plugins: [resolve({ browser: true }), commonjs(), typescript()], |     plugins: [resolve({ browser: true }), commonjs(), typescript()], | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     input: 'src/web-config.ts', |     input: 'src/web-env.ts', | ||||||
|     output: { |     output: { | ||||||
|       file: 'dist/web-config.d.ts', |       file: 'dist/web-config.d.ts', | ||||||
|       format: 'es', |       format: 'es', | ||||||
|     }, |     }, | ||||||
|     plugins: [dts()], |     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', |     input: 'src/page.ts', | ||||||
|     output: { |     output: { | ||||||
| @@ -77,20 +62,4 @@ export default [ | |||||||
|     }, |     }, | ||||||
|     plugins: [dts()], |     plugins: [dts()], | ||||||
|   }, |   }, | ||||||
|   { |  | ||||||
|     input: 'src/web.ts', |  | ||||||
|     output: { |  | ||||||
|       file: 'dist/web.js', |  | ||||||
|       format: 'es', |  | ||||||
|     }, |  | ||||||
|     plugins: [resolve({ browser: true }), commonjs(), typescript()], |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     input: 'src/web.ts', |  | ||||||
|     output: { |  | ||||||
|       file: 'dist/web.d.ts', |  | ||||||
|       format: 'es', |  | ||||||
|     }, |  | ||||||
|     plugins: [dts()], |  | ||||||
|   }, |  | ||||||
| ]; | ]; | ||||||
|   | |||||||
							
								
								
									
										143
									
								
								src/page.ts
									
									
									
									
									
								
							
							
						
						
									
										143
									
								
								src/page.ts
									
									
									
									
									
								
							| @@ -1,10 +1,23 @@ | |||||||
| import { getPathKey } from '@/utils/path-key.ts'; | 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'; | import deepEqual from 'fast-deep-equal'; | ||||||
|  |  | ||||||
|  | const generateRandom = () => { | ||||||
|  |   // return Math.random().toString(36).substring(8); | ||||||
|  |   return crypto.randomUUID(); | ||||||
|  | }; | ||||||
| type PageOptions = { | type PageOptions = { | ||||||
|  |   /** | ||||||
|  |    * 路径 | ||||||
|  |    */ | ||||||
|   path?: string; |   path?: string; | ||||||
|  |   /** | ||||||
|  |    * key | ||||||
|  |    */ | ||||||
|   key?: string; |   key?: string; | ||||||
|  |   /** | ||||||
|  |    * basename | ||||||
|  |    */ | ||||||
|   basename?: string; |   basename?: string; | ||||||
|   isListen?: boolean; |   isListen?: boolean; | ||||||
| }; | }; | ||||||
| @@ -19,6 +32,9 @@ type CallbackInfo = { | |||||||
|   fn: CallFn; |   fn: CallFn; | ||||||
|   id: string; |   id: string; | ||||||
| } & PageModule; | } & PageModule; | ||||||
|  | /** | ||||||
|  |  * 根据basename,其中的path和key,来判断一个应用的模块。 | ||||||
|  |  */ | ||||||
| export class Page { | export class Page { | ||||||
|   pageModule = new Map<string, PageModule>(); |   pageModule = new Map<string, PageModule>(); | ||||||
|   // pathname的第一个路径 |   // pathname的第一个路径 | ||||||
| @@ -28,18 +44,51 @@ export class Page { | |||||||
|   basename: string; |   basename: string; | ||||||
|   isListen: boolean; |   isListen: boolean; | ||||||
|   callbacks = [] as CallbackInfo[]; |   callbacks = [] as CallbackInfo[]; | ||||||
|  |   ok = false; | ||||||
|   constructor(opts?: PageOptions) { |   constructor(opts?: PageOptions) { | ||||||
|     const pathKey = getPathKey(); |     const pathKey = getPathKey(); | ||||||
|     this.path = opts?.path ?? pathKey.path; |     this.path = opts?.path ?? pathKey.path; | ||||||
|     this.key = opts?.key ?? pathKey.key; |     this.key = opts?.key ?? pathKey.key; | ||||||
|     this.basename = opts?.basename ?? `/${this.path}/${this.key}`; |     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; |     const isListen = opts?.isListen ?? true; | ||||||
|     if (isListen) { |     if (isListen) { | ||||||
|       this.listen(); |       this.listen(); | ||||||
|     } |     } | ||||||
|  |     this.ok = !!this.key; | ||||||
|   } |   } | ||||||
|   popstate(event?: PopStateEvent, manualOpts?: { id?: string; type: 'singal' | 'all' }) { |   /** | ||||||
|     const pathname = window.location.pathname; |    * 清除路径的结尾斜杠,所有的最后的斜杠都删除 | ||||||
|  |    */ | ||||||
|  |   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 }) { | ||||||
|  |     const pathname = manualOpts?.pathname ?? window.location.pathname; | ||||||
|     if (manualOpts) { |     if (manualOpts) { | ||||||
|       if (manualOpts.type === 'singal') { |       if (manualOpts.type === 'singal') { | ||||||
|         const item = this.callbacks.find((item) => item.id === manualOpts.id); |         const item = this.callbacks.find((item) => item.id === manualOpts.id); | ||||||
| @@ -56,7 +105,17 @@ export class Page { | |||||||
|       result && item.fn?.(result.params, event.state, { event, manualOpts }); |       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() { |   listen() { | ||||||
|  |     if (this.isListen) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|     this.isListen = true; |     this.isListen = true; | ||||||
|     window.addEventListener('popstate', this.popstate.bind(this), false); |     window.addEventListener('popstate', this.popstate.bind(this), false); | ||||||
|   } |   } | ||||||
| @@ -83,7 +142,7 @@ export class Page { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|   /** |   /** | ||||||
|    * 检查当前路径是否匹配 |    * 检查当前路径是否匹配, 如何提交pathname,则检查pathname | ||||||
|    * @param key |    * @param key | ||||||
|    * @param pathname |    * @param pathname | ||||||
|    * @returns |    * @returns | ||||||
| @@ -100,8 +159,12 @@ export class Page { | |||||||
|     } |     } | ||||||
|     const location = window.location; |     const location = window.location; | ||||||
|     const _pathname = pathname || location.pathname; |     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 cur = _pathname.replace(this.basename, ''); | ||||||
|     const routeMatch = match(pageModule.path, { decode: decodeURIComponent }); |     const routeMatch = pathToRegexp.match(pageModule.path, { decode: decodeURIComponent }); | ||||||
|     const result = routeMatch(cur); |     const result = routeMatch(cur); | ||||||
|     let params = {}; |     let params = {}; | ||||||
|     if (result) { |     if (result) { | ||||||
| @@ -118,20 +181,22 @@ export class Page { | |||||||
|    * @param opts |    * @param opts | ||||||
|    * @returns |    * @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 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; |     const pageModule = this.pageModule.get(key); | ||||||
|     if (!path) { |     if (!pageModule) { | ||||||
|       console.error(`PageModule ${key} not found`); |       console.error(`PageModule ${key} not found`); | ||||||
|       return () => {}; |       return () => {}; | ||||||
|     } |     } | ||||||
|  |     const path = pageModule?.path || ''; | ||||||
|     this.callbacks.push({ key, fn, id: id, path }); |     this.callbacks.push({ key, fn, id: id, path }); | ||||||
|     if (runImmediately) { |     if (runImmediately) { | ||||||
|       const location = window.location; |       const location = window.location; | ||||||
|       const pathname = opts?.pathname ?? location.pathname; |       const pathname = opts?.pathname ?? location.pathname; | ||||||
|       const result = this.check(key, pathname); |       const result = this.check(key, pathname); | ||||||
|       if (result) { |       if (result) { | ||||||
|  |         // 如果是手动调用,则不需要检查是否相等,直接执行,而且是执行当前的subscribe的函数 | ||||||
|         this.popstate({ state: window.history.state } as any, { id, type: 'singal' }); |         this.popstate({ state: window.history.state } as any, { id, type: 'singal' }); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| @@ -139,6 +204,9 @@ export class Page { | |||||||
|       this.callbacks = this.callbacks.filter((item) => item.id !== id); |       this.callbacks = this.callbacks.filter((item) => item.id !== id); | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
|  |   getPathKey() { | ||||||
|  |     return getPathKey(); | ||||||
|  |   } | ||||||
|   /** |   /** | ||||||
|    * 返回当前路径,不包含basename |    * 返回当前路径,不包含basename | ||||||
|    * @returns |    * @returns | ||||||
| @@ -151,12 +219,26 @@ export class Page { | |||||||
|   getAppPath() { |   getAppPath() { | ||||||
|     return this.path; |     return this.path; | ||||||
|   } |   } | ||||||
|  |   /** | ||||||
|  |    * 返回当前key | ||||||
|  |    * @returns | ||||||
|  |    */ | ||||||
|   getAppKey() { |   getAppKey() { | ||||||
|     return this.key; |     return this.key; | ||||||
|   } |   } | ||||||
|  |   /** | ||||||
|  |    * 解码路径 | ||||||
|  |    * @param path | ||||||
|  |    * @returns | ||||||
|  |    */ | ||||||
|   decodePath(path: string) { |   decodePath(path: string) { | ||||||
|     return decodeURIComponent(path); |     return decodeURIComponent(path); | ||||||
|   } |   } | ||||||
|  |   /** | ||||||
|  |    * 编码路径 | ||||||
|  |    * @param path | ||||||
|  |    * @returns | ||||||
|  |    */ | ||||||
|   encodePath(path: string) { |   encodePath(path: string) { | ||||||
|     return encodeURIComponent(path); |     return encodeURIComponent(path); | ||||||
|   } |   } | ||||||
| @@ -164,7 +246,7 @@ export class Page { | |||||||
|    * 如果state 和 pathname都相等,则不执行popstate |    * 如果state 和 pathname都相等,则不执行popstate | ||||||
|    * @param path |    * @param path | ||||||
|    * @param state |    * @param state | ||||||
|    * @param check |    * @param check 是否检查, 默认检查 | ||||||
|    * @returns |    * @returns | ||||||
|    */ |    */ | ||||||
|   navigate(path: string | number, state?: any, check?: boolean) { |   navigate(path: string | number, state?: any, check?: boolean) { | ||||||
| @@ -187,17 +269,45 @@ export class Page { | |||||||
|       this.popstate({ state } as any, { type: 'all' }); |       this.popstate({ state } as any, { type: 'all' }); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |   /** | ||||||
|  |    * 替换当前路径 | ||||||
|  |    * @param path | ||||||
|  |    * @param state | ||||||
|  |    * @param check | ||||||
|  |    */ | ||||||
|   replace(path: string, state?: any, check?: boolean) { |   replace(path: string, state?: any, check?: boolean) { | ||||||
|     let _check = check ?? true; |     let _check = check ?? true; | ||||||
|     window.history.replaceState(state, '', this.basename + path); |     let newPath = this.basename + path; | ||||||
|  |     if (path.startsWith('http')) { | ||||||
|  |       const url = new URL(path); | ||||||
|  |       const origin = url.origin; | ||||||
|  |       newPath = url.toString().replace(origin, ''); | ||||||
|  |     } | ||||||
|  |     window.history.replaceState(state, '', newPath); | ||||||
|     if (_check) { |     if (_check) { | ||||||
|       this.popstate({ state } as any, { type: 'all' }); |       this.popstate({ state } as any, { type: 'all' }); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |   /** | ||||||
|  |    * 刷新当前页面 | ||||||
|  |    */ | ||||||
|   refresh() { |   refresh() { | ||||||
|     const state = window.history.state; |     const state = window.history.state; | ||||||
|     this.popstate({ state } as any, { type: 'all' }); |     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); | ||||||
|  |   } | ||||||
| } | } | ||||||
| /** | /** | ||||||
|  * 获取history state |  * 获取history state | ||||||
| @@ -213,17 +323,16 @@ export const getHistoryState = <T = Record<string, any>>() => { | |||||||
|  * 设置history state |  * 设置history state | ||||||
|  * @param state |  * @param state | ||||||
|  */ |  */ | ||||||
| export const setHistoryState = (state: any) => { | export const setHistoryState = (state: any, url?: string) => { | ||||||
|   const history = window.history; |   const history = window.history; | ||||||
|   const oldState = getHistoryState(); |   const oldState = getHistoryState(); | ||||||
|   // 只更新 state 而不改变 URL |   history.replaceState({ ...oldState, ...state }, '', url || window.location.href); | ||||||
|   history.replaceState({ ...oldState, ...state }, '', window.location.href); |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 清除history state |  * 清除history state | ||||||
|  */ |  */ | ||||||
| export const clearHistoryState = () => { | export const clearHistoryState = (url?: string) => { | ||||||
|   const history = window.history; |   const history = window.history; | ||||||
|   history.replaceState({}, '', window.location.href); |   history.replaceState({}, '', url || window.location.href); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -2,6 +2,8 @@ export const getPathKey = () => { | |||||||
|   // 从localtion.href的路径中,/a/b 中 a为path,b为key |   // 从localtion.href的路径中,/a/b 中 a为path,b为key | ||||||
|   const pathname = location.pathname; |   const pathname = location.pathname; | ||||||
|   const paths = pathname.split('/'); |   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}` }; |   return { path, key, id: path + '---' + key, prefix: `/${path}/${key}` }; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -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 = <T>(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 <T = any>(key: string, init?: () => Promise<T>): Promise<T> => { |  | ||||||
|   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); |  | ||||||
| }; |  | ||||||
| @@ -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 = <T>(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 <T = any>(key: string, init?: () => Promise<T>): Promise<T> => { |  | ||||||
|   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); |  | ||||||
| }; |  | ||||||
							
								
								
									
										138
									
								
								src/web-env.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/web-env.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,138 @@ | |||||||
|  | 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 = gt[initKey]; | ||||||
|  |   const _env = env || initEnv; | ||||||
|  |   if (!env) { | ||||||
|  |     if (_env) { | ||||||
|  |       gt[initKey] = _env; | ||||||
|  |     } else { | ||||||
|  |       gt[initKey] = {}; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return gt[initKey] as GlobalEnv; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // 从全局环境变量中获取指定的key值,如果没有则初始化一个, key不存在,返回Env对象 | ||||||
|  | export const useEnvKey = <T = any>(key: string, init?: () => T | null, initKey = 'config'): T => { | ||||||
|  |   const _env = useEnv({}, initKey); | ||||||
|  |   // 已经存在,直接返回 | ||||||
|  |   if (key && typeof _env[key] !== 'undefined') { | ||||||
|  |     return _env[key]; | ||||||
|  |   } | ||||||
|  |   // 不存在,但是有初始化函数,初始化的返回,同步函数,删除了重新加载? | ||||||
|  |   if (key && init) { | ||||||
|  |     _env[key] = init(); | ||||||
|  |     return _env[key]; | ||||||
|  |   } | ||||||
|  |   if (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; | ||||||
|  |   } | ||||||
|  |   // 不存在,没有初始化函数 | ||||||
|  |   console.error('key is empty '); | ||||||
|  |   return null; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | }; | ||||||
|  | export const useContext = (initContext?: GlobalContext) => { | ||||||
|  |   return useEnv(initContext, 'context'); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const useContextKey = <T = any>(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); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | type GlobalConfig = { | ||||||
|  |   name?: string; | ||||||
|  |   [key: string]: any; | ||||||
|  | }; | ||||||
|  | export const useConfig = (initConfig?: GlobalConfig) => { | ||||||
|  |   return useEnv(initConfig, 'config'); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const useConfigKey = <T = any>(key: string, init?: () => T, isNew?: boolean): T => { | ||||||
|  |   if (isNew) { | ||||||
|  |     return useEnvKeyNew(key, 'config', { getNew: true, init }); | ||||||
|  |   } | ||||||
|  |   return useEnvKey(key, init, 'config'); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | 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(); | ||||||
| @@ -1,8 +0,0 @@ | |||||||
| export * from './page.ts'; |  | ||||||
| export * from './web-context.ts'; |  | ||||||
| export * from './web-config.ts'; |  | ||||||
|  |  | ||||||
| export * from 'nanoid'; |  | ||||||
| export * from 'path-to-regexp'; |  | ||||||
|  |  | ||||||
| export * from 'eventemitter3'; |  | ||||||
| @@ -10,6 +10,7 @@ | |||||||
|     "baseUrl": "./", |     "baseUrl": "./", | ||||||
|     "typeRoots": [ |     "typeRoots": [ | ||||||
|       "node_modules/@types", |       "node_modules/@types", | ||||||
|  |       "node_modules/@kevisual/types", | ||||||
|     ], |     ], | ||||||
|     "declaration": false, |     "declaration": false, | ||||||
|     "noEmit": true, |     "noEmit": true, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user