Lazy Loading Issue | Spartacus 2.0.0 - spartacus-storefront

I am trying to implement lazy loading feature using Spartacus 2.0 but facing below issues:
Templates are not getting visible after it is loaded at very first
time.
Components are getting visible when we again come back to same page.
Also some components are getting visible only after I resize the
screen once after page is loaded.
We are using below strategy mentioned at below location for component lazy loading
https://sap.github.io/spartacus-docs/lazy-loading-guide/
Here is code snippet for one of the module file:
Module1,
Module2,
ConfigModule.withConfig({
cmsComponents: {
ComponentABC: {
component: () =>
import('./xyz.component').then(
(m) => m.xyz
)
}
}
}),
storefrontModule
],
Please suggest for possible solutions.

I think it should looks like:
ConfigModule.withConfig({
cmsComponents: {
ABCComponent: {
component: ABCComponent,
},
XYZComponent: {
component: () => import('./xyz.component').then(m => m.XYZComponent)
}
}
}),

For using lazy loading with Spartacus version 2.0, the 2.x lazy loading guide may be more appropriate: https://sap.github.io/spartacus-docs/2.x/lazy-loading-guide/

Related

Lazy loading & loading states with vue-router, vite & vuejs 2.x

I'm migrating an old project from vue-cli to vite. I followed the migration guide and everything worked great, but there's something it's not working, or at least, not as intended, when I was using vue-cli I tried to implement the loading states as shown in their documentation but then I saw the following pull request explaining how to achieve the wanted behavior (with the downside of losing navigation guards on those routes).
Now, after migrating I noticed that neither the loading/error components are rendered at all, even setting a timeout really small, however, I see in the networking tab that my components are being loaded, but never rendered.
Do you have any suggestions of why might this occur?.
// This is the lazyLoadView that was working before migrating to vite.
function lazyLoadView(AsyncView) {
const AsyncHandler = () => ({
component: AsyncView,
// A component to use while the component is loading.
loading: import("./components/loaders/loader.vue").default,
// A fallback component in case the timeout is exceeded
// when loading the component.
error: import("./components/loaders/error.vue").default,
// Delay before showing the loading component.
// Default: 200 (milliseconds).
delay: 1,
// Time before giving up trying to load the component.
// Default: Infinity (milliseconds).
timeout: 2,
});
return Promise.resolve({
functional: true,
render(h, { data, children }) {
// Transparently pass any props or children
// to the view component.
return h(AsyncHandler, data, children);
},
});
}
And the routes I have:
const routes = [
{
path: "/foo/",
component: () => lazyLoadView(import("./components/bar.vue")),
}
]
Let me know if you find why might this be happening.
So I figured out:
Looks like the loading & error components were also lazy loaded, they were skipped. So continued trying to obtain the main one until shown (that's why didn't render the loading besides of only showing a message).
So in order to fix it I had to import them at the top and include them in the lazyLoadView like this:
//These two imports at the top
import loaderComp from "./components/loaders/loader.vue";
import errorComp from "./components/loaders/error.vue";
function lazyLoadView(AsyncView) {
const AsyncHandler = () => ({
component: AsyncView,
// A component to use while the component is loading.
// must NOT be lazy-loaded
loading: loaderComp,
// A fallback component in case the timeout is exceeded
// when loading the component.
// must NOT be lazy-loaded
error: errorComp,
// Delay before showing the loading component.
// Default: 200 (milliseconds).
delay: 1,
// Time before giving up trying to load the component.
// Default: Infinity (milliseconds).
timeout: 2,
});
return Promise.resolve({
functional: true,
render(h, { data, children }) {
// Transparently pass any props or children
// to the view component.
return h(AsyncHandler, data, children);
},
});
}

Call context in nuxt not working in v-bind

