From e2aa217b2bff009523a9bd4c00cf27f1497571ba Mon Sep 17 00:00:00 2001 From: Elad Frizi Date: Fri, 22 Feb 2019 21:52:10 +0200 Subject: [PATCH 1/4] moved should update logic into spreate module --- src/index.js | 34 ++++++++++++++-------------------- src/shouldUpdate.js | 12 ++++++++++++ 2 files changed, 26 insertions(+), 20 deletions(-) create mode 100644 src/shouldUpdate.js diff --git a/src/index.js b/src/index.js index e848881..e51bf6d 100644 --- a/src/index.js +++ b/src/index.js @@ -7,12 +7,12 @@ import { silentSetLazy, } from './lazy' import { getWatchedGetter } from './watch' +import { getGetterWithShouldUpdate, shouldNotUpdate } from './shouldUpdate' const prefix = '_async_computed$' -const DidNotUpdate = typeof Symbol === 'function' ? Symbol('did-not-update') : {} const AsyncComputed = { - install (Vue, pluginOptions) { + install(Vue, pluginOptions) { pluginOptions = pluginOptions || {} Vue.config @@ -20,12 +20,12 @@ const AsyncComputed = { .asyncComputed = Vue.config.optionMergeStrategies.computed Vue.mixin({ - data () { + data() { return { _asyncComputed: {}, } }, - beforeCreate () { + beforeCreate() { const optionData = this.$options.data const asyncComputed = this.$options.asyncComputed || {} @@ -40,7 +40,7 @@ const AsyncComputed = { this.$options.computed[prefix + key] = getter } - this.$options.data = function vueAsyncComputedInjectedDataFn (vm) { + this.$options.data = function vueAsyncComputedInjectedDataFn(vm) { const data = ( (typeof optionData === 'function') ? optionData.call(this, vm) @@ -58,10 +58,10 @@ const AsyncComputed = { return data } }, - created () { + created() { for (const key in this.$options.asyncComputed || {}) { const item = this.$options.asyncComputed[key], - value = generateDefault.call(this, item, pluginOptions) + value = generateDefault.call(this, item, pluginOptions) if (isComputedLazy(item)) { silentSetLazy(this, key, value) } else { @@ -74,7 +74,7 @@ const AsyncComputed = { const watcher = newPromise => { const thisPromise = ++promiseId - if (newPromise === DidNotUpdate) { + if (shouldNotUpdate(newPromise)) { return } @@ -119,20 +119,20 @@ const AsyncComputed = { } } -function setAsyncState (vm, stateObject, state) { +function setAsyncState(vm, stateObject, state) { vm.$set(vm.$data._asyncComputed[stateObject], 'state', state) vm.$set(vm.$data._asyncComputed[stateObject], 'updating', state === 'updating') vm.$set(vm.$data._asyncComputed[stateObject], 'error', state === 'error') vm.$set(vm.$data._asyncComputed[stateObject], 'success', state === 'success') } -function getterOnly (fn) { +function getterOnly(fn) { if (typeof fn === 'function') return fn return fn.get } -function getterFn (key, fn) { +function getterFn(key, fn) { if (typeof fn === 'function') return fn let getter = fn.get @@ -142,18 +142,12 @@ function getterFn (key, fn) { } if (fn.hasOwnProperty('shouldUpdate')) { - const previousGetter = getter - getter = function getter () { - if (fn.shouldUpdate.call(this)) { - return previousGetter.call(this) - } - return DidNotUpdate - } + getter = getGetterWithShouldUpdate(fn, getter) } if (isComputedLazy(fn)) { const nonLazy = getter - getter = function lazyGetter () { + getter = function lazyGetter() { if (isLazyActive(this, key)) { return nonLazy.call(this) } else { @@ -164,7 +158,7 @@ function getterFn (key, fn) { return getter } -function generateDefault (fn, pluginOptions) { +function generateDefault(fn, pluginOptions) { let defaultValue = null if ('default' in fn) { diff --git a/src/shouldUpdate.js b/src/shouldUpdate.js new file mode 100644 index 0000000..aa32172 --- /dev/null +++ b/src/shouldUpdate.js @@ -0,0 +1,12 @@ +const DidNotUpdate = typeof Symbol === 'function' ? Symbol('did-not-update') : {} + + +export const getGetterWithShouldUpdate = (asyncProprety, currentGetter) => { + return function getter() { + return (asyncProprety.shouldUpdate.call(this)) ? + currentGetter.call(this) + : DidNotUpdate + } +} + +export const shouldNotUpdate = (value) => DidNotUpdate === value From a4bb765cd54478b57c62af40094e1e247e88c51d Mon Sep 17 00:00:00 2001 From: Elad Frizi Date: Fri, 22 Feb 2019 22:24:35 +0200 Subject: [PATCH 2/4] extarct the initialization of the data in beforeCreate into function --- src/index.js | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/index.js b/src/index.js index e51bf6d..a510a01 100644 --- a/src/index.js +++ b/src/index.js @@ -40,23 +40,7 @@ const AsyncComputed = { this.$options.computed[prefix + key] = getter } - this.$options.data = function vueAsyncComputedInjectedDataFn(vm) { - const data = ( - (typeof optionData === 'function') - ? optionData.call(this, vm) - : optionData - ) || {} - for (const key in asyncComputed) { - const item = this.$options.asyncComputed[key] - if (isComputedLazy(item)) { - initLazy(data, key) - this.$options.computed[key] = makeLazyComputed(key) - } else { - data[key] = null - } - } - return data - } + this.$options.data = initDataWithAsyncComputed(this.$options) }, created() { for (const key in this.$options.asyncComputed || {}) { @@ -119,6 +103,28 @@ const AsyncComputed = { } } +function initDataWithAsyncComputed(options) { + const optionData = options.data + const asyncComputed = options.asyncComputed || {} + + return function vueAsyncComputedInjectedDataFn(vm) { + const data = ((typeof optionData === 'function') + ? optionData.call(this, vm) + : optionData) || {}; + for (const key in asyncComputed) { + const item = this.$options.asyncComputed[key]; + if (isComputedLazy(item)) { + initLazy(data, key); + this.$options.computed[key] = makeLazyComputed(key); + } + else { + data[key] = null; + } + } + return data; + }; +} + function setAsyncState(vm, stateObject, state) { vm.$set(vm.$data._asyncComputed[stateObject], 'state', state) vm.$set(vm.$data._asyncComputed[stateObject], 'updating', state === 'updating') From 275b410ca503cfeabfae9d82395ed5ada6a4695e Mon Sep 17 00:00:00 2001 From: Elad Frizi Date: Fri, 22 Feb 2019 22:44:10 +0200 Subject: [PATCH 3/4] creation of is now on the mixin's computed instead inside beforeCreate --- src/index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index a510a01..805a595 100644 --- a/src/index.js +++ b/src/index.js @@ -25,18 +25,18 @@ const AsyncComputed = { _asyncComputed: {}, } }, + computed: { + $asyncComputed(){ + return this.$data._asyncComputed + } + }, beforeCreate() { - const optionData = this.$options.data const asyncComputed = this.$options.asyncComputed || {} - if (!this.$options.computed) this.$options.computed = {} - - this.$options.computed.$asyncComputed = () => this.$data._asyncComputed - if (!Object.keys(asyncComputed).length) return for (const key in asyncComputed) { - const getter = getterFn(key, this.$options.asyncComputed[key]) + const getter = getterFn(key, asyncComputed[key]) this.$options.computed[prefix + key] = getter } From b26bc0929e9945fa6b335c7c29afe93418569e6f Mon Sep 17 00:00:00 2001 From: Elad Frizi Date: Fri, 22 Feb 2019 23:28:13 +0200 Subject: [PATCH 4/4] extract the handling (upadting and registration to promising) of async computed changes into function --- src/index.js | 130 ++++++++++++++++++++++---------------------- src/shouldUpdate.js | 7 +-- 2 files changed, 68 insertions(+), 69 deletions(-) diff --git a/src/index.js b/src/index.js index 805a595..f6c3a09 100644 --- a/src/index.js +++ b/src/index.js @@ -12,7 +12,7 @@ import { getGetterWithShouldUpdate, shouldNotUpdate } from './shouldUpdate' const prefix = '_async_computed$' const AsyncComputed = { - install(Vue, pluginOptions) { + install (Vue, pluginOptions) { pluginOptions = pluginOptions || {} Vue.config @@ -20,17 +20,17 @@ const AsyncComputed = { .asyncComputed = Vue.config.optionMergeStrategies.computed Vue.mixin({ - data() { + data () { return { _asyncComputed: {}, } }, computed: { - $asyncComputed(){ + $asyncComputed () { return this.$data._asyncComputed } }, - beforeCreate() { + beforeCreate () { const asyncComputed = this.$options.asyncComputed || {} if (!Object.keys(asyncComputed).length) return @@ -42,10 +42,10 @@ const AsyncComputed = { this.$options.data = initDataWithAsyncComputed(this.$options) }, - created() { + created () { for (const key in this.$options.asyncComputed || {}) { const item = this.$options.asyncComputed[key], - value = generateDefault.call(this, item, pluginOptions) + value = generateDefault.call(this, item, pluginOptions) if (isComputedLazy(item)) { silentSetLazy(this, key, value) } else { @@ -54,91 +54,91 @@ const AsyncComputed = { } for (const key in this.$options.asyncComputed || {}) { - let promiseId = 0 - const watcher = newPromise => { - const thisPromise = ++promiseId - - if (shouldNotUpdate(newPromise)) { - return - } - - if (!newPromise || !newPromise.then) { - newPromise = Promise.resolve(newPromise) - } - setAsyncState(this, key, 'updating') - - newPromise.then(value => { - if (thisPromise !== promiseId) return - setAsyncState(this, key, 'success') - this[key] = value - }).catch(err => { - if (thisPromise !== promiseId) return - - setAsyncState(this, key, 'error') - Vue.set(this.$data._asyncComputed[key], 'exception', err) - if (pluginOptions.errorHandler === false) return - - const handler = (pluginOptions.errorHandler === undefined) - ? console.error.bind(console, 'Error evaluating async computed property:') - : pluginOptions.errorHandler - - if (pluginOptions.useRawError) { - handler(err) - } else { - handler(err.stack) - } - }) - } - Vue.set(this.$data._asyncComputed, key, { - exception: null, - update: () => { - watcher(getterOnly(this.$options.asyncComputed[key]).apply(this)) - } - }) - setAsyncState(this, key, 'updating') - this.$watch(prefix + key, watcher, { immediate: true }) + handleAsyncComputedPropetyChanges(this, key, pluginOptions, Vue) } } }) } } +function handleAsyncComputedPropetyChanges (vm, key, pluginOptions, Vue) { + let promiseId = 0 + const watcher = newPromise => { + const thisPromise = ++promiseId + + if (shouldNotUpdate(newPromise)) return + + if (!newPromise || !newPromise.then) { + newPromise = Promise.resolve(newPromise) + } + setAsyncState(vm, key, 'updating') + + newPromise.then(value => { + if (thisPromise !== promiseId) return + setAsyncState(vm, key, 'success') + vm[key] = value + }).catch(err => { + if (thisPromise !== promiseId) return -function initDataWithAsyncComputed(options) { + setAsyncState(vm, key, 'error') + Vue.set(vm.$data._asyncComputed[key], 'exception', err) + if (pluginOptions.errorHandler === false) return + + const handler = (pluginOptions.errorHandler === undefined) + ? console.error.bind(console, 'Error evaluating async computed property:') + : pluginOptions.errorHandler + + if (pluginOptions.useRawError) { + handler(err) + } else { + handler(err.stack) + } + }) + } + Vue.set(vm.$data._asyncComputed, key, { + exception: null, + update: () => { + watcher(getterOnly(vm.$options.asyncComputed[key]).apply(vm)) + } + }) + setAsyncState(vm, key, 'updating') + vm.$watch(prefix + key, watcher, { immediate: true }) +} + +function initDataWithAsyncComputed (options) { const optionData = options.data const asyncComputed = options.asyncComputed || {} - return function vueAsyncComputedInjectedDataFn(vm) { + return function vueAsyncComputedInjectedDataFn (vm) { const data = ((typeof optionData === 'function') ? optionData.call(this, vm) - : optionData) || {}; + : optionData) || {} for (const key in asyncComputed) { - const item = this.$options.asyncComputed[key]; + const item = this.$options.asyncComputed[key] if (isComputedLazy(item)) { - initLazy(data, key); - this.$options.computed[key] = makeLazyComputed(key); - } - else { - data[key] = null; + initLazy(data, key) + this.$options.computed[key] = makeLazyComputed(key) + } else { + data[key] = null } } - return data; - }; + return data + } } -function setAsyncState(vm, stateObject, state) { +function setAsyncState (vm, stateObject, state) { vm.$set(vm.$data._asyncComputed[stateObject], 'state', state) vm.$set(vm.$data._asyncComputed[stateObject], 'updating', state === 'updating') vm.$set(vm.$data._asyncComputed[stateObject], 'error', state === 'error') vm.$set(vm.$data._asyncComputed[stateObject], 'success', state === 'success') } -function getterOnly(fn) { +function getterOnly (fn) { if (typeof fn === 'function') return fn return fn.get } -function getterFn(key, fn) { +function getterFn (key, fn) { if (typeof fn === 'function') return fn let getter = fn.get @@ -153,7 +153,7 @@ function getterFn(key, fn) { if (isComputedLazy(fn)) { const nonLazy = getter - getter = function lazyGetter() { + getter = function lazyGetter () { if (isLazyActive(this, key)) { return nonLazy.call(this) } else { @@ -164,7 +164,7 @@ function getterFn(key, fn) { return getter } -function generateDefault(fn, pluginOptions) { +function generateDefault (fn, pluginOptions) { let defaultValue = null if ('default' in fn) { diff --git a/src/shouldUpdate.js b/src/shouldUpdate.js index aa32172..c6d0e25 100644 --- a/src/shouldUpdate.js +++ b/src/shouldUpdate.js @@ -1,10 +1,9 @@ const DidNotUpdate = typeof Symbol === 'function' ? Symbol('did-not-update') : {} - export const getGetterWithShouldUpdate = (asyncProprety, currentGetter) => { - return function getter() { - return (asyncProprety.shouldUpdate.call(this)) ? - currentGetter.call(this) + return function getter () { + return (asyncProprety.shouldUpdate.call(this)) + ? currentGetter.call(this) : DidNotUpdate } }