Skip to content

Commit

Permalink
3327 vue-lazy-hydrate on category page PoC
Browse files Browse the repository at this point in the history
  • Loading branch information
andrzejewsky committed Aug 13, 2019
1 parent bd2d02c commit 2b03048
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 20 deletions.
1 change: 1 addition & 0 deletions config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@
"products": {
"disablePersistentProductsCache": true,
"useMagentoUrlKeys": true,
"lazyLoadingCategoryProducts": true,
"setFirstVarianAsDefaultInURL": false,
"configurableChildrenStockPrefetchStatic": false,
"configurableChildrenStockPrefetchDynamic": false,
Expand Down
9 changes: 6 additions & 3 deletions core/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import Vuelidate from 'vuelidate'
import Meta from 'vue-meta'
import { sync } from 'vuex-router-sync'
import VueObserveVisibility from 'vue-observe-visibility'

import cloneDeep from 'lodash-es/cloneDeep'
import omit from 'lodash-es/omit'
// Apollo GraphQL client
import { getApolloProvider } from './scripts/resolvers/resolveGraphQL'

Expand Down Expand Up @@ -62,7 +63,9 @@ once('__VUE_EXTEND_RR__', () => {
Vue.use(VueRouter)
})

const createApp = async (ssrContext, config, storeCode = null): Promise<{app: Vue, router: VueRouter, store: Store<RootState>}> => {
const initialState = cloneDeep(store.state)

const createApp = async (ssrContext, config, storeCode = null): Promise<{app: Vue, router: VueRouter, store: Store<RootState>, initialState: RootState}> => {
router = createRouter()
// sync router with vuex 'router' store
sync(store, router)
Expand Down Expand Up @@ -132,7 +135,7 @@ const createApp = async (ssrContext, config, storeCode = null): Promise<{app: Vu
// @deprecated from 2.0
EventBus.$emit('application-after-init', app)

return { app, router, store }
return { app, router, store, initialState }
}

export { router, createApp }
6 changes: 4 additions & 2 deletions core/client-entry.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import Vue from 'vue'
import union from 'lodash-es/union'

import { createApp } from '@vue-storefront/core/app'
import rootStore from '@vue-storefront/core/store'
import { registerSyncTaskProcessor } from '@vue-storefront/core/lib/sync/task'
import i18n from '@vue-storefront/i18n'
import omit from 'lodash-es/omit'
import { prepareStoreView, storeCodeFromRoute, currentStoreView, localizedRoute } from '@vue-storefront/core/lib/multistore'
import { onNetworkStatusChange } from '@vue-storefront/core/modules/offline-order/helpers/onNetworkStatusChange'
import '@vue-storefront/core/service-worker/registration' // register the service worker
Expand All @@ -20,7 +20,9 @@ const invokeClientEntry = async () => {
const { app, router, store } = await createApp(null, dynamicRuntimeConfig, storeCode)

if (window.__INITIAL_STATE__) {
store.replaceState(Object.assign({}, store.state, window.__INITIAL_STATE__, { config: globalConfig }))
// skip fields that were set by createApp
const initialState = omit(window.__INITIAL_STATE__, ['storeView', 'config', 'version'])
store.replaceState(Object.assign({}, store.state, initialState, { config: globalConfig }))
}

await store.dispatch('url/registerDynamicRoutes')
Expand Down
2 changes: 1 addition & 1 deletion core/lib/multistore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export function storeCodeFromRoute (matchedRouteOrUrl: LocalizedRoute | RawLocat
return storeCode
}
}
}
}
return ''
}

Expand Down
33 changes: 33 additions & 0 deletions core/scripts/utils/ssr-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ const path = require('path')
const compile = require('lodash.template')
const rootPath = require('app-root-path').path
const resolve = file => path.resolve(rootPath, file)
const omit = require('lodash/omit')
const set = require('lodash/set')
const get = require('lodash/get')
const config = require('config')

function createRenderer (bundle, clientManifest, template) {
// https://github.com/vuejs/vue/blob/dev/packages/vue-server-renderer/README.md#why-use-bundlerenderer
Expand All @@ -16,7 +20,35 @@ function createRenderer (bundle, clientManifest, template) {
})
}

function getFieldsToFilter () {
const fields = config.ssr.initialStateFilter

if (config.products.lazyLoadingCategoryProducts) {
fields.push('category-next.products')
}

return fields
}

function filterState (context) {
if (!config.ssr.useInitialStateFilter) {
return context
}

for (const field of getFieldsToFilter()) {
const newValue = get(context.initialState, field, null)
set(context.state, field, newValue)
}

if (!config.server.dynamicConfigReload) {
context.state = omit(context.state, ['config'])
}

return omit(context, ['initialState'])
}

function applyAdvancedOutputProcessing (context, output, templatesCache, isProd = true, relatvePaths = false, destDir = '', outputFilename = '') {
context = filterState(context)
const contentPrepend = (typeof context.output.prepend === 'function') ? context.output.prepend(context) : '';
const contentAppend = (typeof context.output.append === 'function') ? context.output.append(context) : '';
output = contentPrepend + output + contentAppend;
Expand All @@ -34,6 +66,7 @@ function applyAdvancedOutputProcessing (context, output, templatesCache, isProd
output = output.replace(new RegExp('/assets', 'g'), `${relativePath}/dist`)
output = output.replace(new RegExp('href="/', 'g'), `href="${relativePath}/`)
}

return output;
}

