From 62829e4a969532f8a4568302ff6f89286cde5b77 Mon Sep 17 00:00:00 2001 From: Guillaume Chau Date: Sun, 26 Aug 2018 18:16:17 +0200 Subject: [PATCH] refactor: from provide to apolloProvider option, closes #319 --- docs/guide/installation.md | 4 +- src/apollo-provider.js | 1 + src/index.js | 154 ++++--------------------------------- src/mixin.js | 146 +++++++++++++++++++++++++++++++++++ tests/demo/src/App.vue | 6 ++ tests/demo/src/main.js | 6 +- 6 files changed, 173 insertions(+), 144 deletions(-) create mode 100644 src/mixin.js diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 547f9c5..e224f16 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -45,7 +45,7 @@ Vue.use(VueApollo) ## Apollo provider -The provider holds the apollo client instances that can then be used by all the child components. Inject it into your components with `provide`: +The provider holds the Apollo client instances that can then be used by all the child components. Inject it into your components with `provide`: ```js const apolloProvider = new VueApollo({ @@ -54,7 +54,7 @@ const apolloProvider = new VueApollo({ new Vue({ el: '#app', - provide: apolloProvider.provide(), + apolloProvider, render: h => h(App), }) ``` \ No newline at end of file diff --git a/src/apollo-provider.js b/src/apollo-provider.js index 6267d1c..43c8078 100644 --- a/src/apollo-provider.js +++ b/src/apollo-provider.js @@ -16,6 +16,7 @@ export class ApolloProvider { } provide (key = '$apolloProvider') { + console.warn(`.provide() is deprecated. Use the 'apolloProvider' option instead with the provider object directly.`) return { [key]: this, } diff --git a/src/index.js b/src/index.js index 1fa6f36..bf03771 100644 --- a/src/index.js +++ b/src/index.js @@ -1,108 +1,23 @@ import { DollarApollo } from './dollar-apollo' -import { ApolloProvider as apolloProvider } from './apollo-provider' +import { ApolloProvider as plugin } from './apollo-provider' + import CApolloQuery from './components/ApolloQuery' import CApolloSubscribeToMore from './components/ApolloSubscribeToMore' import CApolloMutation from './components/ApolloMutation' + +import { installMixin } from './mixin' import { Globals, omit } from './utils' const keywords = [ '$subscribe', ] -function hasProperty (holder, key) { - return typeof holder !== 'undefined' && Object.prototype.hasOwnProperty.call(holder, key) -} - -function proxyData () { - let apollo = this.$options.apollo - - if (apollo) { - // watchQuery - for (let key in apollo) { - if (key.charAt(0) !== '$') { - let options = apollo[key] - // Property proxy - if (!options.manual && !hasProperty(this.$options.props, key) && !hasProperty(this.$options.computed, key) && !hasProperty(this.$options.methods, key)) { - Object.defineProperty(this, key, { - get: () => this.$data.$apolloData.data[key], - enumerable: true, - configurable: true, - }) - } - } - } - } -} - -function launch () { - const apolloProvider = this.$apolloProvider - - if (this._apolloLaunched || !apolloProvider) return - this._apolloLaunched = true - - // Prepare properties - let apollo = this.$options.apollo - - if (apollo) { - if (!apollo.$init) { - apollo.$init = true - - // Default options applied to `apollo` options - if (apolloProvider.defaultOptions) { - apollo = this.$options.apollo = Object.assign({}, apolloProvider.defaultOptions, apollo) - } - } - - defineReactiveSetter(this.$apollo, 'skipAll', apollo.$skipAll, apollo.$deep) - defineReactiveSetter(this.$apollo, 'skipAllQueries', apollo.$skipAllQueries, apollo.$deep) - defineReactiveSetter(this.$apollo, 'skipAllSubscriptions', apollo.$skipAllSubscriptions, apollo.$deep) - defineReactiveSetter(this.$apollo, 'client', apollo.$client, apollo.$deep) - defineReactiveSetter(this.$apollo, 'loadingKey', apollo.$loadingKey, apollo.$deep) - defineReactiveSetter(this.$apollo, 'error', apollo.$error, apollo.$deep) - defineReactiveSetter(this.$apollo, 'watchLoading', apollo.$watchLoading, apollo.$deep) - - // Apollo Data - Object.defineProperty(this, '$apolloData', { - get: () => this.$data.$apolloData, - enumerable: true, - configurable: true, - }) - - // watchQuery - for (let key in apollo) { - if (key.charAt(0) !== '$') { - let options = apollo[key] - this.$apollo.addSmartQuery(key, options) - } - } - - if (apollo.subscribe) { - Globals.Vue.util.warn('vue-apollo -> `subscribe` option is deprecated. Use the `$subscribe` option instead.') - } - - if (apollo.$subscribe) { - for (let key in apollo.$subscribe) { - this.$apollo.addSmartSubscription(key, apollo.$subscribe[key]) - } - } - } -} - -function defineReactiveSetter ($apollo, key, value, deep) { - if (typeof value !== 'undefined') { - if (typeof value === 'function') { - $apollo.defineReactiveSetter(key, value, deep) - } else { - $apollo[key] = value - } - } -} - export function install (Vue, options) { if (install.installed) return install.installed = true Globals.Vue = Vue + const vueVersion = Vue.version.substr(0, Vue.version.indexOf('.')) // Options merging const merge = Vue.config.optionMergeStrategies.methods @@ -125,55 +40,14 @@ export function install (Vue, options) { // Lazy creation Object.defineProperty(Vue.prototype, '$apollo', { get () { - if (!this._apollo) { - this._apollo = new DollarApollo(this) + if (!this.$_apollo) { + this.$_apollo = new DollarApollo(this) } - return this._apollo + return this.$_apollo }, }) - const vueVersion = Vue.version.substr(0, Vue.version.indexOf('.')) - - Vue.mixin({ - ...vueVersion === '1' ? { - init () { - let apolloProvider - if (this.$options.apolloProvider) { - apolloProvider = this._apolloProvider = this.$options.apolloProvider - } else { - apolloProvider = this.$root._apolloProvider - } - this.$apolloProvider = apolloProvider - }, - } : {}, - - ...vueVersion === '2' ? { - inject: { - $apolloProvider: { default: null }, - }, - - data () { - return { - '$apolloData': { - queries: {}, - loading: 0, - data: {}, - }, - } - }, - } : {}, - - beforeCreate: proxyData, - created: launch, - - destroyed: function () { - if (this._apollo) { - this._apollo.destroy() - this._apollo = null - } - }, - - }) + installMixin(Vue, vueVersion) if (vueVersion === '2') { Vue.component('apollo-query', CApolloQuery) @@ -185,13 +59,13 @@ export function install (Vue, options) { } } -apolloProvider.install = install +plugin.install = install // eslint-disable-next-line no-undef -apolloProvider.version = VERSION +plugin.version = VERSION // Apollo provider -export const ApolloProvider = apolloProvider +export const ApolloProvider = plugin export { willPrefetch } from './apollo-provider' // Components @@ -207,7 +81,7 @@ if (typeof window !== 'undefined') { GlobalVue = global.Vue } if (GlobalVue) { - GlobalVue.use(apolloProvider) + GlobalVue.use(plugin) } -export default apolloProvider +export default plugin diff --git a/src/mixin.js b/src/mixin.js new file mode 100644 index 0000000..d31240b --- /dev/null +++ b/src/mixin.js @@ -0,0 +1,146 @@ +import { Globals } from './utils' + +function hasProperty (holder, key) { + return typeof holder !== 'undefined' && Object.prototype.hasOwnProperty.call(holder, key) +} + +function initDollarApollo () { + const options = this.$options + // ApolloProvider injection + const optionValue = options.apolloProvider + if (optionValue) { + this.$apolloProvider = typeof optionValue === 'function' + ? optionValue() + : optionValue + } else if (options.parent && options.parent.$apolloProvider) { + this.$apolloProvider = options.parent.$apolloProvider + } else if (options.provide) { + // TODO remove + // Temporary retro-compatibility + const provided = typeof options.provide === 'function' + ? options.provide.call(this) + : options.provide + if (provided.$apolloProvider) { + this.$apolloProvider = provided.$apolloProvider + } + } +} + +function proxyData () { + let apollo = this.$options.apollo + + if (apollo) { + // watchQuery + for (let key in apollo) { + if (key.charAt(0) !== '$') { + let options = apollo[key] + // Property proxy + if (!options.manual && !hasProperty(this.$options.props, key) && !hasProperty(this.$options.computed, key) && !hasProperty(this.$options.methods, key)) { + Object.defineProperty(this, key, { + get: () => this.$data.$apolloData.data[key], + enumerable: true, + configurable: true, + }) + } + } + } + } +} + +function launch () { + const apolloProvider = this.$apolloProvider + + if (this._apolloLaunched || !apolloProvider) return + this._apolloLaunched = true + + // Prepare properties + let apollo = this.$options.apollo + + if (apollo) { + if (!apollo.$init) { + apollo.$init = true + + // Default options applied to `apollo` options + if (apolloProvider.defaultOptions) { + apollo = this.$options.apollo = Object.assign({}, apolloProvider.defaultOptions, apollo) + } + } + + defineReactiveSetter(this.$apollo, 'skipAll', apollo.$skipAll, apollo.$deep) + defineReactiveSetter(this.$apollo, 'skipAllQueries', apollo.$skipAllQueries, apollo.$deep) + defineReactiveSetter(this.$apollo, 'skipAllSubscriptions', apollo.$skipAllSubscriptions, apollo.$deep) + defineReactiveSetter(this.$apollo, 'client', apollo.$client, apollo.$deep) + defineReactiveSetter(this.$apollo, 'loadingKey', apollo.$loadingKey, apollo.$deep) + defineReactiveSetter(this.$apollo, 'error', apollo.$error, apollo.$deep) + defineReactiveSetter(this.$apollo, 'watchLoading', apollo.$watchLoading, apollo.$deep) + + // Apollo Data + Object.defineProperty(this, '$apolloData', { + get: () => this.$data.$apolloData, + enumerable: true, + configurable: true, + }) + + // watchQuery + for (let key in apollo) { + if (key.charAt(0) !== '$') { + let options = apollo[key] + this.$apollo.addSmartQuery(key, options) + } + } + + if (apollo.subscribe) { + Globals.Vue.util.warn('vue-apollo -> `subscribe` option is deprecated. Use the `$subscribe` option instead.') + } + + if (apollo.$subscribe) { + for (let key in apollo.$subscribe) { + this.$apollo.addSmartSubscription(key, apollo.$subscribe[key]) + } + } + } +} + +function defineReactiveSetter ($apollo, key, value, deep) { + if (typeof value !== 'undefined') { + if (typeof value === 'function') { + $apollo.defineReactiveSetter(key, value, deep) + } else { + $apollo[key] = value + } + } +} + +export function installMixin (Vue, vueVersion) { + Vue.mixin({ + ...vueVersion === '1' ? { + init: initDollarApollo, + } : {}, + + ...vueVersion === '2' ? { + data () { + return { + '$apolloData': { + queries: {}, + loading: 0, + data: {}, + }, + } + }, + + beforeCreate () { + initDollarApollo.call(this) + proxyData.call(this) + }, + } : {}, + + created: launch, + + destroyed: function () { + if (this.$_apollo) { + this.$_apollo.destroy() + this.$_apollo = null + } + }, + }) +} diff --git a/tests/demo/src/App.vue b/tests/demo/src/App.vue index 080f9de..ff2ed6c 100644 --- a/tests/demo/src/App.vue +++ b/tests/demo/src/App.vue @@ -10,10 +10,16 @@ diff --git a/tests/demo/src/main.js b/tests/demo/src/main.js index c81aaec..b5b9ed5 100644 --- a/tests/demo/src/main.js +++ b/tests/demo/src/main.js @@ -6,9 +6,11 @@ import { createProvider } from './vue-apollo' Vue.config.productionTip = false +const apolloProvider = createProvider({}, { router }) + new Vue({ router, store, - provide: createProvider({}, { router }).provide(), - render: h => h(App), + apolloProvider, + ...App, }).$mount('#app')