Compare commits
	
		
			21 Commits 
		
	
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 092aaff8b1 | |
|  | f30eac2195 | |
|  | a9ff9f0323 | |
|  | d3a00a319f | |
|  | 37dcc51e05 | |
|  | a1c1740166 | |
|  | b7567a15fc | |
|  | ba92054633 | |
|  | a24ab67694 | |
|  | 9c9561cb1c | |
|  | fc20ea24d4 | |
|  | 137c79fce7 | |
|  | 5136605322 | |
|  | 1e08003ba7 | |
|  | d68ecadb4e | |
|  | e38f1c51dc | |
|  | 66971278a2 | |
|  | 1cddfb6731 | |
|  | 5e9dd986df | |
|  | d1e81474c0 | |
|  | f46b35f9bf | 
							
								
								
									
										14
									
								
								Readme.md
								
								
								
								
							
							
						
						
									
										14
									
								
								Readme.md
								
								
								
								
							|  | @ -5,7 +5,7 @@ | ||||||
|  |  | ||||||
| 
 | 
 | ||||||
| ### 开发文档 | ### 开发文档 | ||||||
| [开发文档](https://github.com/bytedo/wkitd/wiki) | [开发文档](https://git.wkit.fun/bytedo/wkitd/wiki) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ### 我们的特色 | ### 我们的特色 | ||||||
|  | @ -19,8 +19,9 @@ | ||||||
| 
 | 
 | ||||||
| - 路由不支持嵌套, 即`<router-view></router-view>`只能出现`1`次。 | - 路由不支持嵌套, 即`<router-view></router-view>`只能出现`1`次。 | ||||||
| - `$router`对象, 只注入到使用`wkit`创建的组件, 其他地方可以使用`getRouter()`获取`$router`对象。 | - `$router`对象, 只注入到使用`wkit`创建的组件, 其他地方可以使用`getRouter()`获取`$router`对象。 | ||||||
| - 所有路由页面和组件, 均可使用`getCurrentPage()`获取当前的页面的信息; 也可以用`$router.route`获取。  | - 所有路由页面和组件, 均可使用`getCurrentPage()`获取当前的页面的信息; 也可以用`$route`或`$router.route`获取。  | ||||||
| - `$store`对象, 只注入到使用`wkit`创建的组件, 其他组件可使用`getStore()`获取。 | - `$store`对象, 只注入到使用`wkit`创建的组件, 其他组件可使用`getStore()`获取。 | ||||||
|  | - `watch()`方法, 可用于监听`$store`和`$route`的变化。 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -91,10 +92,11 @@ index.html | ||||||
|   <script type="importmap"> |   <script type="importmap"> | ||||||
|     { |     { | ||||||
|       "imports":{ |       "imports":{ | ||||||
|         "es.shim":"https://jscdn.ink/lib/es.shim.js", |         "es.shim":"//jscdn.ink/es.shim/latest/index.js", | ||||||
|         "wkit":"https://jscdn.ink/lib/wkit.js", |         "wkit":"//jscdn.ink/wkit/latest/index.js", | ||||||
|         "fetch":"https://jscdn.ink/lib/fetch.js", |         "wkitd":"//jscdn.ink/wkitd/latest/index.js", | ||||||
|         "@bd/ui/":"https://jscdn.ink/@bd/ui/latest/" |         "fetch":"//jscdn.ink/@bytedo/fetch/latest/next.js", | ||||||
|  |         "@bd/ui/":"//jscdn.ink/@bd/ui/latest/" | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   </script> |   </script> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| { | { | ||||||
|   "name": "wkitd", |   "name": "wkitd", | ||||||
|   "version": "1.1.1", |   "version": "1.3.12", | ||||||
|   "type": "module", |   "type": "module", | ||||||
|   "main": "dist/index.js", |   "main": "dist/index.js", | ||||||
|   "files": [ |   "files": [ | ||||||
|  | @ -13,6 +13,6 @@ | ||||||
|   }, |   }, | ||||||
|   "repository": { |   "repository": { | ||||||
|     "type": "git", |     "type": "git", | ||||||
|     "url": "git+https://github.com/bytedo/wkitd.git" |     "url": "git+https://git.wkit.fun/bytedo/wkitd.git" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,3 +8,5 @@ export const __ROUTER__ = Symbol('router') | ||||||
| export const __ROUTER_VIEW__ = Symbol('router-view') | export const __ROUTER_VIEW__ = Symbol('router-view') | ||||||
| export const __STORE__ = Symbol('store') | export const __STORE__ = Symbol('store') | ||||||
| export const WKITD_COMPONENTS = new Set() | export const WKITD_COMPONENTS = new Set() | ||||||
|  | export const STORE_CALLBACKS = new Map() | ||||||
|  | export const ROUTE_CALLBACKS = new Set() | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ import { noop, readonlyProp } from './utils.js' | ||||||
| import { __ROUTER__, __STORE__, __ROUTER_VIEW__ } from './constants.js' | import { __ROUTER__, __STORE__, __ROUTER_VIEW__ } from './constants.js' | ||||||
| 
 | 
 | ||||||
| export * from './router/index.js' | export * from './router/index.js' | ||||||
| export { createStore } from './store.js' | export { createStore, watch } from './store.js' | ||||||
| 
 | 
 | ||||||
| class App extends Component {} | class App extends Component {} | ||||||
| 
 | 
 | ||||||
|  | @ -92,7 +92,7 @@ export function createApp({ | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       App.reg('app') |       App.reg('app', 'v') | ||||||
|     } |     } | ||||||
|   })() |   })() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -13,20 +13,26 @@ class Wkitd extends WeakMap { | ||||||
|    */ |    */ | ||||||
|   broadcast() { |   broadcast() { | ||||||
|     for (let it of WKITD_COMPONENTS) { |     for (let it of WKITD_COMPONENTS) { | ||||||
|  |       if (it.removed) { | ||||||
|  |         this.deassign(it) | ||||||
|  |         continue | ||||||
|  |       } | ||||||
|       it.$requestUpdate() |       it.$requestUpdate() | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|   /** |   /** | ||||||
|    * 注册缓存组件 |    * 注册缓存组件 | ||||||
|    */ |    */ | ||||||
|   assign(target) { |   assign(target) { | ||||||
|     WKITD_COMPONENTS.add(target) |     WKITD_COMPONENTS.add(target) | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|   /** |   /** | ||||||
|    * 取消注册 |    * 取消注册 | ||||||
|    */ |    */ | ||||||
|   deassign(target) { |   deassign(target) { | ||||||
|     WKITD_COMPONENTS.add(target) |     WKITD_COMPONENTS.delete(target) | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,12 +22,13 @@ export function createRouter({ | ||||||
|   function wrapper() { |   function wrapper() { | ||||||
|     Object.defineProperty(Component.prototype, '$router', { |     Object.defineProperty(Component.prototype, '$router', { | ||||||
|       get() { |       get() { | ||||||
|         return window.wkitd.get(__ROUTER__) |         return $router | ||||||
|       }, |       } | ||||||
|       set(val) { |     }) | ||||||
|         console.error('Can not set readonly property $router of Component') |     Object.defineProperty(Component.prototype, '$route', { | ||||||
|       }, |       get() { | ||||||
|       enumerable: false |         return $router.route | ||||||
|  |       } | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
|   wrapper.beforeEach = $router.beforeEach.bind($router) |   wrapper.beforeEach = $router.beforeEach.bind($router) | ||||||
|  |  | ||||||
|  | @ -1,27 +1,12 @@ | ||||||
| //
 | //
 | ||||||
| import { Component, html, css, raw } from 'wkit' | import { Component, html, css, raw } from 'wkit' | ||||||
| import { object2query } from '../utils.js' | import { object2query, query2object } from '../utils.js' | ||||||
| import { __ROUTER_VIEW__ } from '../constants.js' | import { __ROUTER_VIEW__, ROUTE_CALLBACKS } from '../constants.js' | ||||||
|  | import { watch } from '../store.js' | ||||||
| 
 | 
 | ||||||
| class RouterView extends Component { | class RouterView extends Component { | ||||||
|   static props = { |   static props = { | ||||||
|     keepAlive: false, |     transition: false | ||||||
|     transition: false, |  | ||||||
|     current: { |  | ||||||
|       type: String, |  | ||||||
|       default: '', |  | ||||||
|       attribute: false, |  | ||||||
|       observer(v, old) { |  | ||||||
|         if (this.keepAlive && v) { |  | ||||||
|           if (old && this.$refs[old]) { |  | ||||||
|             this.$refs[old].deactivated() |  | ||||||
|           } |  | ||||||
|           this.$refs[v]?.$requestUpdate() |  | ||||||
|           this.$refs[v]?.$animate() |  | ||||||
|           this.$refs[v]?.activated() |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   static styles = css` |   static styles = css` | ||||||
|  | @ -30,6 +15,49 @@ class RouterView extends Component { | ||||||
|     } |     } | ||||||
|   ` |   ` | ||||||
| 
 | 
 | ||||||
|  |   get current() { | ||||||
|  |     return this.#current | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   set current(v) { | ||||||
|  |     let old = this.#current | ||||||
|  |     let $refs = this.$refs | ||||||
|  |     this.#current = v | ||||||
|  | 
 | ||||||
|  |     if (this.keepAlive) { | ||||||
|  |       if (old) { | ||||||
|  |         let $old = $refs[old] | ||||||
|  |         if ($old) { | ||||||
|  |           $old.removed = true | ||||||
|  |           $old.deactivated() | ||||||
|  |           $old.remove() | ||||||
|  |         } else { | ||||||
|  |           this.$requestUpdate() | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         this.$requestUpdate() | ||||||
|  |       } | ||||||
|  |       if (v) { | ||||||
|  |         let $new = $refs[v] | ||||||
|  |         if ($new) { | ||||||
|  |           this.root.appendChild($new) | ||||||
|  |           $new.$requestUpdate() | ||||||
|  |           if (this.transition) { | ||||||
|  |             $new.$animate() | ||||||
|  |           } | ||||||
|  |           $new.removed = false | ||||||
|  |           $new.activated() | ||||||
|  |         } else { | ||||||
|  |           this.$requestUpdate() | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       this.$requestUpdate() | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   #current = '' | ||||||
|  | 
 | ||||||
|   #views = [] |   #views = [] | ||||||
| 
 | 
 | ||||||
|   created() { |   created() { | ||||||
|  | @ -48,38 +76,29 @@ class RouterView extends Component { | ||||||
|         { transform: 'translateX(0)', opacity: 1 } |         { transform: 'translateX(0)', opacity: 1 } | ||||||
|       ] |       ] | ||||||
|     } |     } | ||||||
|     if (this.keepAlive) { |     let name = this.current | ||||||
|       let template = this.#views.map(it => [ |  | ||||||
|         this.transition |  | ||||||
|           ? `<${it} ref="${it}" :__keep_alive__="%s" #animation="%s" style="%s"></${it}>` |  | ||||||
|           : `<${it} ref="${it}" :__keep_alive__="%s" style=%s></${it}>`, |  | ||||||
|         [ |  | ||||||
|           this.current === it, |  | ||||||
|           { ...option, immediate: this.current === it }, |  | ||||||
|           this.current === it ? '' : 'display:none' |  | ||||||
|         ] |  | ||||||
|       ]) |  | ||||||
| 
 | 
 | ||||||
|       return raw( |     if (name) { | ||||||
|         template.map(it => it[0]).join(''), |       if (this.transition) { | ||||||
|         template.map(it => it[1]).flat() |         return raw( | ||||||
|       ) |           `<${name} ref="${name}" ${ | ||||||
|     } else { |             this.keepAlive ? 'keep-alive' : '' | ||||||
|       if (this.current) { |           } #animation="%s"></${name}>`, | ||||||
|         if (this.transition) { |           [option] | ||||||
|           return raw(`<${this.current} #animation="%s"></${this.current}>`, [ |         ) | ||||||
|             option |  | ||||||
|           ]) |  | ||||||
|         } |  | ||||||
|         return raw(`<${this.current}></${this.current}>`) |  | ||||||
|       } |       } | ||||||
|  |       return raw( | ||||||
|  |         `<${name} ref="${name}" ${ | ||||||
|  |           this.keepAlive ? 'keep-alive' : '' | ||||||
|  |         }></${name}>` | ||||||
|  |       ) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class RouterLink extends Component { | class RouterLink extends Component { | ||||||
|   static props = { |   static props = { | ||||||
|     to: Object, |     to: { type: null }, | ||||||
|     disabled: false |     disabled: false | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -93,6 +112,8 @@ class RouterLink extends Component { | ||||||
|     a { |     a { | ||||||
|       display: flex; |       display: flex; | ||||||
|       align-items: center; |       align-items: center; | ||||||
|  |       justify-content: center; | ||||||
|  |       gap: var(--router-link-gap, 0); | ||||||
|       width: 100%; |       width: 100%; | ||||||
|       height: 100%; |       height: 100%; | ||||||
|       color: inherit; |       color: inherit; | ||||||
|  | @ -105,12 +126,12 @@ class RouterLink extends Component { | ||||||
|       cursor: not-allowed; |       cursor: not-allowed; | ||||||
|     } |     } | ||||||
|   ` |   ` | ||||||
| 
 |   #to = { path: '' } | ||||||
|   #href = '' |   #href = '' | ||||||
| 
 | 
 | ||||||
|   #navigate() { |   #navigate() { | ||||||
|     let type = this.$router.type |     let type = this.$router.type | ||||||
|     let { path } = this.to | 
 | ||||||
|     if (this.disabled) { |     if (this.disabled) { | ||||||
|       return |       return | ||||||
|     } |     } | ||||||
|  | @ -118,45 +139,62 @@ class RouterLink extends Component { | ||||||
|     if (type === 'hash') { |     if (type === 'hash') { | ||||||
|       location.hash = this.#href |       location.hash = this.#href | ||||||
|     } else { |     } else { | ||||||
|       this.$router.push(this.to) |       this.$router.push(this.#to) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   #parsePath() { |   #parsePath() { | ||||||
|     let type = this.$router.type |     let path, query, params | ||||||
|     let { path = '', query = {} } = this.to |     if (typeof this.to === 'string') { | ||||||
|     let params = |       let tmp = this.to.split('?') | ||||||
|       typeof query === 'string' |       path = tmp[0] | ||||||
|         ? query.replaceAll('?', '') |       params = tmp[1] || '' | ||||||
|         : object2query(query) |       query = query2object(params) | ||||||
|  |     } else { | ||||||
|  |       path = this.to.path || '' | ||||||
|  |       query = this.to.query || {} | ||||||
|  |       params = | ||||||
|  |         typeof query === 'string' | ||||||
|  |           ? query.replaceAll('?', '') | ||||||
|  |           : object2query(query) | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     path = path.replace(/^\//, '') |     path = '/' + path.replace(/^\/+/, '') | ||||||
|  |     this.#to = { path, query } | ||||||
| 
 | 
 | ||||||
|     if (params) { |     if (params) { | ||||||
|       path += '?' + params |       path += '?' + params | ||||||
|     } |     } | ||||||
|  |     this.#href = path | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|     return '/' + path |   activated() { | ||||||
|  |     this.mounted() | ||||||
|  |   } | ||||||
|  |   deactivated() { | ||||||
|  |     this.unmounted() | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   mounted() { |   mounted() { | ||||||
|     this.$router.rsync(this, route => { |     watch('$route', route => { | ||||||
|       this.classList.toggle('active', route.path === this.to.path) |       if (this.removed) { | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |       this.classList.toggle('active', route.path === this.#to.path) | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   unmounted() { | ||||||
|  |     ROUTE_CALLBACKS.delete(this) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   render() { |   render() { | ||||||
|     this.#href = this.#parsePath() |     this.#parsePath() | ||||||
|     return html`<a title=${this.#href} @click=${this.#navigate}>
 |     return html`<a title=${this.#href} @click=${this.#navigate} | ||||||
|       <slot></slot |       ><slot></slot | ||||||
|     ></a>` |     ></a>` | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| if (!customElements.get('router-view')) { | RouterView.reg('view', 'router') | ||||||
|   customElements.define('router-view', RouterView) | RouterLink.reg('link', 'router') | ||||||
| } |  | ||||||
| 
 |  | ||||||
| if (!customElements.get('router-link')) { |  | ||||||
|   customElements.define('router-link', RouterLink) |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -6,11 +6,11 @@ | ||||||
|  */ |  */ | ||||||
| import { bind, fire } from 'wkit' | import { bind, fire } from 'wkit' | ||||||
| import { noop, query2object, object2query } from '../utils.js' | import { noop, query2object, object2query } from '../utils.js' | ||||||
| import { __ROUTER_VIEW__ } from '../constants.js' | import { __ROUTER_VIEW__, ROUTE_CALLBACKS } from '../constants.js' | ||||||
| 
 | 
 | ||||||
| //hash前缀正则
 | //hash前缀正则
 | ||||||
| const PREFIX_REGEXP = /^(#!|#)[\/]+?/ | const PREFIX_REGEXP = /^(#!|#)[\/]+?/ | ||||||
| const RULE_REGEXP = /(\/[^/]*)(:[A-Za-z0-9_]+)(\?)?/g | const RULE_REGEXP = /(\/[^/]*)(:[\$@~\\!A-Za-z0-9_=\-]+)(\?)?/g | ||||||
| 
 | 
 | ||||||
| const MODE_HASH = 'hash' | const MODE_HASH = 'hash' | ||||||
| const MODE_HISTORY = 'history' | const MODE_HISTORY = 'history' | ||||||
|  | @ -21,10 +21,9 @@ class Router { | ||||||
|   #tables = new Map() |   #tables = new Map() | ||||||
|   #views = new Set() |   #views = new Set() | ||||||
| 
 | 
 | ||||||
|   #targets = new Map() |  | ||||||
| 
 |  | ||||||
|   #ready = false |   #ready = false | ||||||
|   #route = Object.create(null) |   #route = Object.create(null) | ||||||
|  |   #tmp = null | ||||||
| 
 | 
 | ||||||
|   #beforeEach |   #beforeEach | ||||||
| 
 | 
 | ||||||
|  | @ -66,12 +65,12 @@ class Router { | ||||||
| 
 | 
 | ||||||
|       re = route.path.replace( |       re = route.path.replace( | ||||||
|         RULE_REGEXP, |         RULE_REGEXP, | ||||||
|         function (m, _prefix, _var, _require) { |         function (m, _prefix, _var, _require = '') { | ||||||
|           vars.push(_var.slice(1)) |           vars.push(_var.slice(1)) | ||||||
|           if (_prefix === '/') { |           if (_prefix === '/') { | ||||||
|             _prefix = '/?' |             _prefix = '/?' | ||||||
|           } |           } | ||||||
|           return _prefix + '([A-Za-z0-9_]+)' + _require |           return _prefix + '([\\$\\!@~A-Za-z0-9_=\\-]+)' + _require | ||||||
|         } |         } | ||||||
|       ) |       ) | ||||||
| 
 | 
 | ||||||
|  | @ -101,14 +100,23 @@ class Router { | ||||||
|     let path = isHash |     let path = isHash | ||||||
|       ? hash |       ? hash | ||||||
|       : location.href.replace(location.origin, '').replace(hash, '') |       : location.href.replace(location.origin, '').replace(hash, '') | ||||||
|     let query |     let query = '' | ||||||
| 
 | 
 | ||||||
|     if (path.includes('?')) { |     if (path.includes('?')) { | ||||||
|       ;[path, query] = path.split('?') |       ;[path, query] = path.split('?') | ||||||
|     } |     } | ||||||
|     path = path.replace(PREFIX_REGEXP, '/') |     path = path.replace(PREFIX_REGEXP, '/') | ||||||
|  |     // 修正默认主页,以支持带路径访问的首页
 | ||||||
|  |     if (path === '/index.html') { | ||||||
|  |       path = '/' | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if (!$view || path === this.#route.path) { |     if (!$view || path === this.#route.path) { | ||||||
|  |       // query不同, 只更新query
 | ||||||
|  |       if (query !== object2query(this.#route.query)) { | ||||||
|  |         this.#route.query = query2object(query) | ||||||
|  |         return this.#broadcast() | ||||||
|  |       } | ||||||
|       return |       return | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -125,12 +133,12 @@ class Router { | ||||||
|           params, |           params, | ||||||
|           query: query2object(query) |           query: query2object(query) | ||||||
|         } |         } | ||||||
|  |         Object.defineProperty(next, 'raw', { value: route.path }) | ||||||
|         if (this.#beforeEach) { |         if (this.#beforeEach) { | ||||||
|           return this.#beforeEach(this.route, next, () => { |           return this.#beforeEach(this.route, next, () => { | ||||||
|             this.#exec(next) |             this.#exec(next) | ||||||
|           }) |           }) | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         return this.#exec(next) |         return this.#exec(next) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  | @ -138,19 +146,36 @@ class Router { | ||||||
|       let route = this.#tables.get('!') |       let route = this.#tables.get('!') | ||||||
|       $view.current = route.name |       $view.current = route.name | ||||||
|       this.#route = { path, name: route.name, params: {}, query: {} } |       this.#route = { path, name: route.name, params: {}, query: {} } | ||||||
|  |       this.#exec(this.#route) | ||||||
|  |     } else { | ||||||
|  |       if (this.#tmp) { | ||||||
|  |         this.#exec(this.#tmp) | ||||||
|  |         this.#tmp = null | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   #exec(route) { |   #exec(route) { | ||||||
|     let $view = window.wkitd.get(__ROUTER_VIEW__) |     let $view = window.wkitd.get(__ROUTER_VIEW__) | ||||||
|  |     let table = this.#tables.get(route.raw) | ||||||
|     $view.current = route.name |     $view.current = route.name | ||||||
|     this.#route = route |     this.#route = route | ||||||
|     this.#rsync() | 
 | ||||||
|  |     if (table && typeof table.component === 'function') { | ||||||
|  |       if (!customElements.get(route.name)) { | ||||||
|  |         table.component() | ||||||
|  |         delete table.component //避免多次请求
 | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     this.#broadcast() | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   #rsync() { |   // 广播通知
 | ||||||
|     for (let [target, callback] of this.#targets) { |   #broadcast() { | ||||||
|       callback.call(target, this.route) |     if (this.#ready) { | ||||||
|  |       for (let callback of ROUTE_CALLBACKS) { | ||||||
|  |         callback(this.route) | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -161,17 +186,6 @@ class Router { | ||||||
|     this.#hashchange() |     this.#hashchange() | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** |  | ||||||
|    * 用于同步路由到组件的 |  | ||||||
|    */ |  | ||||||
|   rsync(target, callback) { |  | ||||||
|     this.#targets.set(target, callback) |  | ||||||
|     // 路由已经初始化完成时, 还有新的同步请求则立刻执行
 |  | ||||||
|     if (this.#ready) { |  | ||||||
|       this.#rsync() |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   beforeEach(callback = noop) { |   beforeEach(callback = noop) { | ||||||
|     this.#beforeEach = callback |     this.#beforeEach = callback | ||||||
|   } |   } | ||||||
|  | @ -219,6 +233,9 @@ class Router { | ||||||
|       return |       return | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // 缓存当前路由信息, 当没有匹配到正确的路由时, 回调此缓存
 | ||||||
|  |     this.#tmp = obj | ||||||
|  | 
 | ||||||
|     if (this.type === MODE_HASH) { |     if (this.type === MODE_HASH) { | ||||||
|       if (replace) { |       if (replace) { | ||||||
|         location.replace(path.replace(/^\//, '#/')) |         location.replace(path.replace(/^\//, '#/')) | ||||||
|  |  | ||||||
							
								
								
									
										72
									
								
								src/store.js
								
								
								
								
							
							
						
						
									
										72
									
								
								src/store.js
								
								
								
								
							|  | @ -5,7 +5,62 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| import { Component } from 'wkit' | import { Component } from 'wkit' | ||||||
| import { __STORE__ } from './constants.js' | import { | ||||||
|  |   __STORE__, | ||||||
|  |   __ROUTER__, | ||||||
|  |   STORE_CALLBACKS, | ||||||
|  |   ROUTE_CALLBACKS | ||||||
|  | } from './constants.js' | ||||||
|  | import { noop } from './utils.js' | ||||||
|  | 
 | ||||||
|  | function observe(obj, paths = ['$store']) { | ||||||
|  |   if (obj === null) { | ||||||
|  |     return obj | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return new Proxy(obj, { | ||||||
|  |     get(target, key, receiver) { | ||||||
|  |       let value = Reflect.get(target, key, receiver) | ||||||
|  |       // 当访问的值是对象时,需要对这个对象也进行代理
 | ||||||
|  |       if (typeof value === 'object') { | ||||||
|  |         return observe(value, paths.concat(key)) | ||||||
|  |       } | ||||||
|  |       return value | ||||||
|  |     }, | ||||||
|  |     set(target, key, value, receiver) { | ||||||
|  |       let full = paths.concat(key).join('.') | ||||||
|  |       if (target[key] === value) { | ||||||
|  |         return true | ||||||
|  |       } | ||||||
|  |       Reflect.set(target, key, value, receiver) | ||||||
|  |       if (STORE_CALLBACKS.get(full)) { | ||||||
|  |         STORE_CALLBACKS.get(full).forEach(callback => { | ||||||
|  |           callback(value) | ||||||
|  |         }) | ||||||
|  |       } | ||||||
|  |       window.wkitd.broadcast() | ||||||
|  |       return true | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function watch(key, callback = noop) { | ||||||
|  |   if (key.startsWith('$store.')) { | ||||||
|  |     let list = STORE_CALLBACKS.get(key) | ||||||
|  |     if (list) { | ||||||
|  |       list.add(callback) | ||||||
|  |     } else { | ||||||
|  |       list = new Set() | ||||||
|  |       list.add(callback) | ||||||
|  |       STORE_CALLBACKS.set(key, list) | ||||||
|  |     } | ||||||
|  |   } else if (key.startsWith('$route')) { | ||||||
|  |     ROUTE_CALLBACKS.add(callback) | ||||||
|  |     callback(window.wkitd.get(__ROUTER__).route) | ||||||
|  |   } else { | ||||||
|  |     return console.error('watch() only work on $store and $route') | ||||||
|  |   } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| export function createStore(obj = {}) { | export function createStore(obj = {}) { | ||||||
|   let defined = false |   let defined = false | ||||||
|  | @ -13,6 +68,7 @@ export function createStore(obj = {}) { | ||||||
|   return function () { |   return function () { | ||||||
|     Object.defineProperty(Component.prototype, '$store', { |     Object.defineProperty(Component.prototype, '$store', { | ||||||
|       get() { |       get() { | ||||||
|  |         window.wkitd.assign(this) | ||||||
|         return window.wkitd.get(__STORE__) |         return window.wkitd.get(__STORE__) | ||||||
|       }, |       }, | ||||||
|       set(val) { |       set(val) { | ||||||
|  | @ -21,19 +77,9 @@ export function createStore(obj = {}) { | ||||||
|             'Can not set readonly property $store of Component' |             'Can not set readonly property $store of Component' | ||||||
|           ) |           ) | ||||||
|         } |         } | ||||||
|         window.wkitd.set( |         window.wkitd.set(__STORE__, observe(val)) | ||||||
|           __STORE__, |  | ||||||
|           new Proxy(val, { |  | ||||||
|             set(target, prop, value) { |  | ||||||
|               target[prop] = value |  | ||||||
|               window.wkitd.broadcast() |  | ||||||
|               return true |  | ||||||
|             } |  | ||||||
|           }) |  | ||||||
|         ) |  | ||||||
|         defined = true |         defined = true | ||||||
|       }, |       } | ||||||
|       enumerable: false |  | ||||||
|     }) |     }) | ||||||
|     Component.prototype.$store = obj |     Component.prototype.$store = obj | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -13,9 +13,7 @@ export function readonlyProp(host, name, value) { | ||||||
|   Object.defineProperty(host, name, { |   Object.defineProperty(host, name, { | ||||||
|     get() { |     get() { | ||||||
|       return value |       return value | ||||||
|     }, |     } | ||||||
|     set(vale) {}, |  | ||||||
|     enumerable: false |  | ||||||
|   }) |   }) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue