Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/2985 - a.k.a "localStorage saver" #3180

Merged
merged 5 commits into from
Jul 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Shipping address is saved as default when not logged in user chooses to create account during checkout - @iwonapiotrowska (#2636)
- Can set transition style for Modal content - @grimasod (#3146)
- Added stock to cart items - @cheeerd (#3166)
- Decreased the `localStorage` quota usage + error handling by introducing new config variables: `config.products.disablePersistentProductsCache` to not store products by SKU (by default it's on). Products are cached in ServiceWorker cache anyway so the `product/list` will populate the in-memory cache (`cache.setItem(..., memoryOnly = true)`); `config.seo.disableUrlRoutesPersistentCache` - to not store the url mappings; they're stored in in-memory cache anyway so no additional requests will be made to the backend for url mapping; however it might cause some issues with url routing in the offline mode (when the offline mode PWA installed on homescreen got reloaded, the in-memory cache will be cleared so there won't potentially be the url mappings; however the same like with `product/list` the ServiceWorker cache SHOULD populate url mappings anyway); `config.syncTasks.disablePersistentTaskQueue` to not store the network requests queue in service worker. Currently only the stock-check and user-data changes were using this queue. The only downside it introuces can be related to the offline mode and these tasks will not be re-executed after connectivity established, but just in a case when the page got reloaded while offline (yeah it might happen using ServiceWorker; `syncTasks` can't be re-populated in cache from SW)
- Translation file improvements - @vishal-7037 (#3198)

## [1.10.0-rc.2] - UNRELEASED
Expand Down
10 changes: 7 additions & 3 deletions config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
}
},
"seo": {
"useUrlDispatcher": true
"useUrlDispatcher": true,
"disableUrlRoutesPersistentCache": true
},
"console": {
"showErrorOnProduction" : false,
Expand All @@ -46,7 +47,7 @@
"csrTimeout": 5000,
"ssrTimeout": 1000,
"queryMethod": "GET",
"disableLocalStorageQueriesCache": true,
"disablePersistentQueriesCache": true,
"searchScoring": {
"attributes": {
"attribute_code": {
Expand Down Expand Up @@ -256,6 +257,7 @@
"applycoupon_endpoint": "/api/cart/apply-coupon?token={{token}}&cartId={{cartId}}&coupon={{coupon}}"
},
"products": {
"disablePersistentProductsCache": true,
"useMagentoUrlKeys": true,
"setFirstVarianAsDefaultInURL": false,
"configurableChildrenStockPrefetchStatic": false,
Expand Down Expand Up @@ -335,7 +337,6 @@
"wishlist": "LOCALSTORAGE",
"categories": "LOCALSTORAGE",
"attributes": "LOCALSTORAGE",
"products": "INDEXEDDB",
"elasticCache": "LOCALSTORAGE",
"claims": "LOCALSTORAGE",
"syncTasks": "LOCALSTORAGE",
Expand Down Expand Up @@ -391,6 +392,9 @@
}
]
},
"syncTasks": {
"disablePersistentTaskQueue": true
},
"i18n": {
"defaultCountry": "US",
"defaultLanguage": "EN",
Expand Down
2 changes: 1 addition & 1 deletion core/lib/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export const quickSearchByQuery = async ({ query = {}, start = 0, size = 50, ent
const res = searchAdapter.entities[Request.type].resultPorcessor(resp, start, size)

if (res) { // otherwise it can be just a offline mode
cache.setItem(cacheKey, res, null, config.elasticsearch.disableLocalStorageQueriesCache).catch((err) => { console.error('Cannot store cache for ' + cacheKey + ', ' + err) })
cache.setItem(cacheKey, res, null, config.elasticsearch.disablePersistentQueriesCache).catch((err) => { console.error('Cannot store cache for ' + cacheKey + ', ' + err) })
if (!servedFromCache) { // if navigator onLine == false means ES is unreachable and probably this will return false; sometimes returned false faster than indexedDb cache returns result ...
Logger.debug('Result from ES for ' + cacheKey + ' (' + entityType + '), ms=' + (new Date().getTime() - benchmarkTime.getTime()))()
res.cache = false
Expand Down
2 changes: 1 addition & 1 deletion core/lib/sync/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ async function queue (task) {
if (err) Logger.error(err, 'sync')()
Vue.prototype.$bus.$emit('sync/PROCESS_QUEUE', { config: config }) // process checkout queue
resolve(task)
}).catch((reason) => {
}, config.syncTasks.disablePersistentTaskQueue).catch((reason) => {
Logger.error(reason, 'sync')() // it doesn't work on SSR
reject(reason)
})
Expand Down
6 changes: 0 additions & 6 deletions core/modules/catalog/hooks/beforeRegistration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,4 @@ export function beforeRegistration ({ Vue, config, store, isServer }) {
storeName: 'elasticCache',
driver: localForage[config.localForage.defaultDrivers['elasticCache']]
}), true, config.server.elasticCacheQuota)

Vue.prototype.$db.productsCollection = new UniversalStorage(localForage.createInstance({
name: dbNamePrefix + 'shop',
storeName: 'products',
driver: localForage[config.localForage.defaultDrivers['products']]
}))
}
8 changes: 7 additions & 1 deletion core/modules/catalog/store/product/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,9 +340,15 @@ const actions: ActionTree<ProductState, RootState> = {
}
const cacheKey = entityKeyName(cacheByKey, prod[(cacheByKey === 'sku' && prod['parentSku']) ? 'parentSku' : cacheByKey]) // to avoid caching products by configurable_children.sku
if (isCacheable) { // store cache only for full loads
cache.setItem(cacheKey, prod)
cache.setItem(cacheKey, prod, null, config.products.disablePersistentProductsCache)
.catch((err) => {
Logger.error('Cannot store cache for ' + cacheKey, err)()
if (
err.name === 'QuotaExceededError' ||
err.name === 'NS_ERROR_DOM_QUOTA_REACHED'
) { // quota exceeded error
cache.clear() // clear products cache if quota exceeded
}
})
}
if ((prod.type_id === 'grouped' || prod.type_id === 'bundle') && prefetchGroupProducts && !isServer) {
Expand Down
2 changes: 1 addition & 1 deletion core/modules/compare/store/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function plugin (mutation, state) {

if (type.includes(types.COMPARE_ADD_ITEM) || type.includes(types.COMPARE_DEL_ITEM)) { // check if this mutation is comapre related
cacheStorage.setItem('current-compare', state.compare.items).catch((reason) => {
Logger.error(reason, 'compare') // it doesn't work on SSR
Logger.error(reason, 'compare')
})
}
}
12 changes: 11 additions & 1 deletion core/modules/url/store/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as types from './mutation-types'
// you can use this storage if you want to enable offline capabilities
import { cacheStorage } from '../'
import queryString from 'query-string'
import config from 'config'
import SearchQuery from '@vue-storefront/core/lib/search/searchQuery'
import { processDynamicRoute, normalizeUrlPath, parametrizeRouteData } from '../helpers'
import { storeCodeFromRoute, removeStoreCodeFromRoute } from '@vue-storefront/core/lib/multistore'
Expand All @@ -13,7 +14,16 @@ export const actions: ActionTree<UrlState, any> = {
// if you want to use cache in your module you can load cached data like this
async registerMapping ({ commit }, { url, routeData }: { url: string, routeData: any}) {
commit(types.REGISTER_MAPPING, { url, routeData })
await cacheStorage.setItem(url, routeData)
try {
await cacheStorage.setItem(url, routeData, null, config.seo.disableUrlRoutesPersistentCache)
} catch (err) {
if (
err.name === 'QuotaExceededError' ||
err.name === 'NS_ERROR_DOM_QUOTA_REACHED'
) { // quota exceeded error
cacheStorage.clear() // clear the url cache if quota has been exceeded
}
}
return routeData
},
/**
Expand Down
10 changes: 9 additions & 1 deletion core/store/lib/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ class LocalForageCacheDriver {
private _persistenceErrorNotified: boolean;
private _useLocalCacheByDefault: boolean;
private cacheErrorsCount: any;
private localCache: any;
private _storageQuota: number;

public constructor (collection, useLocalCacheByDefault = true, storageQuota = 0) {
Expand Down Expand Up @@ -128,6 +127,14 @@ class LocalForageCacheDriver {
}
}

public getLastError () {
return this._lastError
}

public getDbName () {
return this._dbName
}

// Retrieve an item from the store. Unlike the original async_storage
// library in Gaia, we don't modify return values at all. If a key's value
// is `undefined`, we pass that value to the callback function.
Expand Down Expand Up @@ -313,6 +320,7 @@ class LocalForageCacheDriver {
}).catch(err => {
isResolved = true
this._lastError = err
throw err
}))
setTimeout(() => {
if (!isResolved) { // this is cache time out check
Expand Down
14 changes: 12 additions & 2 deletions docs/guide/upgrade-notes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

We're trying to keep the upgrade process as easy as possible. Unfortunately, sometimes manual code changes are required. Before pulling out the latest version, please take a look at the upgrade notes below:

## 1.10 -> 1.11

We've decreased the `localStorage` quota usage + error handling by introducing new config variables:

- `config.products.disablePersistentProductsCache` to not store products by SKU (by default it's on). Products are cached in ServiceWorker cache anyway so the `product/list` will populate the in-memory cache (`cache.setItem(..., memoryOnly = true)`);
- `config.seo.disableUrlRoutesPersistentCache` - to not store the url mappings; they're stored in in-memory cache anyway so no additional requests will be made to the backend for url mapping; however it might cause some issues with url routing in the offline mode (when the offline mode PWA installed on homescreen got reloaded, the in-memory cache will be cleared so there won't potentially be the url mappings; however the same like with `product/list` the ServiceWorker cache SHOULD populate url mappings anyway);
- `config.syncTasks.disablePersistentTaskQueue` to not store the network requests queue in service worker. Currently only the stock-check and user-data changes were using this queue. The only downside it introuces can be related to the offline mode and these tasks will not be re-executed after connectivity established, but just in a case when the page got reloaded while offline (yeah it might happen using ServiceWorker; `syncTasks` can't be re-populated in cache from SW)

If by some reasons you wan't to have the `localStorage` back on for `Products by SKU`, `Url Routes` and `SyncTasks` - please juset set these variables back to `false` in your `config/local.json`.

## 1.9 -> 1.10
- Event `application-after-init` is now emitted by event bus instead of root Vue instance (app), so you need to listen to `Vue.prototype.$bus` (`Vue.prototype.$bus.$on()`) now
- The lowest supported node version is currently 8.10.0,
Expand Down Expand Up @@ -54,14 +64,14 @@ Full changelog is available [here](https://github.com/DivanteLtd/vue-storefront/
Starting from Vue Storefront 1.7, we changed the caching strategy and offline-ready features:
- By default, the Elasticsearch Queries are executed using `GET` method and therefore are cached by Service Worker (`config.elasticsearch.queryMethod` — set it to POST for the previous behavior and if you're using graphql).
- By default, products and queries cache is set in `LocalStorage` with a quota set to 4MB (`config.server.elasticCacheQuota`). If the storage quota is set, the cache purging is executed every 30 seconds using the LRU algorithm. Local Storage is limited to 5MB in most browsers.
- We added `config.server. disableLocalStorageQueriesCache`, which is set to `true` by default. When this option is on, we're not storing the Elasticsearch results in the local cache because results are by default cached in the Service Worker cache anyway.
- We added `config.server. disablePersistentQueriesCache`, which is set to `true` by default. When this option is on, we're not storing the Elasticsearch results in the local cache because results are by default cached in the Service Worker cache anyway.
- `module.extend` has been changed to `extendModule`. You can find usage examples in `src/modules/index.ts`.
- [routes](https://github.com/patzick/vue-storefront/commit/a97eb11868de2915e86d57c4279caf944d4de422#diff-a334a7caeb7f61836f8c1178d92de3e0), [layouts](https://github.com/patzick/vue-storefront/commit/a97eb11868de2915e86d57c4279caf944d4de422#diff-48863b9fe31d7713222ec5709ef5a4fa), and component, which are not visible at page rendering are now loaded when they are needed.
- Every store manipulation should be done by dispatching actions. Invoke store dispatch on `category/mergeSearchOptions` when manipulating `store.state.category.current_product_query` somewhere.
- [here](https://github.com/patzick/vue-storefront/commit/a97eb11868de2915e86d57c4279caf944d4de422) are all changes in default themes

Backward compatibility: To reverse to the 1.0–1.6 behavior:
- Set `config.server.disableLocalStorageQueriesCache` = `false`,
- Set `config.server.disablePersistentQueriesCache` = `false`,
- Set `config.elasticsearch.queryMethod` = `POST`
- Set `config.localForage.defaultDrivers.elasticCache` = `INDEXEDDB`

Expand Down