Skip to content

Commit 43bac46

Browse files
committed
feat: initial coding
1 parent 0c85fca commit 43bac46

File tree

8 files changed

+280
-256
lines changed

8 files changed

+280
-256
lines changed

README.md

+19
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,25 @@ Component()
115115
.register()
116116
```
117117

118+
### 关于 Trait Behaviors
119+
120+
这个 Polyfill 提供了对 trait behaviors 的支持。
121+
122+
但是,不能使用 `this.hasBehavior(...)` 来判定 trait behaviors ,应使用 `this.traitBehavior(...)` 。例如:
123+
124+
```ts
125+
const helloTrait = Behavior.trait<{ hello: () => string }>()
126+
127+
Component()
128+
.init(({ self, implement, lifetime }) => {
129+
implement(helloTrait, { hello: () => 'world' } })
130+
lifetime('attached', () => {
131+
const hello = self.traitBehavior(helloTrait).hello()
132+
console.info(hello)
133+
})
134+
})
135+
```
136+
118137
### 其他不支持的细节
119138

120139
* 链式调用上的 `lifetime` `pageLifetime` `observer` 不支持设置 `once` 参数。

src/base.ts

+123-47
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export type PropertyType = WechatMiniprogram.Component.AllProperty
2323
export type PropertyToData<T extends PropertyType> = WechatMiniprogram.Component.PropertyToData<T>
2424
type IAnyObject = WechatMiniprogram.IAnyObject
2525

26-
type NewFieldList<TObject, TNewObject> =
26+
export type NewFieldList<TObject, TNewObject> =
2727
Extract<keyof TObject, keyof TNewObject> extends never ? TNewObject : never
2828

2929
type Lifetimes = {
@@ -47,7 +47,12 @@ export type Component<
4747
TProperty extends PropertyList,
4848
TMethod extends MethodList,
4949
TExtraThisFields extends DataList = Empty,
50-
> = WechatMiniprogram.Component.Instance<TData, TProperty, TMethod, []> & TExtraThisFields
50+
> = WechatMiniprogram.Component.Instance<TData, TProperty, TMethod, [], Empty> &
51+
TExtraThisFields & {
52+
traitBehavior: <TOut extends { [x: string]: any }>(
53+
traitBehavior: TraitBehavior<any, TOut>,
54+
) => TOut | undefined
55+
}
5156

5257
export type ComponentMethod = (...any: []) => void
5358

@@ -103,11 +108,18 @@ class ChainingPolyfillMetadata {
103108
}
104109
this.traitGroup.implement(trait_behavior, impl)
105110
},
106-
observer: () => {
111+
observer: (paths: any, func: (newValue: any) => void) => {
107112
if (this.initDone) {
108113
throw new Error('Cannot execute init-time functions after initialization')
109114
}
110-
// TODO
115+
const fields = typeof paths === 'string' ? paths : paths.join(', ')
116+
const funcs = this.observers[fields]
117+
if (!funcs) {
118+
throw new Error(
119+
'The `observer` call should has a corresponding `.observer` call in chaining',
120+
)
121+
}
122+
funcs.add(func)
111123
},
112124
lifetime: (name: string, func: () => void) => {
113125
if (this.initDone) {
@@ -142,12 +154,14 @@ class ChainingPolyfillMetadata {
142154
}
143155

144156
callLifetime(caller: unknown, name: string, ...args: any[]) {
145-
const funcs = getChainingPolyfillMetadata(self).lifetimes[name] ?? []
157+
const funcs = this.lifetimes[name]
158+
if (!funcs) return
146159
funcs.call(caller, args)
147160
}
148161

149162
callPageLifetime(caller: unknown, name: string, ...args: any[]) {
150-
const funcs = getChainingPolyfillMetadata(self).pageLifetimes[name] ?? []
163+
const funcs = this.pageLifetimes[name]
164+
if (!funcs) return
151165
funcs.call(caller, args)
152166
}
153167
}
@@ -156,47 +170,56 @@ const getChainingPolyfillMetadata = (comp: GeneralComponent): ChainingPolyfillMe
156170
return comp._$chainingPolyfill
157171
}
158172

173+
const takeChainingPolyfillInitData = (
174+
comp: GeneralComponent,
175+
): ChainingPolyfillInitData | undefined => {
176+
const id = comp.data._$chainingPolyfillId
177+
if (!(id >= 0)) return undefined
178+
comp.data._$chainingPolyfillId = -1
179+
const initData = initDataMap[id]
180+
const cloneMap = (src: { [k: string]: FuncArr<any> }) => {
181+
const dest = {} as typeof src
182+
const keys = Object.keys(src)
183+
for (let i = 0; i < keys.length; i += 1) {
184+
const key = keys[i]!
185+
dest[key] = src[key].clone()
186+
}
187+
return dest
188+
}
189+
return {
190+
lifetimes: cloneMap(initData.lifetimes),
191+
pageLifetimes: cloneMap(initData.lifetimes),
192+
observers: cloneMap(initData.observers),
193+
initFuncs: initData.initFuncs.clone(),
194+
}
195+
}
196+
159197
const chainingPolyfillBehavior = Behavior({
160198
lifetimes: {
161199
created() {
162200
const self = this as any
163-
if (self.data._$chainingPolyfillMetadata) {
164-
const initData = self.data._$chainingPolyfillMetadata as ChainingPolyfillInitData
165-
self.data._$chainingPolyfillMetadata = null
201+
const initData = takeChainingPolyfillInitData(self)
202+
if (initData) {
166203
const chainingPolyfillMetadata = new ChainingPolyfillMetadata(initData)
167204
self._$chainingPolyfill = chainingPolyfillMetadata
168205
const ctx = chainingPolyfillMetadata.generateBuilderContext(self)
169206
initData.initFuncs.call(self, [ctx, chainingPolyfillMetadata])
170207
chainingPolyfillMetadata.setInitDone()
171208
}
172-
getChainingPolyfillMetadata(this).callLifetime(this, 'created')
173-
},
174-
attached() {
175-
getChainingPolyfillMetadata(this).callLifetime(this, 'attached')
176-
},
177-
moved() {
178-
getChainingPolyfillMetadata(this).callLifetime(this, 'moved')
179-
},
180-
detached() {
181-
getChainingPolyfillMetadata(this).callLifetime(this, 'detached')
182-
},
183-
ready() {
184-
getChainingPolyfillMetadata(this).callLifetime(this, 'ready')
185209
},
186210
},
187-
pageLifetimes: {
188-
show() {
189-
getChainingPolyfillMetadata(this).callPageLifetime(this, 'show')
190-
},
191-
hide() {
192-
getChainingPolyfillMetadata(this).callPageLifetime(this, 'hide')
193-
},
194-
resize(size) {
195-
getChainingPolyfillMetadata(this).callPageLifetime(this, 'resize', size)
211+
methods: {
212+
traitBehavior<TOut extends { [x: string]: any }>(
213+
traitBehavior: TraitBehavior<any, TOut>,
214+
): TOut | undefined {
215+
return getChainingPolyfillMetadata(this).traitGroup.get(traitBehavior)
196216
},
197217
},
198218
})
199219

220+
let behaviorIdInc = 1
221+
const initDataMap: { [id: number]: ChainingPolyfillInitData } = {}
222+
200223
export class BaseBehaviorBuilder<
201224
TPrevData extends DataList = Empty,
202225
TData extends DataList = Empty,
@@ -207,26 +230,58 @@ export class BaseBehaviorBuilder<
207230
TComponentExport = never,
208231
TExtraThisFields extends DataList = Empty,
209232
> {
233+
behaviorId: number
234+
private _$initData = {
235+
lifetimes: {},
236+
pageLifetimes: {},
237+
observers: {},
238+
initFuncs: new FuncArr(),
239+
} as ChainingPolyfillInitData
210240
protected _$definition = {
211-
data: {
212-
_$chainingPolyfillMetadata: {
213-
lifetimes: {},
214-
observers: {},
215-
} as ChainingPolyfillInitData,
216-
} as DataList,
241+
data: {} as DataList,
217242
properties: {} as WechatMiniprogram.Behavior.PropertyOption,
218243
methods: {} as MethodList,
219244
behaviors: [chainingPolyfillBehavior],
220-
lifetimes: {} as { [key: string]: GeneralFuncType },
221-
pageLifetimes: {} as { [key: string]: GeneralFuncType },
245+
lifetimes: {
246+
created() {
247+
getChainingPolyfillMetadata(this).callLifetime(this, 'created')
248+
},
249+
attached() {
250+
getChainingPolyfillMetadata(this).callLifetime(this, 'attached')
251+
},
252+
moved() {
253+
getChainingPolyfillMetadata(this).callLifetime(this, 'moved')
254+
},
255+
detached() {
256+
getChainingPolyfillMetadata(this).callLifetime(this, 'detached')
257+
},
258+
ready() {
259+
getChainingPolyfillMetadata(this).callLifetime(this, 'ready')
260+
},
261+
},
262+
pageLifetimes: {
263+
show() {
264+
getChainingPolyfillMetadata(this).callPageLifetime(this, 'show')
265+
},
266+
hide() {
267+
getChainingPolyfillMetadata(this).callPageLifetime(this, 'hide')
268+
},
269+
resize(size: unknown) {
270+
getChainingPolyfillMetadata(this).callPageLifetime(this, 'resize', size)
271+
},
272+
},
222273
observers: [] as { fields: string; observer: GeneralFuncType }[],
223274
relations: {} as { [key: string]: WechatMiniprogram.Component.RelationOption },
224275
externalClasses: [] as string[],
225276
export: undefined as undefined | (() => TComponentExport),
277+
options: undefined as undefined | WechatMiniprogram.Component.ComponentOptions,
226278
}
227279

228-
protected _$getChainingPolyfillInitData(): ChainingPolyfillInitData {
229-
return this._$definition.data.chainingPolyfillMetadata
280+
constructor() {
281+
this.behaviorId = behaviorIdInc
282+
behaviorIdInc += 1
283+
this._$definition.data._$chainingPolyfillId = this.behaviorId
284+
initDataMap[this.behaviorId] = this._$initData
230285
}
231286

232287
/** Add external classes */
@@ -344,7 +399,17 @@ export class BaseBehaviorBuilder<
344399
func: (this: Component<TData, TProperty, TMethod, TExtraThisFields>, ...args: any[]) => any,
345400
): ResolveBehaviorBuilder<this> {
346401
const fields = typeof paths === 'string' ? paths : paths.join(', ')
347-
this._$definition.observers.push({ fields, observer: func })
402+
const assistObservers = new FuncArr()
403+
assistObservers.add(func)
404+
const observer = function (
405+
this: Component<TData, TProperty, TMethod, TExtraThisFields>,
406+
...args: any[]
407+
) {
408+
assistObservers.call(this, args)
409+
}
410+
this._$definition.observers.push({ fields, observer })
411+
const observers = this._$initData.observers
412+
observers[fields] = assistObservers
348413
return this as any
349414
}
350415

@@ -358,7 +423,7 @@ export class BaseBehaviorBuilder<
358423
...args: Parameters<Lifetimes[L]>
359424
) => ReturnType<Lifetimes[L]>,
360425
): ResolveBehaviorBuilder<this> {
361-
const lifetimes = this._$getChainingPolyfillInitData().lifetimes
426+
const lifetimes = this._$initData.lifetimes
362427
if (!lifetimes[name]) {
363428
lifetimes[name] = new FuncArr()
364429
}
@@ -376,7 +441,7 @@ export class BaseBehaviorBuilder<
376441
...args: Parameters<PageLifetimes[L]>
377442
) => any,
378443
): ResolveBehaviorBuilder<this> {
379-
const pageLifetimes = this._$getChainingPolyfillInitData().pageLifetimes
444+
const pageLifetimes = this._$initData.pageLifetimes
380445
if (!pageLifetimes[name]) {
381446
pageLifetimes[name] = new FuncArr()
382447
}
@@ -422,7 +487,7 @@ export class BaseBehaviorBuilder<
422487
TExtraThisFields
423488
>
424489
> {
425-
this._$getChainingPolyfillInitData().initFuncs.add((ctx, meta: ChainingPolyfillMetadata) => {
490+
this._$initData.initFuncs.add((ctx, meta: ChainingPolyfillMetadata) => {
426491
const comp = ctx.self
427492
const exported = func.call(comp, ctx)
428493
meta.handleBuilderContextExport(comp, exported)
@@ -436,7 +501,7 @@ export class BaseBehaviorBuilder<
436501
TNewMethod extends MethodList = Empty,
437502
TNewComponentExport = never,
438503
>(
439-
definition: WechatMiniprogram.Component.Options<TNewData, TNewProperty, TNewMethod, any> &
504+
definition: ClassicDefinition<TNewData, TNewProperty, TNewMethod> &
440505
ThisType<
441506
Component<
442507
TData & TNewData,
@@ -475,7 +540,7 @@ export class BaseBehaviorBuilder<
475540
export: exports,
476541
} = definition
477542
if (behaviors) {
478-
this._$definition.behaviors.push(...behaviors)
543+
this._$definition.behaviors.push(...(behaviors as any[]))
479544
}
480545
if (rawProperties) {
481546
Object.assign(this._$definition.properties, rawProperties)
@@ -567,3 +632,14 @@ export interface BuilderContext<
567632
type EventListener<TDetail extends IAnyObject> = (ev: Event<TDetail>) => boolean | void
568633

569634
type Event<TDetail extends IAnyObject> = WechatMiniprogram.CustomEvent<TDetail, any, any, any>
635+
636+
export type ClassicDefinition<
637+
TData extends WechatMiniprogram.Component.DataOption,
638+
TProperty extends WechatMiniprogram.Component.PropertyOption,
639+
TMethod extends WechatMiniprogram.Component.MethodOption,
640+
> = Optional<WechatMiniprogram.Component.Data<TData>> &
641+
Optional<WechatMiniprogram.Component.Property<TProperty>> &
642+
Optional<WechatMiniprogram.Component.Method<TMethod>> &
643+
Optional<WechatMiniprogram.Component.Behavior<WechatMiniprogram.Component.BehaviorOption>> &
644+
Optional<WechatMiniprogram.Component.Lifetimes> &
645+
Partial<WechatMiniprogram.Component.OtherOption>

0 commit comments

Comments
 (0)