I'm writing a module which need to run some code when every vue components destroyed. Is there any mechanism to inject my code into to component destroyed hook?
As for VUE 2 - You can use a mixin as described here https://v2.vuejs.org/v2/guide/mixins.html
A mixin can contain a beforeDestroy or destroyed hook.
Hook functions with the same name are merged into an array so that all of them will be called. Mixin hooks will be called before the component’s own hooks.
You can also use a global mixin to address all components.
You can also apply a mixin globally. Use with caution! Once you apply a mixin globally, it will affect every Vue instance created afterwards.
Vue.mixin({
destroyed() {
console.log('Component has been destroyed logic');
}
})
Related
Is this instance lifecycle hooks diagram also valid(the same) for a vue.js single file component or a global component(vue.componnet())? or is it only used for a Vue instance(new Vue)??
thanks.
Yes, these lifecycle hooks are there for all vue instances: SFCs, global components, explicit new Vue, vue-test-utils wrappers, and so on.
The only thing that SFC do are to write the .render method for you because it's way simpler to write it with an html-like syntax rather than write the render function manually.
In an SFC, you'd tap into them as such:
<script>
export default {
created() {
// do something on created
}
}
</script>
In an explicit new Vue, like this:
new Vue({
created() {
// do something on created
}
});
You get the point.
If the component subscribes to the Vuex events like:
this.$store.watch or this.$store.subscribe
Is it necessary to remove the watcher during component destroy or Vue already takes care of it under the hood?
P.S: Current logic runs on 2.6.10 Vue version.
Following #Cristiano Soleti I checked if listeners are still being called after component that registered them was destroyed.
...and at least in 2.6.10 version, Vuex watcher are actually not removed automatically when component is destroyed. And thus should be unregistered explicitly by developer in beforeDestroy lifecycle hook.
The VueX docs are vague, however it does say "To stop watching, call the returned unwatch function." with no other information, which implies that watchers never get removed any other way: https://vuex.vuejs.org/api/#watch
So, you should manually remove a VueX watcher when destroying a component.
Here's an example:
MyComponent.vue
data: function() {
return {
myWatcher: null // Holds the watcher, so we can reference it
}
},
mounted() {
// Invoke the watcher
this.myWatcher = this.$store.watch(state => state.myStoreVariable, (valueNew, valueOld) => {
// Do stuff
console.log("valueOld: " + valueOld + ", valueNew: " + valueNew)
})
},
beforeDestroy () {
this.myWatcher() // Simply calling the watch holder as a function (ie with parentheses) will remove the watcher.
},
I was sure that the lifecycle hooks in Vue were 8 (beforeCreate, created, beforeMount, mounted, beforeUpdate, updated, beforeDestroy, destroyed) but today I found out(https://v2.vuejs.org/v2/api/#activated) that there are 3 more:
• activated
• deactivated
• error captured
Somebody can explain how these 3 work? Is possible to test them with a console.log? (Just to understand when they are called)
First, a little context:
In Vue 2.0+, there is a built-in component called <keep-alive> that takes the child element inside it and keeps it alive in memory as a cached component. Normally, Vue would reuse a component if its props change, but maybe the component is very complex and is slow to update. You could wrap it with <keep-alive> and the component would be cached for the props provided to it.
When a component inside a <keep-alive> is updated, the activated life-cycle hook is called. When that component is cached and set aside, the deactivated life-cycle hook is called.
The errorCaptured life-cycle hook was added in Vue 2.5.0 and is called whenever an error is captured by a descendent component. So, if you have a component called A that has a child component called B, and that has a child component called C, then if C captures and error, the errorCaptured life-cycle hook will be called on both A and B.
These hooks all work the same as any other hook, so use them the same way.
export default {
data() {
return {}
},
mounted() {
console.log('mounted hook called')
},
errorCaptured(err, vm, info) {
console.log('error captured in component', vm)
console.error(err)
console.log('error info:', info)
},
activated() {
console.log('cached component is being used again')
},
deactivated() {
console.log('component is being kept alive in cache for now')
}
}
https://v2.vuejs.org/v2/api/#activated
https://v2.vuejs.org/v2/api/#deactivated
https://v2.vuejs.org/v2/api/#errorCaptured
I know it's already late for this answer but someone might also looking answer for
the problem. Vue.js disable's console.log function by default, so we have to enable it.
Just put "rules": { "no-console": "off",} on package.json
Cheers
I am trying to create some plugins according to this article:
https://alligator.io/vuejs/creating-custom-plugins/
I have a plugin that needs to run something when the root Vue instance mounts or is created. So far I can only see a way to inject something into all components which is not what I would want.
I simply need to do something when the main Vue instance mounts. How can I do this with a plugin?
The install method from the plugin does not seem to do the trick because this seems to happen before the actual created method.
It's possible to have multiple root Vue components. A "root component" is just a component created with the new syntax and no parent component, so you can detect this as follows:
Vue.mixin({
created() {
if (!this.$parent) {
// This is either the root component or a component
// created with `new` and no parent
}
}
})
It's actually easy to include mixins for just a particular component!
In your component that you want to add the mixin to, just import it like you would anything else, and include an array in your component called mixins like so:
import myMixin from 'src/mixin/myMixin'
export default {
mixins: [myMixin]
}
Then your myMixin code would look like this (don't use Vue.mixin, which is global):
export default {
beforeMount () {
console.log('Your component is about to be mounted!')
}
}
I'm building an app with vue and vue-router. In some routes, I need to check some conditions first, if those conditions are not satisfied, then redirect to another component, so I used the activate hook on the router option of my component, and it works fine. Also, inside that same component, I have the vue created hook to load some data, the thing is that if those conditions that I mentioned before are not met, then I can't load the data in the created hook. What I would expect is that if that condition is not met, and the redirect hook was called, then the created hook wont get triggered, but what is actually happening is that whene that condition is false, then the redirect of the activate hook get calledn and also the created hook from Vue. So, more than a solution for my particular use case, I would like to know the order of execution of the hooks when using vue and vue router together.
For Vue 2.0:
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
beforeDestroy
destroyed
Now when using vue-router 2.0, the data fetching can be done at two places, as per their doc:
Fetching After Navigation: perform the navigation first, and fetch
data in the incoming component's lifecycle hook. Display a loading
state while data is being fetched.
Fetching Before Navigation: Fetch data before navigation in the route
enter guard, and perform the navigation after data has been fetched.
For your case, you can write you data fetching logic in a function and call it inside the "created" hook of the component lifecylce. If at all the data changes with the route, then write a watcher on the $route object, which will trigger your data fetching function.
As the data hook of vue-router 0.7 is deprecated and instead the $route object has been made reactive. Read more here.
Maybe you are interested in In-Component Guards (additional hooks available in components loaded using Vue Router)
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// called before the route that renders this component is confirmed.
// does NOT have access to `this` component instance,
// because it has not been created yet when this guard is called!
},
beforeRouteUpdate (to, from, next) {
// called when the route that renders this component has changed,
// but this component is reused in the new route.
// For example, for a route with dynamic params `/foo/:id`, when we
// navigate between `/foo/1` and `/foo/2`, the same `Foo` component instance
// will be reused, and this hook will be called when that happens.
// has access to `this` component instance.
},
beforeRouteLeave (to, from, next) {
// called when the route that renders this component is about to
// be navigated away from.
// has access to `this` component instance.
}
}
https://router.vuejs.org/guide/advanced/navigation-guards.html#in-component-guards
If you use Vue + Vue Router and you are at RouteA and navigates from it to RouteB and each route/component register for something (e.g. receiving data supported by root) on "created" and unregister on "beforeDestroy" but when you leave RouteA its "beforeDestroy" is called after RouteB "created" so you have nothing registered! I have tested it and VUE 1 had correct order. It must be changed somewhere in VUE 2 + VUE Router 2.
Correct/expected hooks order in VUE 1 when going from RouteA to RouteB:
RouteA beforeDestroy
RouteB created
Incorrect/unexpected hooks order in VUE 2 when going from RouteA to RouteB:
RouteB created
RouteA beforeDestroy
Solution is to use "created" + "beforeRouteLeave" for VUE 2 + VUE Router 2.
Why not add console logs to each and see for yourself?
As far as I can tell without testing:
canReuse
canActivate
-- now the component instance is being created:
created
beforeCompile
compiled
-- (not sure wheither ready or activate comes first, but i guess ready)
ready
activate
data
For your particular case, the data fetching should happen in the data hook - that's what it's for, after all.