compile Vue 3 component for HMR - vue.js

vuejs/vue-hot-reload-api for Vue2 accept simple object with component options.
Vue3 have built-in HMR support and it needs a different approach. I can't figure out how properly update component.
What kind of data is need for __VUE_HMR_RUNTIME__.createRecord()?
And how to compile updated component again for __VUE_HMR_RUNTIME__.reload()
I think both functions need render function. But it is created when the component is loaded to dom or something.
When I use something like below, render() function is not present in cmp.
let cmp = defineComponent({template: '<div>test</div>})
let inst = Vue.component('test', cmp)
When I use component in parent component, render() is compiled and included in cmp.
But I cant register changed component same way because Vue3 show warn that this component is already registered.
How to compile updated component below and use it in __VUE_HMR_RUNTIME__.reload()?
let updatedCmp = defineComponent({template: '<div>test Me!</div>'})
I tried vue's compile() function, but it doesn't work for me...
I assume this is the only way to replace a component with a modified version in runtime? :)

Related

Problems with Kotlin react router dom element attribuute for binding a react component to URL

I'm currently working on a sample application to learn Kotlin/Js with the react library. When using the normal JSX syntax, a component can be set to be triggered when a specific URL hits using the BrowserRouter & Route API like below.
Main Component
Navigation Component as directed like in this youtube video
When attempting the above code in Kotlin, it resembles something like the screenshot below using the KotlinDSL
The main function where the component is rendered is shown below
However, that creates an Uncaught Error Exception like the one below
One of the reasons its also so difficult to debug is because my source maps dont work and hence most of my debugging errors are actually in Javascript. But anyways after searching up the javascript error, I found this article about the new Router update that disallows adding components as children.
As a result I tried to pass in the components through the element prop but there seems to be some incompatibility with them.
the element prop accepts an optional ReactNode? and I cant seem to convert my Function Component of type props to a ReactNode. When I try to pass in an optional ReactElement? by calling the create method, I also get an error.
I have been struggling with the Kotlin Documentation and can't seem to find enough examples of react-router implemented.
Can anyone help me out with how to pass in a Function COmponent into the element prop for the React Router?

Pass ref to default slot in renderless component

I am trying to build a renderless component in vue 3 and want to pass a ref to the default slot.
When I am using the h render function I can just pass a ref:
return h('div', {ref: someRef}); // works
If I try to do the same with the default slot, it does not work:
return slots.default({ ref: someRef}) // does not work
return slots.default({ someRef}) // also does not work
Is there any way to do this without wrapping the default slot into another div or similar?
Checked already the documentation and other resources, but couldn't find any solution.
Direct answer
Yes return a function from your setup hook!
setup(_, slots) {
const someRef = ref()
return () => slots.default({ ref: someRef })
}
vue3 docs link
vue3 docs for renderless component pattern
Contextual answer for those in the comment section questioning the renderless/headless pattern
Yes, sometimes you just want a renderless (or headless as the kids these days say) wrapper for functionality (logic), without limiting the HTML that consumers can use.
But for that functionality to work, the renderless/headless component still needs to identify some of the HTML that consumers render into the default slot, in order to provide DOM-related functionality for example.
I have the same struggle when using this pattern, and have been relying on a "hack": passing specific data attributes via slot scope, that consumers need to bind to the desired elements (not only root) and then using good old document.querySelector to grab them
I has served me well in vue2 and I've been using it in production with no problems, but I was wondering if with the new dynamic :ref attribute of vue3, we can do it differently: probably passing a reactive ref variable and then doing a simple assign, and apparently it works.
<renderless #default="{ someRef }">
<some-consumer-comp :ref="(el) => someRef.value = el" />
</renderless>
Here's a sandbox demo old way for vue 2
Here's a sandbox demo new way for vue 3
Do note that if you want to handle edge cases, like handling conditional rendering of slot content, you probably need to do a bit more work and add some watchers.
This pattern is a bit out of fashion these days, with the introduction of vue composables since you can abstract the logic into a useSomeFunctionality and use it directly on the component you want, but it's sill a valid one IMO.
Cheers!

Hook functions defined by mixin not called for third-party plugin component

I'm using Vue with the Vuetify plugin.
I'm extending Vue components globally with a mixin:
Vue.mixin({
mounted() {
console.log('Component mounted');
}
});
I see the log above for all of my own components but not for the v-container component of Vuetify.
Strange thing is, when I inspect the options of this component like so:
Vue.options.components['v-container'].options.mounted
I see that the mounted function defined by my mixin is added to the array of hooks.
After creating a fiddle I was able to see it did work for all other Vuetify components.
From the docs:
Use global mixins sparsely and carefully, because it affects every
single Vue instance created, including third party components.
Am I missing something?
The problem is specific to a component in use, v-container. It is functional component and functional components cannot have lifecycle methods, so ones defined in a mixin will be ignored.
As the documentation describes functional components,
It doesn’t manage any state, watch any state passed to it, and it has no lifecycle methods. Really, it’s only a function with some props.

Vue2 + JQuery library - preventing infinite loop, private property in the component needed

I'm making a component wrapper for jQuery library in Vue2.
In the component I have an input field set as v-model and main vue instance can update it and read it.
Vue2 Component can update it as well.
If Vue2 updated the field, I need to call init function in the library. If change has happened in library, then library sending callback where I'm updating the Vue2 model.
As a result, I have got an infinite loop where Vue2 receiving callback, updating model and receiving another callback...
Callback from the jQuery library is coming asynchronously, and I cannot set a flag for the time of update in Vue2.
I thought I can make a flag saying - something is pushed from Vue2, ignore the jQuery library callback this time. But, I don't know how to make a private property in the Vue2 component.
You can't have "private" variables without a function involved. Functions are the only way to introduce a new scope in javascript.

Is it possible to globally remove a vue component from the global registry?

I would like to remove a components from Vue at runtime that was previously registered with Vue.component(name, {...}), is this possible ?
We are creating a number of components on the fly in a live development setting and would like to remove old components from memory.
Or is it possible to alter the child components registered with a component at runtime ? Only affecting new component instances built after that of course or refreshed manually.
Currently in Vue 2.x, when you register a component with Vue.component it's added to the base constructor options object. You can unregister it by simply deleting the component from the components object:
Vue.component('child-component', ChildComponent)
delete Vue.options.components['child-component']
To display all components:
this.$options.components
To delete a specific component:
delete this.$options.components.NameOfComponent
I believe this should do the trick for you
wm.$destroy()
Documentation for Destroy