Open gnuletik opened 2 years ago
To clarify, what is the case that is currently not working? Can you provide a code sample demonstrating a simplified case of what you intend to do?
Thanks for looking into this! Here are multiple use cases I encountered :
AppContext.vue
<template>
<slot />
</template>
<script setup>
// it could be a library like vue-i18n
provide('something', 'test')
</script>
MyComponent.vue
<script setup>
// inject fails. it should not.
const val = inject('something')
</script>
main.ts
customElements.define(
'my-app-context',
defineCustomElement(AppContext)
)
customElements.define(
'my-component',
defineCustomElement(defineAsyncComponent(() => import('MyComponent.vue')))
)
index.html
<html>
<body>
<my-app-context>
<my-component></my-component>
</my-app-context>
<script src="main.ts"></script>
</body>
</html>
AppContext.vue
<template>
<slot />
</template>
<script setup>
// it could be a library like vue-i18n
provide('something', 'test')
</script>
UtilComponent.vue
<script setup>
// inject fails. it should not.
const val = inject('something')
</script>
MyComponent.vue
<template>
<UtilComponent />
</template>
main.ts
customElements.define(
'my-app-context',
defineCustomElement(AppContext)
)
customElements.define(
'my-component',
defineCustomElement(defineAsyncComponent(() => import('MyComponent.vue')))
)
index.html
<html>
<body>
<my-app-context>
<my-component></my-component>
</my-app-context>
<script src="main.ts"></script>
</body>
</html>
I made a reproduction of both usecases here : https://github.com/gnuletik/vue-inject-deep
Thanks!
Still doesn't work in Vue 3 it seems?
Yes. This has no real prioity for us - the real-life usecase is still cloudy.
Personal Take: Your web components should not depend on a Vue-specific mechanism like provide/inject. Why turn your Vue Components into Web Components if you can only use them in other Vue apps because they depend on provide/inject?
Why turn your Vue Components into Web Components if you can only use them in other Vue apps because they depend on provide/inject?
My usecase was to integrate Vue into a MVC framework like Django, which already provides its own template system.
The web components allows you to load vue components and inject variables with <my-custom-com var="{{ django_var }}"></my-custom-comp>
.
Your web components should not depend on a Vue-specific mechanism like provide/inject
Using an external mechanism (implementing Vue provide/inject into the app) would work if you don't want to use the existing Vue ecosystem because you can't call the useXX()
function of libraries, which usually calls inject
.
I think the use cases described by @gnuletik are very realistic.
EDIT, that actually works ⬇️
The offical vue documentation is a bit confusing in my opinon:
Vue-defined custom element won't be able to inject properties provided by a non-custom-element Vue component.
This means the parent provider component is a regular vue component and custom element is a child component which cannot inject the provided value.
But it doesn't mention that it is also not possible to provide a value from a custom element down into a sub component of the custom element which is also a regular vue component but it lives in the same project as the parent custom element.
You can get provide/inject to work if you use all child components of a custom element also as custom elements. That means you never use the components: { MySubcomponent }
option, all sub components are global.
EDIT end ⬆️
Unfortunately there is still no solution to use provide/inject between custom elements if they were lazy loaded.
But it doesn't mention that it is also not possible to provide a value from a custom element down into a sub component of the custom element which is also a regular vue component but it lives in the same project as the parent custom element.
That seems to work fine: Playground
That seems to work fine: Playground
You're right! Sorry for the wrong accusations. 💐
Just to clarify.
<custom-element-1>
<custom-element-2 />
<custom-element-1 />
custom-element-2
gets the provide('foo', 'bar')
from custom-element-1
via inject('foo')
<VueElement>
<custom-element />
<VueElement/>
custom-element
does NOT get the provide('foo', 'bar')
from VueElement
via inject('foo')
This feels a bit inconsistent to me.
What problem does this feature solve?
As stated in the docs (https://v3.vuejs.org/guide/web-components.html#provide-inject), the Provide/Inject API only works between Vue's Custom Elements.
It makes it harder to use injected libraries inside custom elements.
The use case is :
What does the proposed API look like?
I've thought of several approaches to implement this.
1. Deep provide function
The provide function (https://github.com/vuejs/vue-next/blob/db1dc1c63097ed62a3f683a7a11c7e819d90bb73/packages/runtime-core/src/apiInject.ts#L58-L60) could search inside parent elements :
However, this can be a performance issue when heavily using provide/inject.
It could be enabled when needed as a function parameter like this :
but many libraries use
provide
insideuseLibrary()
-like functions. So, library developers would have to add thedeep
parameter too.To workaround this, this behavior may be manually enabled with a global configuration when required :
2. Implement an event based provide/inject API
The provide/inject API could be reworked with CustomEvents.
There's a current discussion about implement it as a standard : https://github.com/webcomponents/community-protocols/issues/2 so most questions are covered.
There may be other possibilities I did not think about.
Thanks for your work !