diff --git a/docs/api/ssr.md b/docs/api/ssr.md index 241a30d..d7e04ec 100644 --- a/docs/api/ssr.md +++ b/docs/api/ssr.md @@ -6,6 +6,35 @@ See [SSR guide](../guide/ssr.md). ## Methods +### install + +Install the SSR plugin only on the server with: + +```js +Vue.use(ApolloSSR) +``` + +You can pass additional options like this: + +```js +Vue.use(ApolloSSR, { + fetchPolicy: 'network-only', + suppressRenderErrors: false, +}) +``` + +#### fetchPolicy + +When an Apollo query is prefetched, it's recommended to override `fetchPolicy` to force the queries to happen. + +Default value: `'network-only'`. + +#### suppressRenderErrors + +Silent the fake render errors. + +Default value: `false`. + ### prefetchAll Prefetches all queued component definitions and returns a promise resolved when all corresponding apollo data is ready. @@ -53,3 +82,40 @@ const js = ApolloSSR.exportStates(apolloProvider, options) exportNamespace: '', } ``` + +### globalPrefetch + +Allow you to register a component to be prefetched explicitely. + +Simple example: + +```js +import MyComponent from '@/components/MyComponent.vue' + +ApolloSSR.globalPrefetch(() => MyComponent) +``` + +You can disable prefetching depending on context: + +```js +ApolloSSR.globalPrefetch(context => { + if (context.route.name === 'foo'){ + return MyComponent + } +}) +``` + +### mockInstance + +During `prefetchAll`, the app components tree is re-created with fake instances so the process is faster. You can apply plugins to modify the fake instances to prevent their render functions to crash if you have helpers like `this.$http` that is accessed in the template or render function (typically `Undefined error`). It's recommended to mock those helpers to improve performance. + +```js +const noop = () => {} + +ApolloSSR.mockInstance({ + apply: vm => { + // Mock $http + vm.$http = noop + }, +}) +``` diff --git a/docs/guide/ssr.md b/docs/guide/ssr.md index 83bc8e1..80a1145 100644 --- a/docs/guide/ssr.md +++ b/docs/guide/ssr.md @@ -140,7 +140,11 @@ import Vue from 'vue' import ApolloSSR from 'vue-apollo/ssr' import App from './App.vue' -Vue.use(ApolloSSR) +Vue.use(ApolloSSR, { + // SSR config + fetchPolicy: 'network-only', + suppressRenderErrors: false, +}) export default () => new Promise((resolve, reject) => { const { app, router, store, apolloProvider } = CreateApp({ @@ -360,4 +364,6 @@ export default () => new Promise((resolve, reject) => { // Prefetch, render HTML (see above) }) }) -``` \ No newline at end of file +``` + +See the [SSR API](../api/ssr.md) for more details and other features. diff --git a/ssr/index.js b/ssr/index.js index 75bcada..5ced0df 100644 --- a/ssr/index.js +++ b/ssr/index.js @@ -3,12 +3,29 @@ const { VUE_APOLLO_QUERY_KEYWORDS } = require('../lib/consts') const { createFakeInstance, resolveComponent } = require('./utils') const { Globals, getMergedDefinition, omit } = require('../lib/utils') -exports.install = function (Vue) { - Globals.Vue = Vue +const config = exports.config = { + globalPrefetchs: [], + fakeInstanceMocks: [], + fetchPolicy: 'network-only', + suppressRenderErrors: false, } -exports.prefetchAll = function (apolloProvider, components, context) { - return exports.getQueriesFromTree(components, context) +exports.install = function (Vue, options = {}) { + Globals.Vue = Vue + Object.assign(config, options) +} + +exports.globalPrefetch = function (handler) { + config.globalPrefetchs.push(handler) +} + +exports.mockInstance = function (plugin) { + config.fakeInstanceMocks.push(plugin) +} + +exports.prefetchAll = function (apolloProvider, components = [], context = {}) { + const globalPrefetchs = config.globalPrefetchs.map(handler => handler(context)).filter(Boolean) + return exports.getQueriesFromTree(components.concat(globalPrefetchs), context) .then(queries => Promise.all(queries.map( query => prefetchQuery(apolloProvider, query, context) ))) @@ -27,6 +44,13 @@ function walkTree (component, data, parent, children, context, queries, componen const queue = [] data = data || {} const vm = createFakeInstance(component, data, parent, children, context) + + // Mocks + for (const mock of config.fakeInstanceMocks) { + mock.apply(mock) + } + + // Render h function vm.$createElement = (el, data, children) => { if (typeof data === 'string' || Array.isArray(data)) { children = data @@ -58,8 +82,10 @@ function walkTree (component, data, parent, children, context, queries, componen try { component.render.call(vm, vm.$createElement) } catch (e) { - console.log(chalk.red(`Error while rendering ${component.name || component.__file}`)) - console.log(e.stack) + if (!config.suppressRenderErrors) { + console.log(chalk.red(`Error while rendering ${component.name || component.__file}`)) + console.log(e.stack) + } } Promise.all(queue).then(queue => queue.filter(child => !!child).map( @@ -163,15 +189,22 @@ function prefetchQuery (apolloProvider, query, context) { queryOptions.query = queryOptions.query.call(vm) } - const options = omit(queryOptions, [ - ...VUE_APOLLO_QUERY_KEYWORDS, - 'fetchPolicy', - ]) + // Default query options from apollo provider + if (apolloProvider.defaultOptions && apolloProvider.defaultOptions.$query) { + queryOptions = Object.assign({}, apolloProvider.defaultOptions.$query, queryOptions) + } + + // Remove vue-apollo specific options + const options = omit(queryOptions, VUE_APOLLO_QUERY_KEYWORDS) options.variables = variables - options.fetchPolicy = 'network-only' + // Override fetchPolicy + if (config.fetchPolicy != null) { + options.fetchPolicy = config.fetchPolicy + } return client.query(options) } catch (e) { + console.log(chalk.red(`[ERROR] While prefetching query`), query, chalk.grey(`Error stack trace:`)) console.log(e.stack) } }