I have question related to using context or prototype in Nuxt
I create a constant for 'modal' name like this:
export default Object.freeze({
MODAL_SHOWPRO: "MODAL_SHOWPRO",
})
I also created constant.js in plugin folder and already added to nuxt config.
import modals from '#/constants/modal';
export default ({ app }, inject) => {
inject('modalName', modals)
}
In component I can't call value from v-bind, it said : undefined MODAL_SHOWPRO
<Popup :id="$modalName.MODAL_SHOWPRO" />
but I can call it from $emit function something like this:
#click="$nuxt.$emit('showModal', {id: $modalName.MODAL_SHOWPRO})"
Can you let me know why and how to fix it?
Notice: It will work if:
I make data
{
modal: ''
}
and add to created:
async created() {
this.modalName = await this.$modalName
}
Nuxt is a meta-framework aimed at providing an universal app (server then client side). So, you need to think about both server and client.
In your code, you specified ssr: false, this is outdated and should rather be mode: 'client'. But setting so is still false because it means that the ENUM will not be available on the server (hence the error).
Setting it like this is more appropriate (regarding the nature of the plugin) and also fixes the issue
plugins: ['~/plugins/constant.js'],
More on Nuxt plugins: https://nuxtjs.org/docs/directory-structure/plugins#plugins-directory

How to embed html/js widgets in Nuxt, specifically iFlyChat but broadly applicable

I have a couple of plugin widgets that I'd like to integrate into my site but I'm wondering how best to do it to preserve the Nuxt functionality like code-splitting, etc. For example, the code below is for iFlyChat. When I first used the code in my appHeader, it worked, then was intermittent for a while but now doesn't show up at all:
<script>
var iflychat_app_id="xyzappidcode";
var iflychat_external_cdn_host="cdn.iflychat.com",iflychat_bundle=document.createElement("SCRIPT");iflychat_bundle.src="//"+iflychat_external_cdn_host+"/js/iflychat-v2.min.js?app_id="+iflychat_app_id,iflychat_bundle.async="async",document.body.appendChild(iflychat_bundle);var iflychat_popup=document.createElement("DIV");iflychat_popup.className="iflychat-popup",document.body.appendChild(iflychat_popup);
</script>
I've since tried a similar widget for edwid but that didn't show up on the page at all.
You have to create a nuxt plugin to init your code on each page.
Fist, create a file plugins/iflychat.js:
export default () => {
console.log("init iflychat plugin");
var iflychat_app_id = "xyzappidcode";
var iflychat_external_cdn_host="cdn.iflychat.com",iflychat_bundle=document.createElement("SCRIPT");iflychat_bundle.src="//"+iflychat_external_cdn_host+"/js/iflychat-v2.min.js?app_id="+iflychat_app_id,iflychat_bundle.async="async",document.body.appendChild(iflychat_bundle);var iflychat_popup=document.createElement("DIV");iflychat_popup.className="iflychat-popup",document.body.appendChild(iflychat_popup);
}
Then, configure Nuxt.js to import it:
//nuxt.config.js
export default {
plugins: [
{ src: '~plugins/iflychat.js', mode: 'client' }
]
}
That's it, you iFlatChat will run on every page view.

“window is not defined” in Nuxt.js

