Skip to content

Commit 78b2e6b

Browse files
authored
增加eventCenter,迁移Taro API中eventCenter的最小实现 (#48)
1 parent 8f71319 commit 78b2e6b

File tree

5 files changed

+254
-0
lines changed

5 files changed

+254
-0
lines changed

packages/nextjs/taro/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ import {uploadFile} from './network/upload'
169169

170170
import {getLocation} from './location'
171171

172+
import {eventCenter} from './shared'
173+
172174
import {initTaroApp, TaroPage} from './internal'
173175

174176
export {
@@ -306,6 +308,8 @@ export {
306308

307309
getMenuButtonBoundingClientRect,
308310

311+
eventCenter,
312+
309313
initTaroApp,
310314
TaroPage
311315
}
@@ -445,6 +449,8 @@ export default {
445449

446450
getMenuButtonBoundingClientRect,
447451

452+
eventCenter,
453+
448454
initTaroApp,
449455
TaroPage
450456
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
type EventName = string | symbol
2+
type EventCallbacks = Record<EventName, Record<'next' | 'tail', unknown>>
3+
4+
export class Events {
5+
protected callbacks?: EventCallbacks
6+
static eventSplitter = ',' // Note: Harmony ACE API 8 开发板不支持使用正则 split 字符串 /\s+/
7+
8+
constructor (opts?) {
9+
this.callbacks = opts?.callbacks ?? {}
10+
}
11+
12+
on (eventName: EventName, callback: (...args: any[]) => void, context?: any): this {
13+
let event: EventName | undefined, tail, _eventName: EventName[]
14+
if (!callback) {
15+
return this
16+
}
17+
if (typeof eventName === 'symbol') {
18+
_eventName = [eventName]
19+
} else {
20+
_eventName = eventName.split(Events.eventSplitter)
21+
}
22+
this.callbacks ||= {}
23+
const calls = this.callbacks
24+
while ((event = _eventName.shift())) {
25+
const list = calls[event]
26+
const node: any = list ? list.tail : {}
27+
node.next = tail = {}
28+
node.context = context
29+
node.callback = callback
30+
calls[event] = {
31+
tail,
32+
next: list ? list.next : node
33+
}
34+
}
35+
return this
36+
}
37+
38+
once (events: EventName, callback: (...r: any[]) => void, context?: any): this {
39+
const wrapper = (...args: any[]) => {
40+
callback.apply(this, args)
41+
this.off(events, wrapper, context)
42+
}
43+
44+
this.on(events, wrapper, context)
45+
46+
return this
47+
}
48+
49+
off (events?: EventName, callback?: (...args: any[]) => void, context?: any) {
50+
let event: EventName | undefined, calls: EventCallbacks | undefined, _events: EventName[]
51+
if (!(calls = this.callbacks)) {
52+
return this
53+
}
54+
if (!(events || callback || context)) {
55+
delete this.callbacks
56+
return this
57+
}
58+
if (typeof events === 'symbol') {
59+
_events = [events]
60+
} else {
61+
_events = events ? events.split(Events.eventSplitter) : Object.keys(calls)
62+
}
63+
while ((event = _events.shift())) {
64+
let node: any = calls[event]
65+
delete calls[event]
66+
if (!node || !(callback || context)) {
67+
continue
68+
}
69+
const tail = node.tail
70+
while ((node = node.next) !== tail) {
71+
const cb = node.callback
72+
const ctx = node.context
73+
if ((callback && cb !== callback) || (context && ctx !== context)) {
74+
this.on(event, cb, ctx)
75+
}
76+
}
77+
}
78+
return this
79+
}
80+
81+
trigger (events: EventName, ...args: any[]) {
82+
let event: EventName | undefined, node, calls: EventCallbacks | undefined, _events: EventName[]
83+
if (!(calls = this.callbacks)) {
84+
return this
85+
}
86+
if (typeof events === 'symbol') {
87+
_events = [events]
88+
} else {
89+
_events = events.split(Events.eventSplitter)
90+
}
91+
while ((event = _events.shift())) {
92+
if ((node = calls[event])) {
93+
const tail = node.tail
94+
while ((node = node.next) !== tail) {
95+
node.callback.apply(node.context || this, args)
96+
}
97+
}
98+
}
99+
return this
100+
}
101+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { Events } from "./event-emitter"
2+
import { isFunction } from './is'
3+
4+
// Note: @tarojs/runtime 不依赖 @tarojs/taro, 所以不能改为从 @tarojs/taro 引入 (可能导致循环依赖)
5+
type TFunc = (...args: any[]) => any
6+
7+
export enum HOOK_TYPE {
8+
SINGLE,
9+
MULTI,
10+
WATERFALL
11+
}
12+
13+
interface Hook {
14+
type: HOOK_TYPE
15+
initial?: TFunc | null
16+
}
17+
18+
interface Node {
19+
next: Node
20+
context?: any
21+
callback?: TFunc
22+
}
23+
24+
export function TaroHook (type: HOOK_TYPE, initial?: TFunc): Hook {
25+
return {
26+
type,
27+
initial: initial || null
28+
}
29+
}
30+
31+
export class TaroHooks<T extends Record<string, TFunc> = any> extends Events {
32+
hooks: Record<keyof T, Hook>
33+
34+
constructor (hooks: Record<keyof T, Hook>, opts?) {
35+
super(opts)
36+
this.hooks = hooks
37+
for (const hookName in hooks) {
38+
const { initial } = hooks[hookName]
39+
if (isFunction(initial)) {
40+
this.on(hookName, initial)
41+
}
42+
}
43+
}
44+
45+
private tapOneOrMany<K extends Extract<keyof T, string>> (hookName: K, callback: T[K] | T[K][]) {
46+
const list = isFunction(callback) ? [callback] : callback
47+
list.forEach(cb => this.on(hookName, cb))
48+
}
49+
50+
tap<K extends Extract<keyof T, string>> (hookName: K, callback: T[K] | T[K][]) {
51+
const hooks = this.hooks
52+
const { type, initial } = hooks[hookName]
53+
if (type === HOOK_TYPE.SINGLE) {
54+
this.off(hookName)
55+
this.on(hookName, isFunction(callback) ? callback : callback[callback.length - 1])
56+
} else {
57+
initial && this.off(hookName, initial)
58+
this.tapOneOrMany(hookName, callback)
59+
}
60+
}
61+
62+
call<K extends Extract<keyof T, string>> (hookName: K, ...rest: Parameters<T[K]>): ReturnType<T[K]> | undefined {
63+
const hook = this.hooks[hookName]
64+
if (!hook) return
65+
66+
const { type } = hook
67+
68+
const calls = this.callbacks
69+
if (!calls) return
70+
71+
const list = calls[hookName] as { tail: Node, next: Node }
72+
73+
if (list) {
74+
const tail = list.tail
75+
let node: Node = list.next
76+
let args = rest
77+
let res
78+
79+
while (node !== tail) {
80+
res = node.callback?.apply(node.context || this, args)
81+
if (type === HOOK_TYPE.WATERFALL) {
82+
const params: any = [res]
83+
args = params
84+
}
85+
node = node.next
86+
}
87+
return res
88+
}
89+
}
90+
91+
isExist (hookName: string) {
92+
return Boolean(this.callbacks?.[hookName])
93+
}
94+
}
95+
96+
type ITaroHooks = {
97+
getEventCenter: (EventsClass: typeof Events) => Events
98+
}
99+
100+
export const hooks = new TaroHooks<ITaroHooks>({
101+
getEventCenter: TaroHook(HOOK_TYPE.SINGLE, Events => new Events())
102+
})
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { hooks } from './hooks'
2+
import { Events } from './event-emitter'
3+
4+
const eventCenter = hooks.call('getEventCenter', Events)!
5+
6+
export type EventsType = typeof Events
7+
export { eventCenter, Events }
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
export function isString (o: unknown): o is string {
2+
return typeof o === 'string'
3+
}
4+
5+
export function isUndefined (o: unknown): o is undefined {
6+
return typeof o === 'undefined'
7+
}
8+
9+
export function isNull (o: unknown): o is null {
10+
return o === null
11+
}
12+
13+
export function isObject<T> (o: unknown): o is T {
14+
return o !== null && typeof o === 'object'
15+
}
16+
17+
export function isBoolean (o: unknown): o is boolean {
18+
return o === true || o === false
19+
}
20+
21+
export function isFunction (o: unknown): o is (...args: any[]) => any {
22+
return typeof o === 'function'
23+
}
24+
25+
export function isNumber (o: unknown): o is number {
26+
if (Number.isFinite) return Number.isFinite(o)
27+
return typeof o === 'number'
28+
}
29+
30+
export function isBooleanStringLiteral (o: unknown): o is string {
31+
return o === 'true' || o === 'false' || o === '!0' || o === '!1'
32+
}
33+
34+
export function isObjectStringLiteral (o: unknown): o is string {
35+
return o === '{}'
36+
}
37+
38+
export const isArray = Array.isArray

0 commit comments

Comments
 (0)