Vuejs - vuex lazy load - vue.js

I have a fairly large VueJS SPA, and I just wanted to load the vuex modules on certain routes, using the lazy load.
I was following this article to reproduce this - https://alexjoverm.github.io/2017/07/16/Lazy-load-in-Vue-using-Webpack-s-code-splitting/
However this is giving me an error in vuex.
Folder structure
root
|- store
| |- modules
| | |- auth.js
| | |- module2.js
| |- store.js
|- app.js
auth.js
const state = {
var1: {},
var2: false
}
const mutations = {
'MUTATION_1'(state, obj) {
// logic
}
}
const actions = {
action1({commit}) {
// logic
}
}
const getters = {
var1: state => state.var1,
var2: state => state.var2
}
export default {
state,
mutations,
actions,
getters
}
store.js -
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store();
import('./modules/auth.js').then(auth => {
store.registerModule('/login', auth);
});
export default store;
app.js -
import Vue from 'vue';
import store from './store/store';
import VueRouter from 'vue-router';
import { routes } from './routes/routes';
// vue-router config
Vue.use(VueRouter);
const router = new VueRouter({
mode: 'history',
routes
});
const app = new Vue({
el: '#app',
store,
router
});
error -
[vuex] unknown getter: var1
Any suggestion?

store.registerModule('/login', auth);
The above code registers the auth module with the namespace of login so in order to access its state, getters, mutations, and actions from the store you have to prefix all of those with the path login/. You got the error because you probably tried store.getters.var1 when you should have called store.getters['login/var1'].

Getter is called before the login module was loaded. This happens because import() is asynchronous function, and the module was registered sometime after computed watcher was created.
return store.state.login && store.getters['login/var1'] inside component computed getter will subscribe your component to store.state changes first, then after login module loaded and registered store.state will change and trigger component computed getter and automatically subscribe to any state.login changes

Related

Vue Test Utils with Jest - Mixins not working

