Skip to content

Commit 3153cc0

Browse files
author
boquanfu
committed
feat: add watch utils
1 parent bfe0997 commit 3153cc0

File tree

6 files changed

+199
-43
lines changed

6 files changed

+199
-43
lines changed

README.md

+13-17
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
77
注意: 4.0.0 大版本变更了最基本的接口名,从低版本升级到 4.0.0 以上时请注意 [#60](https://github.com/wechat-miniprogram/computed/issues/60) 的问题。
88

9+
910
## 使用方法
1011

11-
### 方式一 代码片段
12+
### 快速体验
1213

1314
需要小程序基础库版本 >= 2.11.0 的环境。
1415

@@ -20,22 +21,6 @@
2021
npm install --save miniprogram-computed
2122
```
2223

23-
### 方式二 本地构建
24-
25-
将本仓库 clone 到本地,进入根目录安装 npm 依赖。
26-
27-
```shell
28-
npm install
29-
```
30-
31-
安装完成后执行
32-
33-
```shell
34-
npm run dev // 构建 dev 版本
35-
```
36-
37-
构建完毕后,根目录下的 `demo` 即为小程序代码根目录,可以将此 demo 导入开发者工具中进行体验。
38-
3924
### computed 基本用法
4025

4126
```js
@@ -188,6 +173,17 @@ ComponentWithComputed({
188173

189174
针对此问题,推荐使用 `ComponentWithComputed` 构造器代替 `Component` 构造器。
190175

176+
### 其他 API
177+
178+
有一些辅助 API 用于控制 `watch` 时的一些行为表现。可以在 `this` 上访问到这些方法;使用 Chaining API 时, `watch()` 的返回值会带有这些方法。
179+
180+
| 方法名 | 参数示例 | 说明 |
181+
| ---- | ---- | ---- |
182+
| disableWatches | disableWatches() | 暂时禁用 watch |
183+
| enableWatches | enableWatches(triggerNow) | 启用 watch ,如果 `triggerNow` 为真,立刻触发所有 watch |
184+
| triggerAllWatches | triggerAllWatches() | 立刻触发所有 watch 一次 |
185+
186+
191187
## 常见问题说明
192188

193189
### 我应该使用 computed 还是 watch ?

UPDATE.md

+4
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,7 @@
4646
## 6.0.0
4747

4848
- 更新到 miniprogram-api-typing v4
49+
50+
## 7.0.0
51+
52+
- 新增一些 watch 辅助接口

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "miniprogram-computed",
3-
"version": "6.0.2",
3+
"version": "7.0.0",
44
"description": "Computed & watch - wechat miniprogram custom component extend behavior",
55
"main": "dist/index.js",
66
"types": "types/index.d.ts",

src/behavior.ts

+69-21
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,15 @@ interface ComputedWatchInfo {
2828
computedUpdaters: Array<(...args: unknown[]) => boolean>
2929
computedRelatedPathValues: Array<Array<dataTracer.RelatedPathValue>>
3030
watchCurVal: Array<unknown>
31-
_triggerFromComputedAttached: Record<string, boolean>
31+
watchDisabled: boolean
3232
}
3333

3434
enum ComputedWatchInitStatus {
3535
CREATED,
3636
ATTACHED,
37+
DISABLE_WATCHES,
38+
ENABLE_WATCHES,
39+
CALL_WATCHES,
3740
}
3841

3942
let computedWatchDefIdInc = 0
@@ -53,22 +56,24 @@ class ComputedBuilder {
5356
private computedWatchDefId = computedWatchDefIdInc++
5457
private computedList: Array<[string, (data: Record<string, unknown>) => unknown]> = []
5558
private watchList: Array<DataPathWithOptions[]> = []
59+
private watchFunctions: Array<(...args: any[]) => void> = []
5660

5761
constructor() {
5862
const computedWatchDefId = this.computedWatchDefId
5963
const computedList = this.computedList
6064
const watchList = this.watchList
65+
const watchFunctions = this.watchFunctions
6166
this.observersItems.push({
6267
fields: '_computedWatchInit',
6368
observer(this: BehaviorExtend) {
6469
const status = this.data._computedWatchInit
6570
if (status === ComputedWatchInitStatus.CREATED) {
6671
// init data fields
67-
const computedWatchInfo = {
72+
const computedWatchInfo: ComputedWatchInfo = {
6873
computedUpdaters: [],
6974
computedRelatedPathValues: new Array(computedList.length),
7075
watchCurVal: new Array(watchList.length),
71-
_triggerFromComputedAttached: Object.create(null),
76+
watchDisabled: false,
7277
}
7378
if (!this._computedWatchInfo) this._computedWatchInfo = {}
7479
this._computedWatchInfo[computedWatchDefId] = computedWatchInfo
@@ -95,7 +100,6 @@ class ComputedBuilder {
95100
this.setData({
96101
[targetField]: dataTracer.unwrap(val),
97102
})
98-
computedWatchInfo._triggerFromComputedAttached[targetField] = true
99103
computedWatchInfo.computedRelatedPathValues[index] = relatedPathValuesOnDef
100104

101105
// will be invoked when setData is called
@@ -140,6 +144,14 @@ class ComputedBuilder {
140144
}
141145
computedWatchInfo.computedUpdaters.push(updateValueAndRelatedPaths)
142146
})
147+
} else if (status === ComputedWatchInitStatus.DISABLE_WATCHES) {
148+
const computedWatchInfo = this._computedWatchInfo[computedWatchDefId]
149+
computedWatchInfo.watchDisabled = true
150+
} else if (status === ComputedWatchInitStatus.ENABLE_WATCHES) {
151+
const computedWatchInfo = this._computedWatchInfo[computedWatchDefId]
152+
computedWatchInfo.watchDisabled = false
153+
} else if (status === ComputedWatchInitStatus.CALL_WATCHES) {
154+
watchFunctions.forEach((func) => func.call(this))
143155
}
144156
},
145157
})
@@ -170,32 +182,20 @@ class ComputedBuilder {
170182
}
171183

172184
addWatch(watchPath: string, listener: (args: any) => void) {
185+
const computedWatchDefId = this.computedWatchDefId
173186
const paths = dataPath.parseMultiDataPaths(watchPath)
174187
const index = this.watchList.length
175188
this.watchList.push(paths)
176-
const computedWatchDefId = this.computedWatchDefId
189+
this.watchFunctions.push(function (this: BehaviorExtend) {
190+
const args = paths.map(({ path }) => dataPath.getDataOnPath(this.data, path))
191+
listener.apply(this, args)
192+
})
177193
this.observersItems.push({
178194
fields: watchPath,
179195
observer(this: BehaviorExtend) {
180196
if (!this._computedWatchInfo) return
181197
const computedWatchInfo = this._computedWatchInfo[computedWatchDefId]
182198
if (!computedWatchInfo) return
183-
// (issue #58) ignore watch func when trigger by computed attached
184-
if (Object.keys(computedWatchInfo._triggerFromComputedAttached).length) {
185-
const pathsMap: Record<string, boolean> = {}
186-
paths.forEach((path) => (pathsMap[path.path[0]] = true))
187-
for (const computedVal in computedWatchInfo._triggerFromComputedAttached) {
188-
if (computedWatchInfo._triggerFromComputedAttached[computedVal]) {
189-
if (
190-
pathsMap[computedVal] &&
191-
computedWatchInfo._triggerFromComputedAttached[computedVal]
192-
) {
193-
computedWatchInfo._triggerFromComputedAttached[computedVal] = false
194-
return
195-
}
196-
}
197-
}
198-
}
199199
const oldVal = computedWatchInfo.watchCurVal[index]
200200

201201
// get new watching field value
@@ -207,6 +207,7 @@ class ComputedBuilder {
207207
options.deepCmp ? deepClone(val) : val,
208208
)
209209
computedWatchInfo.watchCurVal[index] = curVal
210+
if (computedWatchInfo.watchDisabled) return
210211

211212
// compare
212213
let changed = false
@@ -245,6 +246,29 @@ export const behavior = Behavior({
245246
},
246247
},
247248

249+
methods: {
250+
disableWatches() {
251+
this.setData({
252+
_computedWatchInit: ComputedWatchInitStatus.DISABLE_WATCHES,
253+
})
254+
},
255+
enableWatches(callWatchesImmediately: boolean) {
256+
this.setData({
257+
_computedWatchInit: ComputedWatchInitStatus.ENABLE_WATCHES,
258+
})
259+
if (callWatchesImmediately) {
260+
this.setData({
261+
_computedWatchInit: ComputedWatchInitStatus.CALL_WATCHES,
262+
})
263+
}
264+
},
265+
triggerAllWatches() {
266+
this.setData({
267+
_computedWatchInit: ComputedWatchInitStatus.CALL_WATCHES,
268+
})
269+
},
270+
},
271+
248272
definitionFilter(defFields: any & BehaviorExtend) {
249273
const computedDef = defFields.computed
250274
const watchDef = defFields.watch
@@ -473,6 +497,29 @@ export function computed<
473497
return ctx.data as any
474498
}
475499

500+
export const generateWatches = (self: BehaviorExtend) => ({
501+
disableWatches() {
502+
self.setData({
503+
_computedWatchInit: ComputedWatchInitStatus.DISABLE_WATCHES,
504+
})
505+
},
506+
enableWatches(triggerWatchesImmediately: boolean) {
507+
self.setData({
508+
_computedWatchInit: ComputedWatchInitStatus.ENABLE_WATCHES,
509+
})
510+
if (triggerWatchesImmediately) {
511+
self.setData({
512+
_computedWatchInit: ComputedWatchInitStatus.CALL_WATCHES,
513+
})
514+
}
515+
},
516+
triggerAllWatches() {
517+
self.setData({
518+
_computedWatchInit: ComputedWatchInitStatus.CALL_WATCHES,
519+
})
520+
},
521+
})
522+
476523
export const watch = (
477524
ctx: adapter.builder.BuilderContext<any, any, any>,
478525
watchPath: string,
@@ -484,4 +531,5 @@ export const watch = (
484531
builder.observersItems.forEach(({ fields, observer }) => {
485532
ctx.observer(fields as any, observer)
486533
})
534+
return generateWatches(ctx.self)
487535
}

test/env.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export const renderComponent: (
77
Component: adapter.ComponentConstructor,
88
env: { Behavior: adapter.BehaviorConstructor, Page: adapter.PageConstructor },
99
) => void,
10-
) => adapter.glassEasel.GeneralComponent
10+
) => adapter.component.GeneralComponent
1111

1212
export const defineComponent: (
1313
path: string | undefined,
@@ -16,4 +16,4 @@ export const defineComponent: (
1616
Component: adapter.ComponentConstructor,
1717
env: { Behavior: adapter.BehaviorConstructor },
1818
) => void,
19-
) => adapter.glassEasel.GeneralComponent
19+
) => void

test/index.test.ts

+110-2
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,7 @@ describe('computed behavior', () => {
895895
expect(c2TriggerCount).toBe(2)
896896
})
897897

898-
test('ignore watch func when trigger by computed attached', () => {
898+
test('call watch func when trigger by computed attached', () => {
899899
let cTriggerCount = 0
900900
let dTriggerCount = 0
901901
const behA = BehaviorWithComputed({
@@ -939,8 +939,116 @@ describe('computed behavior', () => {
939939
} as any)
940940
}) as any
941941
expect(innerHTML(component)).toBe('<view>40</view>')
942+
expect(cTriggerCount).toBe(2)
943+
expect(dTriggerCount).toBe(1)
944+
})
945+
946+
test('disable and enable watches', () => {
947+
let cTriggerCount = 0
948+
const component = renderComponent(undefined, '<view>{{c}}</view>', (Component) => {
949+
Component({
950+
behaviors: [computedBehavior],
951+
data: {
952+
c: 1,
953+
},
954+
watch: {
955+
c() {
956+
cTriggerCount += 1
957+
},
958+
},
959+
created() {
960+
this.disableWatches()
961+
},
962+
methods: {
963+
set(n: number) {
964+
this.setData({
965+
c: n,
966+
})
967+
},
968+
enable(b: boolean) {
969+
this.enableWatches(b)
970+
},
971+
disable() {
972+
this.disableWatches()
973+
},
974+
trigger() {
975+
this.triggerAllWatches()
976+
},
977+
},
978+
} as any)
979+
}) as any
980+
component.set(2)
981+
expect(cTriggerCount).toBe(0)
982+
component.enable(false)
983+
expect(cTriggerCount).toBe(0)
984+
component.set(2)
985+
expect(cTriggerCount).toBe(0)
986+
component.disable()
987+
component.trigger()
988+
expect(cTriggerCount).toBe(1)
989+
component.enable(true)
990+
expect(cTriggerCount).toBe(2)
991+
component.set(3)
992+
expect(cTriggerCount).toBe(3)
993+
component.trigger()
994+
expect(cTriggerCount).toBe(4)
995+
})
996+
997+
test('disable and enable watches with chaining API', () => {
998+
let cTriggerCount = 0
999+
interface Watcher {
1000+
set(n: number): void
1001+
enable(b: boolean): void
1002+
disable(): void
1003+
trigger(): void
1004+
}
1005+
const watcherTrait = Behavior.trait<Watcher>()
1006+
const comp = renderComponent(undefined, '<view>{{c}}</view>', (Component) => {
1007+
Component()
1008+
.data(() => ({
1009+
c: 1,
1010+
}))
1011+
.init((ctx) => {
1012+
const { setData, implement, lifetime } = ctx
1013+
const watcher = watch(ctx, 'c', () => cTriggerCount += 1)
1014+
lifetime('created', () => {
1015+
watcher.disableWatches()
1016+
})
1017+
implement(watcherTrait, {
1018+
set(n: number) {
1019+
setData({
1020+
c: n,
1021+
})
1022+
},
1023+
enable(b: boolean) {
1024+
watcher.enableWatches(b)
1025+
},
1026+
disable() {
1027+
watcher.disableWatches()
1028+
},
1029+
trigger() {
1030+
watcher.triggerAllWatches()
1031+
},
1032+
})
1033+
})
1034+
.register()
1035+
})
1036+
const component = comp.traitBehavior(watcherTrait)!
1037+
component.set(2)
1038+
expect(cTriggerCount).toBe(0)
1039+
component.enable(false)
1040+
expect(cTriggerCount).toBe(0)
1041+
component.set(2)
1042+
expect(cTriggerCount).toBe(0)
1043+
component.disable()
1044+
component.trigger()
9421045
expect(cTriggerCount).toBe(1)
943-
expect(dTriggerCount).toBe(0)
1046+
component.enable(true)
1047+
expect(cTriggerCount).toBe(2)
1048+
component.set(3)
1049+
expect(cTriggerCount).toBe(3)
1050+
component.trigger()
1051+
expect(cTriggerCount).toBe(4)
9441052
})
9451053

9461054
test('computed Array', () => {

0 commit comments

Comments
 (0)