完成router-link跳转

master
yutent 2023-08-14 15:07:02 +08:00
parent b068f90f0e
commit 34ac7ab8ed
4 changed files with 239 additions and 128 deletions

View File

@ -5,13 +5,12 @@
* *
*/ */
import { bind, fire } from 'wkit' import { bind, fire } from 'wkit'
import { hideProp, targetIsThisWindow } from '../utils.js' import { query2object } from '../utils.js'
//hash前缀正则 //hash前缀正则
const PREFIX_REGEXP = /^(#!|#)[\/]?/ const PREFIX_REGEXP = /^(#!|#)[\/]?/
const TRIM_REGEXP = /(^[/]+)|([/]+$)/g const TRIM_REGEXP = /(^[/]+)|([/]+$)/g
const DEFAULT_OPTIONS = { const DEFAULT_OPTIONS = {
prefix: '!/',
allowReload: true //连续点击同一个链接是否重新加载 allowReload: true //连续点击同一个链接是否重新加载
} }
@ -48,17 +47,12 @@ class Router {
this.#ready = true this.#ready = true
} }
let path = location.hash
path = path.replace(PREFIX_REGEXP, '').trim()
path = path.replace(TRIM_REGEXP, '')
if (ev?.type === 'load') { if (ev?.type === 'load') {
this.go(path) // this.go()
// hash模式要手动触发一下路由检测 // hash模式要手动触发一下路由检测
this.#check(path) this.#check()
} else { } else {
this.#check(path) this.#check()
} }
} }
@ -100,9 +94,18 @@ class Router {
} }
// 路由检测 // 路由检测
#check(path) { #check() {
let { allowReload } = this.#options let { allowReload } = this.#options
let $view = window.__wkitd__.get('ROUTER_VIEW') 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)) { if (!$view || (!allowReload && path === this.#route.path)) {
return return

View File

@ -3,128 +3,13 @@
* @author yutent<yutent.io@gmail.com> * @author yutent<yutent.io@gmail.com>
* @date 2023/08/10 10:10:06 * @date 2023/08/10 10:10:06
*/ */
import { Component } from 'wkit'
import { Component, html, raw } from 'wkit'
import createWebHashHistory from './hash-router.js' import createWebHashHistory from './hash-router.js'
import createWebHistory from './modern-router.js' import createWebHistory from './modern-router.js'
import './router-components.js'
export { createWebHashHistory, createWebHistory } 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}>`
: `<${it} ref="${it}" :__keep_alive__="%s" style=%s></${it}>`,
[
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"></${this.current}>`, [
option
])
}
return raw(`<${this.current}></${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`<a @click=${this.#navigate}> <slot></slot></a>`
}
}
if (!customElements.get('router-view')) {
customElements.define('router-view', RouterView)
}
if (!customElements.get('router-link')) {
customElements.define('router-link', RouterLink)
}
export function createRouter( export function createRouter(
{ history = createWebHashHistory, routes = [] } = {}, { history = createWebHashHistory, routes = [] } = {},
options options

View File

@ -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}>`
: `<${it} ref="${it}" :__keep_alive__="%s" style=%s></${it}>`,
[
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"></${this.current}>`, [
option
])
}
return raw(`<${this.current}></${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`<a title=${this.#href} @click=${this.#navigate}>
<slot></slot
></a>`
}
}
if (!customElements.get('router-view')) {
customElements.define('router-view', RouterView)
}
if (!customElements.get('router-link')) {
customElements.define('router-link', RouterLink)
}

View File

@ -4,6 +4,9 @@
* @date 2023/08/10 10:07:51 * @date 2023/08/10 10:07:51
*/ */
const encode = encodeURIComponent
const decode = decodeURIComponent
export function noop() {} export function noop() {}
export function hideProp(host, name, value) { export function hideProp(host, name, value) {
@ -26,3 +29,87 @@ export function targetIsThisWindow(target) {
} }
return false 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('&')
}