Skip to content

Commit

Permalink
feat(reactive-vue): add observer option scheduler (#2672)
Browse files Browse the repository at this point in the history
  • Loading branch information
muuyao authored Dec 26, 2021
1 parent 03e9e7d commit ca55e48
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 19 deletions.
48 changes: 48 additions & 0 deletions packages/reactive-vue/src/__tests__/observer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,51 @@ test('observer: component with setup', async () => {
expect(wrapper.find('button').text()).toBe('12')
wrapper.destroy()
})

test('observer: component scheduler', async () => {
let schedulerRequest = null

const model = observable<any>({
age: 10,
setAge() {
model.age++
},
})
const Component = observer(
{
data() {
return {
model,
}
},
render(this: any, h: CreateElement) {
return h('button', {
on: { click: this.model.setAge },
domProps: { textContent: this.model.age },
})
},
},
{
scheduler: (update) => {
clearTimeout(schedulerRequest)
schedulerRequest = setTimeout(() => {
update()
}, 100)
},
}
)
const wrapper = shallowMount(Component)

expect(wrapper.find('button').text()).toBe('10')

wrapper.find('button').trigger('click')
await new Promise((r) => setTimeout(r, 150))
expect(wrapper.find('button').text()).toBe('11')

// test second render
wrapper.find('button').trigger('click')
await new Promise((r) => setTimeout(r, 150))
expect(wrapper.find('button').text()).toBe('12')

wrapper.destroy()
})
38 changes: 25 additions & 13 deletions packages/reactive-vue/src/hooks/useObserver.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,41 @@
import { autorun } from '@formily/reactive'
import { Tracker } from '@formily/reactive'
import { getCurrentInstance, onBeforeUnmount, isVue3 } from 'vue-demi'
import { IObserverOptions } from '../types'

/* istanbul ignore next */
export const useObserver = () => {
export const useObserver = (options?: IObserverOptions) => {
if (isVue3) {
const vm = getCurrentInstance()

let dispose: () => void | undefined

onBeforeUnmount(() => {
if (dispose) {
dispose()
let tracker: Tracker = null
const disposeTracker = () => {
if (tracker) {
tracker.dispose()
tracker = null
}
})
}

onBeforeUnmount(disposeTracker)

Object.defineProperty(vm, 'update', {
get() {
// https://github.com/alibaba/formily/issues/2655
return vm['_updateEffect'] || {}
},
set(newValue) {
if (dispose) {
dispose()
}
dispose = autorun(newValue)
disposeTracker()

const update = () => tracker.track(newValue)

tracker = new Tracker(() => {
if (options?.scheduler) {
options?.scheduler?.(update)
} else {
update()
}
})

update()

vm['_updateEffect'] = newValue
},
})
Expand Down
6 changes: 2 additions & 4 deletions packages/reactive-vue/src/observer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import { observer as observerV3 } from './observerInVue3'
import collectData from './collectData'
import { IObserverOptions } from '../types'

export function observer<C>(
baseComponent: C,
options?: IObserverOptions & { forwardRef: true }
): C {
export function observer<C>(baseComponent: C, options?: IObserverOptions): C {
/* istanbul ignore else */
if (isVue2) {
return observerV2(baseComponent, options)
} else {
Expand Down
8 changes: 7 additions & 1 deletion packages/reactive-vue/src/observer/observerInVue2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ function observer(Component: any, observerOptions?: IObserverOptions): any {
return this
}

const tracker = new Tracker(reactiveRender)
const tracker = new Tracker(() => {
if (observerOptions?.scheduler) {
observerOptions?.scheduler?.(reactiveRender)
} else {
reactiveRender()
}
})

this[disposerSymbol] = tracker.dispose

Expand Down
2 changes: 1 addition & 1 deletion packages/reactive-vue/src/observer/observerInVue3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const observer = function (opts: any, options?: IObserverOptions): any {
name,
...opts,
setup(props: Record<string, any>, context: any) {
useObserver()
useObserver(options)
return opts?.setup?.(props, context)
},
}
Expand Down
1 change: 1 addition & 0 deletions packages/reactive-vue/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export interface IObserverOptions {
name?: string
scheduler?: (updater: () => void) => void
}
13 changes: 13 additions & 0 deletions packages/reactive/docs/api/vue/observer.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@

In Vue, the component rendering method is changed to Reaction, and dependencies are collected every time the view is re-rendered, and dependencies are updated automatically to re-render.

### Signature

```ts
interface IObserverOptions {
scheduler?: (updater: () => void) => void //The scheduler, you can manually control the timing of the update
name?: string //name of the packaged component
}

interface observer<T extends VueComponent> {
(component: T, options?: IObserverOptions): T
}
```

## Example

```html
Expand Down
13 changes: 13 additions & 0 deletions packages/reactive/docs/api/vue/observer.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@

在 Vue 中,将组件渲染方法变成 Reaction,每次视图重新渲染就会收集依赖,依赖更新会自动重渲染。

### 签名

```ts
interface IObserverOptions {
scheduler?: (updater: () => void) => void //调度器,可以手动控制更新时机
name?: string //包装后的组件的name
}

interface observer<T extends VueComponent> {
(component: T, options?: IObserverOptions): T
}
```

## 用例

```html
Expand Down

0 comments on commit ca55e48

Please sign in to comment.