Expand Down
14 changes: 4 additions & 10 deletions core/server-entry.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import union from 'lodash-es/union'

import { createApp } from '@vue-storefront/core/app'
import { HttpError } from '@vue-storefront/core/helpers/internal'
import { prepareStoreView, storeCodeFromRoute } from '@vue-storefront/core/lib/multistore'
Expand Down Expand Up @@ -31,14 +30,8 @@ function _ssrHydrateSubcomponents (components, store, router, resolve, reject, a
}
})).then(() => {
AsyncDataLoader.flush({ store, route: router.currentRoute, context: null } /* AsyncDataLoaderActionContext */).then((r) => {
if (buildTimeConfig.ssr.useInitialStateFilter) {
context.state = omit(store.state, config.ssr.initialStateFilter)
} else {
context.state = store.state
}
if (!buildTimeConfig.server.dynamicConfigReload) { // if dynamic config reload then we're sending config along with the request
context.state = omit(store.state, buildTimeConfig.ssr.useInitialStateFilter ? [...config.ssr.initialStateFilter, 'config'] : ['config'])
} else {
context.state = store.state
if (buildTimeConfig.server.dynamicConfigReload) {
const excludeFromConfig = buildTimeConfig.server.dynamicConfigExclude
const includeFromConfig = buildTimeConfig.server.dynamicConfigInclude
console.log(excludeFromConfig, includeFromConfig)
Expand All @@ -63,7 +56,8 @@ function getHostFromHeader (headers: string[]): string {
}

export default async context => {
const { app, router, store } = await createApp(context, context.vs && context.vs.config ? context.vs.config : buildTimeConfig)
const { app, router, store, initialState } = await createApp(context, context.vs && context.vs.config ? context.vs.config : buildTimeConfig)
context.initialState = initialState
return new Promise((resolve, reject) => {
context.output.cacheTags = new Set<string>()
const meta = (app as any).$meta()
Expand Down
7 changes: 6 additions & 1 deletion core/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ const state = {
ui: {},
newsletter: {},
wishlist: {},
attribute: '',
attribute: {
list_by_code: {},
list_by_id: {},
blacklist: [],
labels: {}
},
category: {
current_path: '',
current_product_query: {},
Expand Down
2 changes: 1 addition & 1 deletion core/types/RootState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default interface RootState {
shipping: any,
user: any,
wishlist: any,
attribute: string,
attribute: any,
ui: any,
newsletter: any,
category: {
Expand Down
18 changes: 16 additions & 2 deletions src/themes/default/pages/Category.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,18 @@
</h4>
<p>{{ $t('Please change Your search criteria and try again. If still not finding anything relevant, please visit the Home page and try out some of our bestsellers!') }}</p>
</div>
<product-listing :columns="defaultColumn" :products="getCategoryProducts" />
<lazy-hydrate :trigger-hydration="!loading" v-if="isLazyHydrateEnabled">
<product-listing :columns="defaultColumn" :products="getCategoryProducts" />
</lazy-hydrate>
<product-listing v-else :columns="defaultColumn" :products="getCategoryProducts" />
</div>
</div>
</div>
</div>
</template>

<script>
import LazyHydrate from 'vue-lazy-hydration'
import Sidebar from '../components/core/blocks/Category/Sidebar.vue'
import ProductListing from '../components/core/ProductListing.vue'
import Breadcrumbs from '../components/core/Breadcrumbs.vue'
Expand All @@ -86,6 +90,7 @@ import ButtonFull from 'theme/components/theme/ButtonFull.vue'
import { mapGetters } from 'vuex'
import uniq from 'lodash-es/uniq'
import onBottomScroll from '@vue-storefront/core/mixins/onBottomScroll'
import rootStore from '@vue-storefront/core/store';
const composeInitialPageState = async (store, route) => {
try {
Expand All @@ -106,6 +111,7 @@ const composeInitialPageState = async (store, route) => {
export default {
components: {
LazyHydrate,
ButtonFull,
ProductListing,
Breadcrumbs,
Expand All @@ -118,7 +124,8 @@ export default {
return {
mobileFilters: false,
defaultColumn: 3,
loadingProducts: false
loadingProducts: false,
loading: true
}
},
computed: {
Expand All @@ -129,6 +136,9 @@ export default {
getCategoryProductsTotal: 'category-next/getCategoryProductsTotal',
getAvailableFilters: 'category-next/getAvailableFilters'
}),
isLazyHydrateEnabled () {
return config.products.lazyLoadingCategoryProducts
},
isCategoryEmpty () {
return this.getCategoryProductsTotal === 0
},
Expand All @@ -143,12 +153,16 @@ export default {
if (isServer) next() // SSR no need to invoke SW caching here
else if (from.name) { // SSR but client side invocation, we need to cache products
next(async vm => {
vm.loading = true
await vm.$store.dispatch('category-next/cacheProducts', { route: to })
vm.loading = false
})
} else { // Pure CSR, with no initial category state
next(async vm => {
vm.loading = true
await composeInitialPageState(vm.$store, to)
await vm.$store.dispatch('category-next/cacheProducts', { route: to })
vm.loading = false
})
}
},
Expand Down

0 comments on commit 2b03048

Please sign in to comment.