I am looking for information on where Vue stores the cached component data when a component is wrapped in the keep-alive component. I looked at the local storage, cache storage, and session storage but I don't see anything obvious. I suspect that the cache is stored in the DOM but I was hoping that someone might know for sure or offer some clarification.
The issue I am having is that I have some Vue components that I am wrapping as web components using https://github.com/vuejs/vue-web-component-wrapper. This library is responsible for adding the keep-alive to my vue components. These components are then used inside other front end frameworks. The keep alive doesn't work as expected in this scenario, if I navigate away from the component and return the component is not restored to it's previous state. What I am expecting to happen is that navigation away from the component and returning restores the component to it's previous state as described in https://v2.vuejs.org/v2/guide/components-dynamic-async.html
After some digging around - in the implementation of keep-alive (Here).
You can see that the cache is stored locally on the component level, meaning that if the <keep-alive> is removed from the DOM it will lose its stored cache.
In theory, if you need it another way you may extend the component - create a prop like cacheName that you pass down to it. Have on mounted check if there is anything in localStorage for that prop to supplement it. Then add a watcher (deep) to watch for changes to the cache and store it in LocalStorage with the key coming for cacheName.
Some rough code of how the component may look - Here. Keep in mind that it may not be possible to get the object from Vue this way so you may need to copy the whole thing then add the code on top of it.
Related
I created a vue component based on MapBox, which is restricted in initializations before it costs money and that is perfectly fine. But I want to reduce reinitializations of my map component for their and my sake.
That's why I thought if it is possible to define the component once, pass in some properties and then handle the state via vuex.
Right now, I'd have to import my component and add the data like this:
<Map
:sources="geoData.sources"
:layers="geoData.layers"
:mapOptions="mapOptions"
:componentOptions="{ drawingEnabled: toggleMapDrawing, activeLayers: activeMapLayers, activeMarkerGroups: [] }"
#loaded="onMapLoaded" #selectedMarkers="onSelectedObjects"/>
The componentOptions are being watched, so the component changes its state accordingly.
My ideas/approaches so far were the following:
I thought about adding the snippet above to the root vue file, but that won't help since I want to place the map component dynamically and not statically before the rest of the page content.
Passing a rendered vue component into a variable and appending that later would be a bit too hacky, if it is even possible.
Using slots, but from what I've seen in the docs, it's not possible to use a slotted component from a parent component in a child like this.
The best idea that has come to my mind was to define the actual MapBox variable (which I suppose triggers the API for initialization) and then save that globally using the store or something. But since that will immediately append the component to a DOM element that will be specified in the options, so I'd have to store that somehow, too.
The initialization of the map happens in the mounted hook of the component and looks like this:
const baseOptions = {
accessToken: process.env.MAPBOX_TOKEN,
container: 'map',
style: process.env.MAPBOX_STYLE_URL,
minZoom: 10,
maxZoom: 20,
zoom: 13,
bearing: 150,
pitch: 50
}
this.map = new mapboxgl.Map(Object.assign(baseOptions, this.mapOptions))
if (!this.map) { throw new Error('Could not create map. Make sure the token is valid.') }
I might be wrong, maybe there's a better way or maybe this whole idea might be garbage, but hopefully it's not. Also please note that I'm not using the vue-mapbox module, because it's not being maintained anymore.
I'm thankful for any ideas and hints :)
You may use <KeepAlive>, a built-in component available in both Vue2 (docs) and Vue3 (docs).
Basically it ensures that a component tagged with keep-alive will only be mounted once. So in your case, you can place the map wherever you want, and the Map will only be initialized once in its mounted hook.
If you need to utilize the moment that your Map gets "focused" or "activated" so to say, then you can utilize the activated and deactivated hooks.
Why you cannot use KeepAlive.
There is an obvious and logical limitation. As long as the parent is alive and mounted, the component's children that are being kept-alive will stay alive. But if the keep-alive component's parent gets unmounted, then all the children will be unmounted aswell even if they were being kept alive. This is all very obvious but I just felt like pointing it out.
Solution
So, in your use case, you want a component (the <Map> component) to be globally kept-alive after its first initialization. I suggest you cache the map element itself and store it in the store. Then on every <Map> component onBeforeMount (Composition API) or beforeMount (Options API) hook, manually check if the element is cached, if it is then insert the cached map from the store, otherwise initialize the map.
I have a Vuejs SPA that I want to clean-up and do some refactoring on. One thing I would like to do is detect
Unused or extra props defined in custom components. I don't mean within the component itself (this I do via eslint-plugin-vue), but when the component is instantiated somewhere in the app within another component.
Unused or extra $emits defined in custom components. Again, are there $emit that are never actually handled when instantiating a component?
Identify component's data that actually dont need to be reactive and can be removed from data
Unused components
Unused exports in my js files
The linter I use, eslint-plugin-vue, does its work component by component but here I want to be checking unused stuff across several components.
I could not find any built-in tool for these tasks, what's the best way to do this?
Even if the app contains hundreds of components I could still do this manually, but ideally I would like to run this process frequently to keep the app clean.
After some research I ended up writing my own node.js script using regex for parsing my codebase. It's too ugly for me to post it anywhere but it is <300 lines and writeable in a day or so.
Let's suppose I have a component called FirstPage, which is my default route, now FirstPage triggers an asynchronous call, with the help of an action of the vuex store, to be made each minute to a backend Api (it's triggered when the component is loaded as a route), now let's say I go to an about route that goes to an About component, is FirstPage still making the calls?
Edit:
I'm not developing an app with that yet, so I can't provide examples.
It's on my interest to know the behavior in these cases of the router, because whenever I change the route I would want to stop making the constant calls (as they won't be necessary).
The reason is that Depending on this I'd have to switch tooling for a project I have in mind.
In general, a component's instance will be destroyed when you navigate away from it. However, there are two exceptions to this ..
When you use routes with params. From the Vue Router docs
One thing to note when using routes with params is that when the user navigates from /user/foo to /user/bar, the same component instance will be reused. Since both routes render the same component, this is more efficient than destroying the old instance and then creating a new one. However, this also means that the lifecycle hooks of the component will not be called.
When you wrap your router-view component within a keep-alive element. Since the <router-view> is essentially a dynamic component.
Generally Vue does a very good job of housekeeping and cleaning up after a component's instance when it gets destroyed. But sometimes you'll have to do some manual cleanup, especially if you use some kind of external library. This is usually handled in the beforeDestroy hook of an instance's lifecycle.
In normal conditions, any logic/scripts/etc done at creation inside said component will be "purged" on the on destroy/close hooks (not only pertinent to vue but seen in lots of other tools), if there is a need to persist something then it should be in a higher scope (or other solution)
Any script written for the respective component only runs if the component is rendered in page. Once you go to about component replacing the previous component then previous script wont run.
you can make a parent component with a router-view and load in your page you always want to get loaded, so your FirstPage component, but this component should just have logic behind it, and no html because otherwise you will always see that rendered. Router-view the page you want to display the real html and stuff. I hope you get the idea, if not i could make an example for you. Goodluck.
I construct deep nested tree of parent and children Vue custom components using my top level component dynamically and then I am updating the data from which all tree is constructed. Which has an effect of rendering the entire tree (its a form with various custom components). I refresh/rebuild the whole form after fetching the data (which is what vue do for reactive data) that itself tell me how to regenerate the view (its like a JSON Schema from which I render the entire view).
This is related to my other issue here.
I am observing a very weird behavior in my Vue Application. When I destroy all my children components and rebuild the data to force rendering the form, it appears that even after I have called $destroy on every child component...Vue is not entirely removing them from cache?
Does vue remove the component from cache if a $destroy is called ?
Because I do not see multiple components of the same type in the Vue component list in the Chrome Vue DevTool extension panel. But I see that the same custom event is handled twice by the same component. Same function that handle the events is getting called twice even though there is only one component visible in Vue DevTools of this type.
This only happens after I render the form. When the page is loaded for the first time every thing works. Then after I reset the form by destroying the child component and resetting the data to re-render the form, magically this child component start handling the event twice.. and in 3rd render it handle the events thrice. But I see only one component in google chrome VueJS DevTool extension panel. So my guess is that vue is still keeping the previously destroyed component in cache. I am trying to figure out how should I destroy those components in the cache.
If anyone has observed something similar and found a solution please let me know.
At the moment I am going to dig little bit more on my component keys (this particular component does not have explicit key set by me).
First and foremost, the vue documentation states:
vm.$destroy
In normal use cases you shouldn’t have to call this method yourself.
Prefer controlling the lifecycle of child components in a data-driven
fashion using v-if and v-for.
So instead of destroying and rebuilding the components manually yourself, you should really letting vue handle that via v-if and v-for. If the components aren't updating to your changes, you might be dealing with a reactivity issue.
As you mentioned that this is a deeply nested structure, the reactivity is key to keeping the components up to data with the data.
Vue does not allow dynamically adding new root-level reactive properties to an already created instance. However, it’s possible to add reactive properties to a nested object using the Vue.set(object, key, value) method:
Vue.set(vm.someObject, 'b', 2)
Inside of a component:
this.$set(this.someObject, 'b', 2)
Also, keep in mind that Vue should be doing the heavy lifting in regards to component management, while you should define the parameters by which a component is rendered.
Basically, I want to keep alive 2 components in router-view and it works, however, I don't know if I am doing it correctly.
<keep-alive include="users, data">
<router-view></router-view>
</keep-alive>
users and data are route names. Is this correct way to do it. Are there any disadvantages of keep-alive?
The only disadvantage is that these components are kept in memory and therefore their state is saved and not reset.
You also lose lifecycle hooks like created, mounted, etc. since the component is not being rebuilt from scratch anymore. You can replace those lifecycle hooks with hooks that are specific to keep-alive components. For example:
https://v2.vuejs.org/v2/api/#activated
Whether keep-alive is a disadvantage or advantage is entirely up to your scenario. If you want to keep the state because you want to switch fast and often between the keep-alive components, it could be an advantage. If you really rely on a clean state through components being built and destroyed, it could be a disadvantage.
1. We can achieve it by using
<keep-alive>
// Render component inside
</keep-alive>
special tag which has provided by vueJs.
2. use life cycle hooks:
a. activated()
b. deactivated()