feat(nuxt): remove wrapper from client only components#6165
Conversation
✅ Deploy Preview for nuxt3-docs canceled.
|
- prefer using ctx for attrs instead of options - wrap with h() to avoid oldNodes is null issue
|
@danielroe it should be good now, i've tested on a component with a <template>
<div>
hellow world
<button @click="add">
{{ count }}
</button>
</div>
</template>
<script setup lang="ts">
const count = ref(0)
const add = () => count.value++
defineExpose({ add, count })
</script><template>
<div>
hellow world
<button @click="add">
{{ count }}
</button>
</div>
</template>
<script lang="ts">
export default {
setup () {
const count = ref(0)
const add = () => count.value++
return {
count, add
}
}
}
</script> |
danielroe
left a comment
There was a problem hiding this comment.
This is a nice fix ❤️
I've made a couple of minor changes - feel free to check and let me know if you agree.
|
@danielroe only the setup state was missing, which was the "res". That mean we don't even have to use a collision-resistant name |
|
Would you take a look at stateful use, and test on both build + dev mode? It's not quite there yet, and I wonder if we may have to migrate back to using a render function in some cases. Modify the example component <script setup lang="ts">
function exposedFunc () {
console.log('ok')
}
const state = ref('component')
defineExpose({ exposedFunc })
await new Promise(resolve => setTimeout(resolve, 300))
onMounted(() => { console.log('mounted') })
</script>
<template>
<div>
<!-- this can be commented in/out to trigger different compile approaches -->
client-only {{ state }}
</div>
</template>See SFC playground. |
|
i could fix the build-dev issue, i set this PR to draft to do more testing. There might be some refactor that can be done on the fix. I think moving |
- must use a Fragment to render the build component to avoid "f is null".
|
yes, it needs a fix. I've finally understood when does the component have the render function. Only <script setup> SFC are compiled into setup only component. So we need to keep the render function if the the original component have one |
|
checks
done on
|
|
Currently if users pass Would you explain more about the oldChildren null issue you unearthed? |
This was happening only in build, not sure why but at the setup render function, in some components, if we don't wrap the component render here with a Fragment, the component is not being re-rendered when mounted. And in some cases there's a oldChildren is null error triggered by vue in build and dev. I'll try to reproduce it. |
|
I'm sure you're looking into it, but just to clarify - we're currently getting attrs applied before mount, but not afterwards. |
|
here is the reproduction |
- set back attribute inheritance for template (runtime-compiler) - force null on Fragment props - use h() to render the functions with attrs inheritance
yes, just fixed. Sorry for taking so much of your time for a simple PR 🙏 😢 |
danielroe
left a comment
There was a problem hiding this comment.
This is great - looks good in my testing! Not a simple PR by any means. Well done.
This is likely a Vue issue stackblitz, so using Fragment seems to be good to avoid this |
|
Thank you for this! @huang-julien @danielroe @pi0 |
🔗 Linked issue
resolve nuxt/nuxt#14310
❓ Type of change
📚 Description
Before, the createClientOnly returned a new component wrapping the component with ".client" in its name. So when we were setting a ref on
<HelloWorld ref="helloworld" />the ref assigned was on the<ClientOnly />component but not on<HelloWorld />.This PR make
createClientOnly()modifying the component instead of wrapping it. The setup hook return an additionnalmountedref(). It also override therender()function to conditionally render the component ifmountedvalue is set to true📝 Checklist