fix(runtime-core): ensure this context for $nextTick callback

fix #2282
This commit is contained in:
Evan You 2020-10-05 18:18:38 -04:00
parent f4119249f2
commit 5c3e8e9840
3 changed files with 13 additions and 4 deletions

View File

@ -69,7 +69,7 @@ describe('component: proxy', () => {
expect('count' in instanceProxy).toBe(false) expect('count' in instanceProxy).toBe(false)
}) })
test('public properties', () => { test('public properties', async () => {
let instance: ComponentInternalInstance let instance: ComponentInternalInstance
let instanceProxy: any let instanceProxy: any
const Comp = { const Comp = {
@ -96,6 +96,11 @@ describe('component: proxy', () => {
expect(instanceProxy.$options).toBe(instance!.type) expect(instanceProxy.$options).toBe(instance!.type)
expect(() => (instanceProxy.$data = {})).toThrow(TypeError) expect(() => (instanceProxy.$data = {})).toThrow(TypeError)
expect(`Attempting to mutate public property "$data"`).toHaveBeenWarned() expect(`Attempting to mutate public property "$data"`).toHaveBeenWarned()
const nextTickThis = await instanceProxy.$nextTick(function(this: any) {
return this
})
expect(nextTickThis).toBe(instanceProxy)
}) })
test('user attached properties', async () => { test('user attached properties', async () => {

View File

@ -213,7 +213,7 @@ const publicPropertiesMap: PublicPropertiesMap = extend(Object.create(null), {
$emit: i => i.emit, $emit: i => i.emit,
$options: i => (__FEATURE_OPTIONS_API__ ? resolveMergedOptions(i) : i.type), $options: i => (__FEATURE_OPTIONS_API__ ? resolveMergedOptions(i) : i.type),
$forceUpdate: i => () => queueJob(i.update), $forceUpdate: i => () => queueJob(i.update),
$nextTick: () => nextTick, $nextTick: i => nextTick.bind(i.proxy!),
$watch: i => (__FEATURE_OPTIONS_API__ ? instanceWatch.bind(i) : NOOP) $watch: i => (__FEATURE_OPTIONS_API__ ? instanceWatch.bind(i) : NOOP)
} as PublicPropertiesMap) } as PublicPropertiesMap)

View File

@ -1,5 +1,6 @@
import { ErrorCodes, callWithErrorHandling } from './errorHandling' import { ErrorCodes, callWithErrorHandling } from './errorHandling'
import { isArray } from '@vue/shared' import { isArray } from '@vue/shared'
import { ComponentPublicInstance } from './componentPublicInstance'
export interface SchedulerJob { export interface SchedulerJob {
(): void (): void
@ -48,9 +49,12 @@ let currentPreFlushParentJob: SchedulerJob | null = null
const RECURSION_LIMIT = 100 const RECURSION_LIMIT = 100
type CountMap = Map<SchedulerJob | SchedulerCb, number> type CountMap = Map<SchedulerJob | SchedulerCb, number>
export function nextTick(fn?: () => void): Promise<void> { export function nextTick(
this: ComponentPublicInstance | void,
fn?: () => void
): Promise<void> {
const p = currentFlushPromise || resolvedPromise const p = currentFlushPromise || resolvedPromise
return fn ? p.then(fn) : p return fn ? p.then(this ? fn.bind(this) : fn) : p
} }
export function queueJob(job: SchedulerJob) { export function queueJob(job: SchedulerJob) {