I get an error porting from Vue.js to Nuxt.js.
I am trying to use vue-session in node_modules. It compiles successfully, but in the browser I see the error:
ReferenceError window is not defined
node_modules\vue-session\index.js:
VueSession.install = function(Vue, options) {
if (options && 'persist' in options && options.persist) STORAGE = window.localStorage;
else STORAGE = window.sessionStorage;
Vue.prototype.$session = {
flash: {
parent: function() {
return Vue.prototype.$session;
},
so, I followed this documentation:
rewardadd.vue:
import VueSession from 'vue-session';
Vue.use(VueSession);
if (process.client) {
require('vue-session');
}
nuxt.config.js:
build: {
vendor: ['vue-session'],
But I still cannot solve this problem.
UPDATED AUGUST 2021
The Window is not defined error results from nodejs server side scripts not recognising the window object which is native to browsers only.
As of nuxt v2.4 you don't need to add the process.client or process.browser object.
Typically your nuxt plugin directory is structured as below:
~/plugins/myplugin.js
import Vue from 'vue';
// your imported custom plugin or in this scenario the 'vue-session' plugin
import VueSession from 'vue-session';
Vue.use(VueSession);
And then in your nuxt.config.js you can now add plugins to your project using the two methods below:
METHOD 1:
Add the mode property with the value 'client' to your plugin
plugins: [
{ src: '~/plugins/myplugin.js', mode: 'client' }
]
METHOD 2: (Simpler in my opinion)
Rename your plugin with the extension .client.js and then add it to your plugins in the nuxt.config.js plugins. Nuxt 2.4.x will recognize the plugin extension as to be rendered on the server side .server.js or the client side .client.js depending on the extension used.
NOTE: Adding the file without either the .client.js or .server.js extensions will render the plugin on both the client side and the server side. Read more here.
plugins: ['~/plugins/myplugin.client.js']
There is no window object on the server side rendering side. But the quick fix is to check process.browser.
created(){
if (process.browser){
console.log(window.innerWidth, window.innerHeight);
}
}
This is a little bit sloppy but it works. Here's a good writeup about how to use plugins to do it better.
Its all covered in nuxt docs and in faq. First you need to make it a plugin. Second you need to make your plugin client side only
plugins: [
{ src: '~/plugins/vue-notifications', mode: 'client' }
]
Also vendor is not used in nuxt 2.x and your process.client not needed if its in plugin with ssr false
In Nuxt 3 you use process.client like so:
if (process.client) {
alert(window);
}
If you've tried most of the answers here and it isn't working for you, check this out, I also had the same problem when using Paystack, a payment package. I will use the OP's instances
Create a plugin with .client.js as extension so that it can be rendered on client side only. So in plugins folder,
create a file 'vue-session.client.js' which is the plugin and put in the code below
import Vue from 'vue'
import VueSession from 'vue-session'
//depending on what you need it for
Vue.use(VueSession)
// I needed mine as a component so I did something like this
Vue.component('vue-session', VueSession)
so in nuxt.config.js, Register the plugin depending on your plugin path
plugins:[
...
{ src: '~/plugins/vue-session.client.js'},
...
]
In index.vue or whatever page you want to use the package... import the package on mounted so it is available when the client page mounts...
export default {
...
mounted() {
if (process.client) {
const VueSession = () => import('vue-session')
}
}
...
}
You can check if you're running with client side or with the browser. window is not defined from the SSR
const isClientSide: boolean = typeof window !== 'undefined'
Lazy loading worked for me. Lazy loading a component in Vue is as easy as importing the component using dynamic import wrapped in a function. We can lazy load the StepProgress component as follows:
export default {
components: {
StepProgress: () => import('vue-step-progress')
}
};
On top of all the answers here, you can also face some other packages that are not compatible with SSR out of the box and that will require some hacks to work properly. Here is my answer in details.
The TLDR is that you'll sometimes need to:
use process.client
use the <client-only> tag
use a dynamic import if needed later on, like const Ace = await import('ace-builds/src-noconflict/ace')
load a component conditionally components: { [process.client && 'VueEditor']: () => import('vue2-editor') }
For me it was the case of using apex-charts in Nuxt, so I had to add ssr: false to nuxt.config.js.

Page reload causes Vuex getter to return undefined

Using Vue.js (Vuetify for FE).
A page reload causes the getter in Vuex to fail with pulling required data from the store. The getter returns undefined. The code can be found on GitHub at: https://github.com/tineich/timmyskittys/tree/master/src
Please see the full details on this issue at timmyskittys.netlify.com/stage1. This page has complete info on the issue and instructions on how to view the issue.
Note, there is mention of www.timmyskittys.com in the issue description. This is the main site. timmyskittys.netlify.com is my test site. So, they are the same for all intents and purposes. But, my demo of this issue is at the Netlify site.
I read the complete issue in the website you mentioned. It's a generic case.
Say, for cat details page url: www.timmyskittys.com/stage2/:id.
Now in Per-Route Guard beforeEnter() you can set the cat-id in store. Then from your component call the api using the cat-id (read from getters)
I found the solution to my issue:
I had to move the call of the action which calls the mutation that loads the .json file (dbdata.json) into a computed() within App.vue. This was originally done in Stage1.vue.
Thanks all for responding.
I had the same issue and my "fix" if it can be called that was to make a timer, so to give the store time to get things right, like so:
<v-treeview
:items="items"
:load-children="setChildren"
/>
</template>
<script>
import { mapGetters } from 'vuex'
const pause = ms => new Promise(resolve => setTimeout(resolve, ms))
export default {
data () {
return {
children: []
}
},
computed: {
...mapGetters('app', ['services']),
items () {
return [{
id: 0,
name: 'Services',
children: this.children
}]
}
},
methods: {
async setChildren () {
await pause(1000)
this.children.push(...this.services)
}
}
}
</script>
Even though this is far from ideal, it works.