@yomua/y-screw
v0.1.3
Published
为 JavaScript 提供额外的功能
Downloads
17
Readme
描述
提供轻便好用的 JavaScript 功能的库.
环境
除了依赖浏览器环境的包, 其余都是浏览器或 node 环境下都可使用.
依赖浏览器环境 API
urlChange
node + browser 环境
addBigNumbers
import assert from '@yomua/y-assert'
import { addBigNumbers } from '@yomua/y-screw'
const { expect } = assert
const v1 = '11111111111.11111111111111111111111111111111111111111111111'
const v2 = '22222222222.33333333333333333333333333333333333333333333333'
const v3 = '33333333333.44444444444444444444444444444444444444444444444'
expect(addBigNumbers(v1).add(v2).get()).equal(v3)
expect(addBigNumbers('123').get()).equal('123')debounce
import { debounce } from '@yomua/y-screw'
// 延迟 0ms, 若当前使用 debounce 的上下文被刷新, 则会生成新的计时器
// => 在 React 组件中使用 debounce, 若当前组件刷新, 则会执行 2 次回调函数.
debounce(() => {}, 0, { isRefreshItself: false })
// 延迟 1000ms, 若当前使用 debounce 的上下文被刷新, 则不会生成新的计时器
// => 在 React 组件中使用 debounce, 若当前组件刷新, 则只会执行 1 次回调函数.
// => 即: debounce 生成的 timer, 将拥有自己的上下文
debounce(() => {}, 1000, { isRefreshItself: true })getType
import assert from '@yomua/y-assert'
import { getType } from '@yomua/y-screw'
const { expect } = assert
expect(getType([])).equal('array')
expect(getType({})).equal('object')
expect(getType(1)).equal('number')
expect(getType('yomua')).equal('string')
expect(getType(true)).equal('boolean')
expect(getType(Symbol(1))).equal('symbol')
expect(getType(null)).equal('null')
expect(getType(undefined)).equal('undefined')
expect(getType(BigInt(1))).equal('bigInt')
expect(getType(function () {})).equal('function')isEmptyObject
会校验对象中是否存在 Symbol 值.
import assert from '@yomua/y-assert'
import { isEmptyObject } from '@yomua/y-screw'
const { expect } = assert
expect(isEmptyObject({})).equal(true)
expect(isEmptyObject({ a: 1 })).equal(false)
expect(
isEmptyObject({
[Symbol(1)]: 1,
}),
).equal(false)isNil
import assert from '@yomua/y-assert'
import { isNil } from '@yomua/y-screw'
const { expect } = assert
expect(isNil(null)).equal(true)
expect(isNil(undefined)).equal(true)
expect(isNil({})).equal(false)isType
import assert from '@yomua/y-assert'
import { isType } from '@yomua/y-screw'
const { expect } = assert
// 在 ts 中, 可以传入指定的类型, 从而约束传入数据的类型.
expect(isType<any[]>([], 'array')).equal(true)
expect(isType<object>({}, 'object')).equal(true)
expect(isType(1, 'number')).equal(true)
expect(isType('yomua', 'string')).equal(true)
expect(isType(true, 'boolean')).equal(true)
expect(isType(Symbol(1), 'symbol')).equal(true)
expect(isType(null, 'null')).equal(true)
expect(isType(undefined, 'undefined')).equal(true)
expect(isType(BigInt(1), 'bigInt')).equal(true)
expect(isType(function () {}, 'function')).equal(true)limitPromise
对一次性发送的请求次数进行限制.
术语称之为请求并发的限制.
简单来说: 当你限制一次性只能发送 2 个请求时, 若此时有 5 个请求要被发送, 则会先取前 2 个, 后 3 个则是放入一个数组进行等待, 当前面请求完成时, 再从数组中取出新请求, 再发送; 如此往复, 直到所有请求完成.
TIP: limitPromise 并不保证请求的顺序性, 即: 不保证先发出的请求先完成, 这取决于请求处理时间.
import { limitPromise } from '@yomua/y-screw'
const limit = limitPromise(2)
function job(name) {
console.log(`started: ${name}`)
return new Promise((resolve, reject) => {
// 当作异步任务
setTimeout(
() => {
console.log(` finished: ${name}`)
resolve(name)
},
Math.floor(Math.random() * 1000),
)
})
}
for (let i = 0; i <= 4; i++) {
limit(() => job(i))
}
// 输出
// started: 0
// started: 1
// finished: 0
// started: 2
// finished: 2
// started: 3
// finished: 3
// started: 4
// finished: 1
// finished: 4memoizeFn
import assert from '@yomua/y-assert'
import { memoizeFn } from '@yomua/y-screw'
const { expect } = assert
let i = 0
let j = 0
function increasing1() {
return ++i // 先自增再作为值返回;
}
function increasing2() {
return ++j // 先自增再作为值返回;
}
// memoizeFn 返回的函数指向的是你传进来的函数 increasing1
const memo1 = memoizeFn(increasing1, { resolver: '123' })
const memo2 = memoizeFn(increasing1, { resolver: '456' })
// 只对函数进行缓存, 不会对值进行缓存; 若想要对值进行缓存, Refer: memoizeFnValue
expect(memo1(1, 2, 3)).equal(1)
expect(memo1(1, 2, 3)).equal(2)
// 即使 key 不一样, 但是由于 memo 的是同一个函数, memoizeFn 返回函数就是这同一个函数, 所以返回 true
expect(memo1).equal(memo2)
/**
* 由于 cache 是声明在全局内存中, 所以调用 memoizeFn 时, cache 都指向同一个
* 所以若有相同的 key, 则后面出现的一样的 key 将会被忽略
* 所以 memoizeFn 返回的函数将指向 cache.get(resolver) => 这里就是 increasing1
*/
const memo3 = memoizeFn(increasing2, { resolver: '123' })
const memo4 = memoizeFn(increasing2, { resolver: '456' })
// memo3, memo4 是 increasing1, 不是 increasing2
// 所以不是使用 j 进行计算
expect(memo3()).equal(3)
expect(memo4()).equal(4)
// 相同而 key, 只会记录第一个出现的 key
expect(memo3).equal(memo1)
expect(memo4).equal(memo2)// 不指定 key, 自动使用 hashString 进行计算,
// 这会使得缓存不同函数时, 后面出现的函数不会被忽略, 将会单独保存
// 但是相同函数, 得到的 hash key 仍然是一样的
let y = 0
function increasing3() {
return ++y
}
const memo5 = memoizeFn(increasing3)
const memo6 = memoizeFn(increasing3)
expect(memo5()).equal(1)
expect(memo6()).equal(2)
let z = 0
function increasing4() {
return ++z
}
// 这里和 memo5, memo6 一样没有使用 key, 但是新函数会被单独缓存
// 因为 key 不同, 由 memoizeFn 自动计算
const memo7 = memoizeFn(increasing4)
const memo8 = memoizeFn(increasing4)
expect(memo7()).equal(1)
expect(memo8()).equal(2)memoizeFnValue
import assert from '@yomua/y-assert'
import { memoizeFnValue } from '@yomua/y-screw'
const { expect } = assert
let i = 0
function increasing() {
i += 1
return i
}
const increasing1 = memoizeFnValue(increasing)
// 连续调用 3 次, 仍然和第一次计算的值
expect(increasing1()).equal(1)
expect(increasing1()).equal(1)
expect(increasing1()).equal(1)
// 具有有效期, 且为 0s
const increasing2 = memoizeFnValue(increasing, { maxAge: 0 })
expect(increasing2()).equal(2)
expect(increasing2()).equal(3)
expect(increasing2()).equal(4)
// 具有有效期, 且为 1s;
// 注意: 这是持续性的缓存, 即: 每 maxAge 秒后, 都会重新计算值, 并缓存 maxAge 秒, 直到过期, 如此反复
;(async function () {
const increasing3 = memoizeFnValue(increasing, { maxAge: 1 })
expect(increasing3()).equal(5) // 得到新值后, 缓存函数值
expect(increasing3()).equal(5) // 使用缓存值
await new Promise((resolve) => {
return setTimeout(() => {
// 1秒后缓存到期, 重新计算函数值并缓存, 这里新值为: 6
// TIP: 当缓存到期, 得到新值后, 又会重新以当前时间戳 + maxAge 计算缓存时间.
expect(increasing3()).equal(6)
resolve()
}, 1000)
})
expect(increasing3()).equal(6) // 使用新缓存值: 6
await new Promise((resolve) => {
return setTimeout(() => {
// 1秒后缓存又到期, 又重新计算函数值并缓存, 这里新值为: 7
expect(increasing3()).equal(7)
resolve()
}, 1000)
})
expect(increasing3()).equal(7) // 在没有过期之前, 这里将一直会使用缓存值: 7
})()throttle
import { throttle } from '@yomua/y-screw'
throttle(() => {
console.log('throttled')
}, 1000)toJSON
和 JSON.stringify 类似, 但是特殊处理以下值:
防止循环引用, 若循环引用, 则对循环引用值返回 Circular
null 视为 null
undefined 视为 null
function, symbol, bigInt: 做 toString() 处理
Date: 做 toISOString() 处理
在对象, 数组中, 含以上值, 也对其做以上处理.
其他未特殊处理的值都和 JSON.stringify() 保持一致
示例
以下示例的 equal(value) 就是最终 toJSON(data) 得到的数据
import assert from '@yomua/y-assert'
import { toJSON } from '@yomua/y-screw'
const { expect } = assert// 原始值测试
expect(toJSON(1)).equal('1')
expect(toJSON('1')).equal('"1"')
expect(toJSON([1])).equal('[1]')
expect(toJSON(['1'])).equal('["1"]')
expect(toJSON({ a: 1 })).equal('{"a":1}')
expect(toJSON({ a: '1' })).equal('{"a":"1"}')// Symbol, BigInt (也是原始值, 不过在这里将它们独立书写, 以便阅读)
const symbol1 = Symbol(1)
expect(toJSON(symbol1)).equal('Symbol(1)')
expect(toJSON([symbol1])).equal('["Symbol(1)"]')
expect(toJSON({ [symbol1]: '1' })).equal('{"Symbol(1)":"1"}')
expect(toJSON(BigInt(1111111111111111))).equal('1111111111111111n')
expect(toJSON([BigInt(1111111111111111)])).equal('["1111111111111111n"]')
expect(toJSON({ bigInt: BigInt(1111111111111111) })).equal(
'{"bigInt":"1111111111111111n"}',
)// NaN
expect(toJSON(NaN)).equal('null')
expect(toJSON([NaN])).equal('[null]')
expect(toJSON({ name: NaN })).equal('{"name":null}')// undefined (所有 undefined, 最后都会被转为 null 显示)
expect(toJSON()).equal(null)
expect(toJSON(undefined)).equal(null)
expect(toJSON([undefined])).equal('[null]')
expect(toJSON({ name: undefined })).equal('{"name":null}')
expect(toJSON([[undefined]])).equal('[[null]]')
expect(toJSON([{ name: undefined }])).equal('[{"name":null}]')// null
expect(toJSON(null)).equal(null)
expect(toJSON([null])).equal('[null]')
expect(toJSON({ name: null })).equal('{"name":null}')
expect(toJSON({ obj: { name: null } })).equal('{"obj":{"name":null}}')// 日期
const date = new Date()
expect(toJSON(date)).equal(date.toISOString())
expect(toJSON([date])).equal(`["${date.toISOString()}"]`)
expect(toJSON({ date })).equal(`{"date":"${date.toISOString()}"}`)// 函数
expect(toJSON(function () {})).equal('function () {}')
expect(toJSON([function () {}])).equal('["function () {}"]')
expect(toJSON({ func: function () {} })).equal('{"func":"function () {}"}')
expect(toJSON([{ func: function () {} }])).equal('[{"func":"function () {}"}]')// 数组
expect(toJSON([1, 2, 3])).equal(JSON.stringify([1, 2, 3]))
expect(toJSON([null])).equal(JSON.stringify([null]))
expect(toJSON([undefined])).equal(JSON.stringify([undefined]))
expect(toJSON([null, undefined])).equal(JSON.stringify([null, undefined]))
expect(toJSON([[1, 2, 3]])).equal(JSON.stringify([[1, 2, 3]]))
expect(toJSON([{ name: 'yomua' }])).equal(JSON.stringify([{ name: 'yomua' }]))
expect(toJSON([{ obj: { name: 'yomua' } }])).equal(
JSON.stringify([{ obj: { name: 'yomua' } }]),
)// 对象
expect(toJSON({ name: 'yomua' })).equal(JSON.stringify({ name: 'yomua' }))
expect(toJSON({ obj: { age: '18' } })).equal(
JSON.stringify({ obj: { age: '18' } }),
)
expect(toJSON({ arr: ['yomua'] })).equal(JSON.stringify({ arr: ['yomua'] }))
expect(toJSON({ arr: [{ name: 'yomua' }] })).equal(
JSON.stringify({ arr: [{ name: 'yomua' }] }),
)// 循环引用数组
const arrCircular = ['yomua']
arrCircular.push(arrCircular)
expect(toJSON(arrCircular)).equal('["yomua","[Circular]"]')
// 对象自身循环引用
const objCircular = { name: 'yomua' }
objCircular.data = objCircular
expect(toJSON({ objCircular })).equal(
'{"objCircular":{"name":"yomua","data":"[Circular]"}}',
)
// 两个对象相互引用
const obj1 = { name: 'obj1' }
const obj2 = { name: 'obj2' }
obj1.obj2 = obj2
obj2.obj1 = obj1
expect(toJSON(obj1)).equal(
'{"name":"obj1","obj2":{"name":"obj2","obj1":"[Circular]"}}',
)
expect(toJSON(obj2)).equal(
'{"name":"obj2","obj1":{"name":"obj1","obj2":"[Circular]"}}',
)
// 对象和数组的相互引用
const obj3 = { name: 'obj3' }
const arr3 = [obj3]
obj3.arr3 = arr3
expect(toJSON(obj3)).equal('{"name":"obj3","arr3":["[Circular]"]}')
expect(toJSON(arr3)).equal('[{"name":"obj3","arr3":"[Circular]"}]')// 各种值混合测试
const date2 = new Date()
const symbol2 = Symbol(1)
const obj = {
name: 'yomua',
}
const arr = [
1,
'2',
'true',
true,
'null',
'undefined',
null, // null
undefined, // null
NaN, // nul
symbol2, // 保留 symbol 值: Symbol(1)
BigInt(1),
'2n',
function () {},
{ name: 'yomua', value1: null, value2: undefined },
[1, 2, 3, { name: 'yomua' }],
date2,
obj, // 循环引用
]
obj.arr = arr // 循环引用
expect(toJSON(arr)).equal(
`[1,"2","true",true,"null","undefined",null,null,null,"Symbol(1)","1n","2n","function () {}",{"name":"yomua","value1":null,"value2":null},[1,2,3,{"name":"yomua"}],"${date2.toISOString()}",{"name":"yomua","arr":"[Circular]"}]`,
)
expect(toJSON(obj)).equal(
`{"name":"yomua","arr":[1,"2","true",true,"null","undefined",null,null,null,"Symbol(1)","1n","2n","function () {}",{"name":"yomua","value1":null,"value2":null},[1,2,3,{"name":"yomua"}],"${date2.toISOString()}","[Circular]"]}`,
)toParse
专用来解析使用 toJSON 生成的 JSON 字符串
特殊处理以下值 (非对象字符串和数组字符串中的值):
'null': 还原为 null
'undefined': 还原为 null
'NaN': 还原为 null
function, Symbol 字符串: 不做任何处理
'function () {}' => 'function () {}'
'Symbol(1)' => 'Symbol(1)'
BigInt 字符串: 还原为 BigInt 类型值. 如: '1111n' => 1111n
Date 字符串: 还原为对象. 如: '2020-01-01T00:00:00.000Z' => new Date('2020-01-01T00:00:00.000Z')
对字符串数组 '[...]' 和对象数组 '{...}', 单独使用另一套进行递归解析, 规则如下:
将对象字符串和数组字符串中的 Circular 的值还原为 null
因为只有对象和数组中, 才会存在循环引用
其他格式值不做任何处理
其他未特殊处理的值都和 JSON.parse() 保持一致, 包括对象字符串和数组字符串中的其他未特殊处理的值.
示例
此示例和 toJson() 示例一一对应, 直接 toParse 解析 toJson 的示例
equal(value) 就是最终 toParse(data) 得到的数据
import assert from '@yomua/y-assert'
import { toJSON, toParse } from '#test/index.mjs'
const { expect } = assert// 原始值测试
expect(toParse(toJSON(1))).equal(1)
expect(toParse(toJSON('1'))).equal('1')
expect(toParse(toJSON([1]))).equal([1])
expect(toParse(toJSON(['1']))).equal(['1'])
expect(toParse(toJSON({ a: 1 }))).equal({ a: 1 })
expect(toParse(toJSON({ a: '1' }))).equal({ a: '1' })// Symbol, BigInt (也是原始值, 不过在这里将它们独立书写, 以便阅读)
const symbol1 = Symbol(1)
expect(toParse(toJSON(symbol1))).equal('Symbol(1)')
expect(toParse(toJSON([symbol1]))).equal(['Symbol(1)'])
expect(toParse(toJSON({ [symbol1]: '1' }))).equal({ 'Symbol(1)': '1' })
expect(toParse(toJSON(BigInt(1111111111111111)))).equal(1111111111111111n)
expect(toParse(toJSON([BigInt(1111111111111111)]))).equal(['1111111111111111n'])
expect(toParse(toJSON({ bigInt: BigInt(1111111111111111) }))).equal({
bigInt: '1111111111111111n',
})// NaN
expect(toParse(toJSON(NaN))).equal(null)
expect(toParse(toJSON([NaN]))).equal([null])
expect(toParse(toJSON({ name: NaN }))).equal({ name: null })// undefined (所有 undefined, 最后都会被转为 null 显示)
expect(toParse(toJSON(undefined))).equal(null)
expect(toParse(toJSON())).equal(null)
expect(toParse(toJSON({ name: undefined }))).equal({ name: null })
expect(toParse(toJSON([undefined]))).equal([null])
expect(toParse(toJSON([[undefined]]))).equal([[null]])
expect(toParse(toJSON([{ name: undefined }]))).equal([{ name: null }])// null
expect(toParse(toJSON(null))).equal(null)
expect(toParse(toJSON([null]))).equal([null])
expect(toParse(toJSON({ name: null }))).equal({ name: null })
expect(toParse(toJSON({ obj: { name: null } }))).equal({ obj: { name: null } })// 日期
const date = new Date()
expect(toParse(toJSON(date))?.getTime?.()).equal(date?.getTime())
expect(toParse(toJSON([date]))).equal([date.toISOString()])
expect(toParse(toJSON({ date }))).equal({ date: date.toISOString() })// 函数
expect(toParse(toJSON(function () {}))).equal('function () {}')
expect(toParse(toJSON([function () {}]))).equal(['function () {}'])
expect(toParse(toJSON({ func: function () {} }))).equal({
func: 'function () {}',
})
expect(toParse(toJSON([{ func: function () {} }]))).equal([
{ func: 'function () {}' },
])// 数组
expect(toParse(toJSON([1, 2, 3]))).equal(JSON.parse(JSON.stringify([1, 2, 3])))
expect(toParse(toJSON([null]))).equal(JSON.parse(JSON.stringify([null])))
expect(toParse(toJSON([undefined]))).equal(
JSON.parse(JSON.stringify([undefined])),
)
expect(toParse(toJSON([null, undefined]))).equal(
JSON.parse(JSON.stringify([null, undefined])),
)
expect(toParse(toJSON([[1, 2, 3]]))).equal(
JSON.parse(JSON.stringify([[1, 2, 3]])),
)
expect(toParse(toJSON([{ name: 'yomua' }]))).equal(
JSON.parse(JSON.stringify([{ name: 'yomua' }])),
)
expect(toParse(toJSON([{ obj: { name: 'yomua' } }]))).equal(
JSON.parse(JSON.stringify([{ obj: { name: 'yomua' } }])),
)// 对象
expect(toParse(toJSON({ name: 'yomua' }))).equal(
JSON.parse(JSON.stringify({ name: 'yomua' })),
)
expect(toParse(toJSON({ obj: { age: '18' } }))).equal(
JSON.parse(JSON.stringify({ obj: { age: '18' } })),
)
expect(toParse(toJSON({ arr: ['yomua'] }))).equal(
JSON.parse(JSON.stringify({ arr: ['yomua'] })),
)
expect(toParse(toJSON({ arr: [{ name: 'yomua' }] }))).equal(
JSON.parse(JSON.stringify({ arr: [{ name: 'yomua' }] })),
)// 对象自身循环引用
const objCircular = { name: 'yomua' }
objCircular.data = objCircular
expect(toParse(toJSON({ objCircular }))).equal({
objCircular: { name: 'yomua', data: null },
})
// 两个对象相互引用
const obj1 = { name: 'obj1' }
const obj2 = { name: 'obj2' }
obj1.obj2 = obj2
obj2.obj1 = obj1
expect(toParse(toJSON(obj1))).equal({
name: 'obj1',
obj2: { name: 'obj2', obj1: null },
})
expect(toParse(toJSON(obj2))).equal({
name: 'obj2',
obj1: { name: 'obj1', obj2: null },
})
// 对象和数组的相互引用
const obj3 = { name: 'obj3' }
const arr3 = [obj3]
obj3.arr3 = arr3
expect(toParse(toJSON(obj3))).equal({ name: 'obj3', arr3: [null] })
expect(toParse(toJSON(arr3))).equal([{ name: 'obj3', arr3: null }])// 各种值混合测试
const symbol2 = Symbol(1)
const date2 = new Date()
const obj = {
name: 'yomua',
}
const arr = [
1,
'2',
'true',
true,
'null',
'undefined',
null, // null
undefined, // null
NaN, // nul
symbol2, // 保留 symbol 值: Symbol(1)
BigInt(1),
'2n',
function () {},
{ name: 'yomua', value1: null, value2: undefined },
[1, 2, 3, { name: 'yomua' }],
date2,
obj, // 循环引用
]
obj.arr = arr // 循环引用
expect(toParse(toJSON(arr))).equal([
1,
'2',
'true',
true,
'null',
'undefined',
null,
null,
null,
'Symbol(1)',
'1n',
'2n',
'function () {}',
{ name: 'yomua', value1: null, value2: null },
[1, 2, 3, { name: 'yomua' }],
date2.toISOString(),
{ name: 'yomua', arr: null },
])
expect(toParse(toJSON(obj))).equal({
name: 'yomua',
arr: [
1,
'2',
'true',
true,
'null',
'undefined',
null,
null,
null,
'Symbol(1)',
'1n',
'2n',
'function () {}',
{ name: 'yomua', value1: null, value2: null },
[1, 2, 3, { name: 'yomua' }],
date2.toISOString(),
null,
],
})conditionalChain
类似三元表达式, 但对多条件判断时, 语义更清晰
支持链式调用
对每个
cond(condition)和r(result)实行位置上的一对一匹配
import assert from '@yomua/y-assert'
import { conditionalChain } from '@yomua/y-screw'
const { expect } = assert
// 对字符串进行判断
expect(conditionalChain().cond('a').cond('b').cond('c').get()).equal(undefined)
expect(conditionalChain().cond('a').r('r1').get()).equal('r1')
// 对布尔值进行判断
expect(conditionalChain().cond(false).cond(true).r('r1').r('r2').get()).equal(
'r2',
)
// 对表达式进行判断
expect(
conditionalChain()
.cond(1 === 0)
.r('r1')
.cond(1 === 1)
.r('r2')
.get(),
).equal('r2')
// 对函数表达式进行判断
expect(
conditionalChain()
.cond(() => true)
.r('r1')
.cond(() => false)
.r('r2')
.get(),
).equal('r1')
// 使用默认值
expect(
conditionalChain()
.default('default1')
.cond(false)
.default('default2')
.r('r1')
.get(),
).equal('default2')
expect(conditionalChain().cond(true).get()).equal(undefined)
// 条件全为 true, 则返回第一个 true 对应的值
expect(conditionalChain().cond(true).r('r1').cond(true).r('r2').get()).equal(
'r1',
)hashString
import assert from '@yomua/y-assert'
import { hashString } from '@yomua/y-screw'
const { expect } = assert;(async function () {
// 将一个字符串转为 hash 值
// 支持各种环境: node, browser(新版浏览器), browser(旧版浏览器)
const value = await hashString('yomua')
expect(value).equal(
'e80789d2f56d544c268e51bccb69ae9d0650f77b4646467fc9ea51c3874ebec6',
)
expect(value).typeOf('string')
})browser 环境
urlChange
import { toJSON } from '@yomua/y-screw'
// 更改浏览器 url
urlChange(window.location.origin + '/yomua')
// 修改 url 时是否直接跳转过去
urlChange(window.location.origin + '/yomua', {
go: true,
})// 携带 state
window.addEventListener('popstate', (event) => {
console.log(event.state) // {name: 'yhw'}
})
// 当使用者监听 popstate 时,要传给 event.state 的数据
// TIP: 仅当 go: true 时才有用.
urlChange(window.location.origin + '/yomua', {
state: { name: 'ywh' },
go: true,
})