diff --git a/src/router/hash-router.js b/src/router/hash-router.js index 9ba9141..d01a279 100644 --- a/src/router/hash-router.js +++ b/src/router/hash-router.js @@ -5,13 +5,12 @@ * */ import { bind, fire } from 'wkit' -import { hideProp, targetIsThisWindow } from '../utils.js' +import { query2object } from '../utils.js' //hash前缀正则 const PREFIX_REGEXP = /^(#!|#)[\/]?/ const TRIM_REGEXP = /(^[/]+)|([/]+$)/g const DEFAULT_OPTIONS = { - prefix: '!/', allowReload: true //连续点击同一个链接是否重新加载 } @@ -48,17 +47,12 @@ class Router { this.#ready = true } - let path = location.hash - - path = path.replace(PREFIX_REGEXP, '').trim() - path = path.replace(TRIM_REGEXP, '') - if (ev?.type === 'load') { - this.go(path) + // this.go() // hash模式要手动触发一下路由检测 - this.#check(path) + this.#check() } else { - this.#check(path) + this.#check() } } @@ -100,9 +94,18 @@ class Router { } // 路由检测 - #check(path) { + #check() { let { allowReload } = this.#options let $view = window.__wkitd__.get('ROUTER_VIEW') + let path = location.hash + let query + + if (path.includes('?')) { + ;[path, query] = path.split('?') + } + path = path.replace(PREFIX_REGEXP, '').replace(TRIM_REGEXP, '') + + // console.log(path, query, query2object(query)) if (!$view || (!allowReload && path === this.#route.path)) { return diff --git a/src/router/index.js b/src/router/index.js index 16b8979..e54eac4 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -3,128 +3,13 @@ * @author yutent * @date 2023/08/10 10:10:06 */ - -import { Component, html, raw } from 'wkit' - +import { Component } from 'wkit' import createWebHashHistory from './hash-router.js' import createWebHistory from './modern-router.js' +import './router-components.js' export { createWebHashHistory, createWebHistory } -class RouterView extends Component { - static props = { - keepAlive: 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]?.$animate() - this.$refs[v]?.activated() - } - } - } - } - - #views = [] - - created() { - window.__wkitd__.set('ROUTER_VIEW', this) - } - - sync(views) { - this.#views = views - } - - render() { - let option = { - immediate: true, - custom: [ - { transform: 'translateX(-32px)', opacity: 0 }, - { transform: 'translateX(0)', opacity: 1 } - ] - } - if (this.keepAlive) { - let template = this.#views.map(it => [ - this.transition - ? `<${it} ref="${it}" :__keep_alive__="%s" #animation="%s" style="%s">` - : `<${it} ref="${it}" :__keep_alive__="%s" style=%s>`, - [ - this.current === it, - { ...option, immediate: this.current === it }, - this.current === it ? '' : 'display:none' - ] - ]) - - return raw( - template.map(it => it[0]).join(''), - template.map(it => it[1]).flat() - ) - } else { - if (this.current) { - if (this.transition) { - return raw(`<${this.current} #animation="%s">`, [ - option - ]) - } - return raw(`<${this.current}>`) - } - } - } -} - -class RouterLink extends Component { - static props = { - to: Object, - disabled: false - } - - static styles = css` - :host { - display: inline-flex; - } - a { - color: inherit; - text-decoration: inherit; - cursor: pointer; - } - - :host([disabled]) a { - opacity: 0.6; - cursor: not-allowed; - } - ` - - #navigate() { - let type = this.$router.type - if (this.disabled) { - return - } - if (type === 'hash') { - // - } else { - // - } - } - - render() { - return html` ` - } -} - -if (!customElements.get('router-view')) { - customElements.define('router-view', RouterView) -} - -if (!customElements.get('router-link')) { - customElements.define('router-link', RouterLink) -} - export function createRouter( { history = createWebHashHistory, routes = [] } = {}, options diff --git a/src/router/router-components.js b/src/router/router-components.js new file mode 100644 index 0000000..b226e02 --- /dev/null +++ b/src/router/router-components.js @@ -0,0 +1,136 @@ +// +import { Component, html, css, raw } from 'wkit' +import { object2query } from '../utils.js' + +class RouterView extends Component { + static props = { + keepAlive: 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]?.$animate() + this.$refs[v]?.activated() + } + } + } + } + + #views = [] + + created() { + window.__wkitd__.set('ROUTER_VIEW', this) + } + + sync(views) { + this.#views = views + } + + render() { + let option = { + immediate: true, + custom: [ + { transform: 'translateX(-32px)', opacity: 0 }, + { transform: 'translateX(0)', opacity: 1 } + ] + } + if (this.keepAlive) { + let template = this.#views.map(it => [ + this.transition + ? `<${it} ref="${it}" :__keep_alive__="%s" #animation="%s" style="%s">` + : `<${it} ref="${it}" :__keep_alive__="%s" style=%s>`, + [ + this.current === it, + { ...option, immediate: this.current === it }, + this.current === it ? '' : 'display:none' + ] + ]) + + return raw( + template.map(it => it[0]).join(''), + template.map(it => it[1]).flat() + ) + } else { + if (this.current) { + if (this.transition) { + return raw(`<${this.current} #animation="%s">`, [ + option + ]) + } + return raw(`<${this.current}>`) + } + } + } +} + +class RouterLink extends Component { + static props = { + to: Object, + disabled: false + } + + static styles = css` + :host { + display: inline-flex; + -webkit-user-select: none; + user-select: none; + } + a { + color: inherit; + text-decoration: inherit; + cursor: pointer; + } + + :host([disabled]) a { + opacity: 0.6; + cursor: not-allowed; + } + ` + + #href = '' + + #navigate() { + let type = this.$router.type + let { path } = this.to + if (this.disabled) { + return + } + if (type === 'hash') { + location.hash = this.#href + } else { + window.history.pushState({ path }, null, this.#href) + } + } + + #parsePath() { + let type = this.$router.type + let { path = '', query = {} } = this.to + let params = typeof query === 'string' ? query : object2query(query) + + path = path.replace(/^\//, '') + + return '/' + path + '?' + params + } + + render() { + this.#href = this.#parsePath() + + return html` + ` + } +} + +if (!customElements.get('router-view')) { + customElements.define('router-view', RouterView) +} + +if (!customElements.get('router-link')) { + customElements.define('router-link', RouterLink) +} diff --git a/src/utils.js b/src/utils.js index ae151ca..9bcd6fd 100644 --- a/src/utils.js +++ b/src/utils.js @@ -4,6 +4,9 @@ * @date 2023/08/10 10:07:51 */ +const encode = encodeURIComponent +const decode = decodeURIComponent + export function noop() {} export function hideProp(host, name, value) { @@ -26,3 +29,87 @@ export function targetIsThisWindow(target) { } return false } + +/** + * query 序列化 + */ +function serialize(p, obj, callback) { + var k + if (Array.isArray(obj)) { + obj.forEach(function (it, i) { + k = p ? `${p}[${Array.isArray(it) ? i : ''}]` : i + if (typeof it === 'object') { + serialize(k, it, callback) + } else { + callback(k, it) + } + }) + } else { + for (let i in obj) { + k = p ? `${p}[${i}]` : i + if (typeof obj[i] === 'object') { + serialize(k, obj[i], callback) + } else { + callback(k, obj[i]) + } + } + } +} + +/** + * 将url query转回json对象 + */ +export function query2object(str = '') { + let params = new URLSearchParams(str) + let output = Object.create(null) + + for (let [_k, _v] of params.entries()) { + let k = decode(_k) + let v = decode(_v) + let isArray = 0 + + if (/(\w+)\[(\w*?)\]/.test(k)) { + let k1 = RegExp.$1 + let k2 = RegExp.$2 + + k = k1 + + if (!k2 || +k2 === +k2) { + v = [v] + isArray |= 2 + } else { + isArray |= 1 + v = { [k2]: v } + } + } + if (output[k]) { + if (isArray & 2) { + output[k] = output[k].concat(v) + } else { + Object.assign(output[k], v) + } + } else { + output[k] = v + } + } + return output +} + +/** + * 将json数据转成 url query字符串 + */ +export function object2query(obj = {}) { + if (!obj || typeof obj === 'string' || typeof obj === 'number') { + return obj + } + let output = [] + let query = function (k, v) { + output.push(k + '=' + encode(v)) + } + + if (typeof obj === 'object') { + serialize('', obj, query) + } + + return output.join('&') +}