I have created a local Vue app and I need to mix a method into all components. The problem is whenever I mount the app the method does not appear to be mixed for child components. Here is my code:
import { createLocalVue, mount } from '#vue/test-utils'
import {gc} from '.....';
import .... from ....
const App = createLocalVue()
App.use( .... )
App.mixin({
methods: {
gc: key => gc(key)
}
})
export const Wrapper = mount(AppComponent, {
App,
i18n,
router,
store,
...
})
Whenever I import "Wrapper" into any test it fails to the first mounted component with message:
TypeError: _vm.gc is not a function
How can I include mixins to propagate to all the child components?
mount has no App option. Instead, there's localVue to supply Vue copy.
It should be:
mount(AppComponent, {
localVue: App,
...

Using the NuxtJS Strapi Module in Vuex

I am using #nuxt/strapi as a wrapper around Strapi for NuxtJS.
For Vuex inside Nuxt I am using the namespaced modules mode, so my structure looks as an example like this:
store
├── user.js
├── posts.js
└── index.js
In a Vue project I would import Vue, Vuex, maybe Axios and then create a store instance const store = new Vuex.Store(storeOptions) and dispatch my initial actions or make some API calls from inside store/index.js.
Example for index.js in a vue project:
import Vuex from 'vuex';
import Vue from 'vue';
import user from './modules/user'
import posts from './modules/posts'
Vue.use(Vuex);
const storeOptions = { ... }
const store = new Vuex.Store(storeOptions)
if (user) {
...
store.dispatch('startPageLoading', 'store');
store.commit('SET_USER', user);
await store.dispatch("getSomeData");
...
}
But in a Nuxt project the store and the strapi instances already were created in root_project/.nuxt/.... So what is the best way to use $strapi and $store inside store/index.js? Neither $strapi or $store are available in store/index.js
there is this nuxtServerInit action that gets executed on the server side after you open / refresh page.
You can only do it in index.js file
This is how your index.js could look like:
//index.js
export const state = () => ({
someState: false
})
export const mutations = {
SOME_MUTATION(state, payload) {
...
}
}
export const actions = {
SOME_ACTION({ state, commit }, payload) {
...
},
nuxtServerInit({ commit }, { $strapi }){
//do here something on the server side...
}
}
The documentation says if you added the strapi module to your project, you should be able to access it from the context. The context is the second argument from nuxtServerInit
Thats it. This is how a store could look like, nothing fancy.
You could dispatch / commit something from any component with
this.$store.commit("SOME_MUTATION")
They say its global available with this.$strapi
This module globally injects $strapi instance, meaning that you can access it anywhere using this.$strapi. For plugins, asyncData, fetch, nuxtServerInit and Middleware, you can access it from context.$strapi.

Theory: Axios Calls (Specifically for VueJS)

On component mount(), Axios fetches information from the back end. On a production site, where the user is going back and forth between routes it would be inefficient to make the same call again and again when the data is already in state.
How do the pros design their VueJS apps so that unnecessary Axios calls are not made?
Thank you,
If the data is central to your application and being stored in Vuex (assuming that's what you mean by "state"), why not just load it where you initialise your store?
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'wherever'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
centralData: {}
},
mutations: {
setCentralData (state, centralData) {
state.centralData = centralData
}
},
actions: {
async loadCentralData ({ commit }) {
const { data } = await axios.get('/backend')
commit('setCentralData', data)
}
}
}
// initialise
export const init = store.dispatch('loadCentralData')
export default store
If you need to wait for the dispatch to complete before (for example) mounting your root Vue instance, you can use the init promise
import Vue from 'vue'
import router from 'path/to/router'
import store, { init } from 'path/to/store'
init.then(() => {
new Vue({
store,
router,
// etc
}).$mount('#app')
})
You can import and use the init promise anywhere in order to wait for the data to load.

importing store to a vuejs nuxt project

I'm trying to write a simple plugin for my Vue.js(Nuxt) project. I came across this post Adding Mutations to Vuex store as part of Vue Plugin but still unable to get it working.
Here is my application structure.
~ is root
~/plugins/HTTP/index.js
~/plugins/HTTP/_store/ => index.js, actions.js, getters.js, mutations.js
~/plugins/HTTP/_api/ => index.js
**Global Store**
~/store/index.js
~/store/modules/
~/store/modules/testing => index.js, actions.js, getters.js, mutations.js
in my ~/plugins/HTTP/index.js, I have the following code
import Vue from 'vue';
import store from '~/store';
const HTTP = {
install(vue, { store }){ // Now you plugin depend on store
if(!store){
throw new Error('Please provide vuex plugin.')
}
// register your own vuex module
store.registerModule({store})
}
}
export default HTTP;
Vue.use(HTTP)
In my ~/store/index.js I have the following code:
import Vuex from 'vuex'
import testingModule from './modules/testing'
const state = () => {
return new Vuex.Store({
modules:{
testing: testingModule
}
})
}
export default state
When I try to run it, it gives me the following message:
Cannot destructure property `store` of 'undefined' or 'null'.
What did I do wrong here?
You aren't passing any properties so the error is correct. You need pass in an options object when you tell it to use. It can be empty, but it needs an object.
import Vue from 'vue';
import store from '~/store';
const HTTP = {
install(vue, { store }){ // Now you plugin depend on store
if(!store){
throw new Error('Please provide vuex plugin.')
}
// register your own vuex module
store.registerModule({store})
}
}
export default HTTP;
Vue.use(HTTP, {}) // <---------- Empty object to avoid allow destructuring.

Vue.js / Mixins - Is there a way to get the global mixin-object outside of the vue component?

I am new with Vue.js
I am using Vue.js 2.4.4.
I have created the global mixin in my app.js file:
...
import router from './router'
...(some imports and plugins definitions)
Vue.use(VeeValidate);
Vue.component(VuePassword);
...
Vue.mixin({
data: function(){
return {
get auth(){
return Auth;
}
}
}
});
const app = new Vue({
el: '#root',
template: `<app></app>`,
components: { App },
router
});
This mixin imports some Auth object with validation methods e.t.c which needed to be in every component.
All of my components can check this mixin and it's working fine.
But I need to check the auth state after every route request, and I want to use my currently existing mixin, so I am trying to make something like this in my router.js file:
import Vue from 'vue'
import VueRouter from 'vue-router'
...
Vue.use(VueRouter);
const router = new VueRouter({
routes:[
...
]
});
router.beforeEach((to, from, next) => {
if(to.meta.requiresAuth) {
if(...call to mixin method) {
next();
} else {
next('/');
}
} else {
next();
}
});
export default router
Question:
Is there a way to get the global mixin object and change it's inner values or can you please give some small advise or example what is the right solution to this kind of tasks?
Or should I use the plugins instead of mixins?
I would rather create a seperate file for auth and not make it a mixin. Then using Vue.use() which will set auth on the vue object.
A sample of what the files might look like:
auth.js
export default function(Vue) {
Vue.auth = {
// do your auth logic
}
}
Then in your main js file
main.js
import Auth from './auth.js'
Vue.use(Auth);
Then you should be able to use Vue.auth
Another option would be keep using the mixin and pass the value to a store (like vuex) or create your own if your project is small...