diff --git a/src/core/config.js b/src/core/config.js index f25f09d1f35..cae9d3dd395 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -28,6 +28,9 @@ export type Config = { getTagNamespace: (x?: string) => string | void; mustUseProp: (tag: string, type: ?string, name: string) => boolean; + // private + async: boolean; + // legacy _lifecycleHooks: Array; }; @@ -114,6 +117,12 @@ export default ({ */ mustUseProp: no, + /** + * Perform updates asynchronously. Intended to be used by Vue Test Utils + * This will significantly reduce performance if set to false. + */ + async: true, + /** * Exposed for legacy reasons */ diff --git a/src/core/observer/dep.js b/src/core/observer/dep.js index abf3b275ce4..1dd37783490 100644 --- a/src/core/observer/dep.js +++ b/src/core/observer/dep.js @@ -2,6 +2,7 @@ import type Watcher from './watcher' import { remove } from '../util/index' +import config from '../config' let uid = 0 @@ -36,6 +37,12 @@ export default class Dep { notify () { // stabilize the subscriber list first const subs = this.subs.slice() + if (process.env.NODE_ENV !== 'production' && !config.async) { + // subs aren't sorted in scheduler if not running async + // we need to sort them now to make sure they fire in correct + // order + subs.sort((a, b) => a.id - b.id) + } for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } diff --git a/src/core/observer/scheduler.js b/src/core/observer/scheduler.js index e7ccf57dd25..176ee2c36f7 100644 --- a/src/core/observer/scheduler.js +++ b/src/core/observer/scheduler.js @@ -145,6 +145,11 @@ export function queueWatcher (watcher: Watcher) { // queue the flush if (!waiting) { waiting = true + + if (process.env.NODE_ENV !== 'production' && !config.async) { + flushSchedulerQueue() + return + } nextTick(flushSchedulerQueue) } } diff --git a/test/unit/features/global-api/config.spec.js b/test/unit/features/global-api/config.spec.js index 1edfc3c7829..f2e5074ce72 100644 --- a/test/unit/features/global-api/config.spec.js +++ b/test/unit/features/global-api/config.spec.js @@ -55,4 +55,71 @@ describe('Global config', () => { Vue.config.ignoredElements = [] }) }) + + describe('async', () => { + it('does not update synchronously when true', () => { + const spy = jasmine.createSpy() + const vm = new Vue({ + template: `
`, + updated: spy, + data: { value: true } + }).$mount() + vm.value = false + expect(spy).not.toHaveBeenCalled() + }) + + it('updates synchronously when false', () => { + const spy = jasmine.createSpy() + Vue.config.async = false + const vm = new Vue({ + template: `
`, + updated: spy, + data: { value: true } + }).$mount() + vm.value = false + expect(spy).toHaveBeenCalled() + Vue.config.async = true + }) + + it('runs watchers in correct order when false', () => { + Vue.config.async = false + const vm = new Vue({ + template: ` +
+ {{ computed }} +
`, + props: ['prop'], + propsData: { + 'prop': [] + }, + data: () => ({ + data: '' + }), + computed: { + computed () { + return this.prop.join(',') + } + }, + watch: { + prop: 'execute' + }, + methods: { + execute () { + this.data = this.computed + } + } + }).$mount() + expect(vm.computed).toBe('') + expect(vm.data).toBe('') + + vm.prop = [1, 2, 3] + expect(vm.computed).toBe('1,2,3') + expect(vm.data).toBe('1,2,3') + + vm.prop.push(4, 5) + expect(vm.computed).toBe('1,2,3,4,5') + expect(vm.data).toBe('1,2,3,4,5') + Vue.config.async = true + }) + }) }) diff --git a/types/test/vue-test.ts b/types/test/vue-test.ts index 96949371793..326969c1637 100644 --- a/types/test/vue-test.ts +++ b/types/test/vue-test.ts @@ -73,6 +73,7 @@ class Test extends Vue { }; config.keyCodes = { esc: 27 }; config.ignoredElements = ['foo', /^ion-/]; + config.async = false } static testMethods() { diff --git a/types/vue.d.ts b/types/vue.d.ts index e0b29157cfa..179fb5fe38e 100644 --- a/types/vue.d.ts +++ b/types/vue.d.ts @@ -74,6 +74,7 @@ export interface VueConfiguration { warnHandler(msg: string, vm: Vue, trace: string): void; ignoredElements: (string | RegExp)[]; keyCodes: { [key: string]: number | number[] }; + async: boolean; } export interface VueConstructor {