v-bind css variable crashes during ssr dev
Vue version
3.2.41
Link to minimal reproduction
https://github.com/Trinovantes/vue-ssr-css-bind
Steps to reproduce
yarn install
yarn webpack
yarn watch
Visit localhost:8080
and server crashes with
[Vue warn]: Unhandled error during execution of watcher callback
at <App>
[Vue warn]: Unhandled error during execution of setup function
at <App>
ReferenceError: Cannot access 'paddingTop' before initialization
at /home/stephen/Projects/vue-ssr-css-bind/dist/server.js:14760:73
at setVars (/home/stephen/Projects/vue-ssr-css-bind/dist/server.js:12565:58)
at callWithErrorHandling (/home/stephen/Projects/vue-ssr-css-bind/dist/server.js:6047:18)
at callWithAsyncErrorHandling (/home/stephen/Projects/vue-ssr-css-bind/dist/server.js:6055:17)
at getter (/home/stephen/Projects/vue-ssr-css-bind/dist/server.js:7266:16)
at doWatch (/home/stephen/Projects/vue-ssr-css-bind/dist/server.js:7286:7)
at watchPostEffect (/home/stephen/Projects/vue-ssr-css-bind/dist/server.js:7207:10)
at useCssVars (/home/stephen/Projects/vue-ssr-css-bind/dist/server.js:12566:69)
at setup (/home/stephen/Projects/vue-ssr-css-bind/dist/server.js:14759:52)
at callWithErrorHandling (/home/stephen/Projects/vue-ssr-css-bind/dist/server.js:6047:18)
What is expected?
No crash
What is actually happening?
The generated setup function seems to be referencing paddingTop
variable before it is declared
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({
__name: "App",
setup(__props, { expose }) {
expose();
(0,vue__WEBPACK_IMPORTED_MODULE_0__.useCssVars)((_ctx) => ({
"7ba5bd90-paddingTop": (0,vue__WEBPACK_IMPORTED_MODULE_0__.unref)(paddingTop)
}));
const paddingTop = (0,vue__WEBPACK_IMPORTED_MODULE_0__.computed)(() => "100px");
const __returned__ = { paddingTop, computed: vue__WEBPACK_IMPORTED_MODULE_0__.computed };
Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
return __returned__;
}
});
System Info
System:
OS: Linux 5.10 Ubuntu 20.04.5 LTS (Focal Fossa)
CPU: (12) x64 AMD Ryzen 5 2600 Six-Core Processor
Memory: 12.60 GB / 15.60 GB
Container: Yes
Shell: 5.0.17 - /bin/bash
Binaries:
Node: 16.15.0 - /usr/local/bin/node
Yarn: 1.22.19 - ~/.yarn/bin/yarn
npm: 8.17.0 - /usr/local/bin/npm
npmPackages:
vue: ^3.2.41 => 3.2.41
Any additional comments?
Works fine without SSR
Workaround:
<h1 :style="{paddingTop}">
in the <template>
instead but this is pretty cumbersome
We do not inject useCssVars
in SSR mode. This seems related to vue-loader
.
The inlineTemplate
is always false
passed from vue-loader
in DEV mode.
@edison1105 There are some use cases where it would actually be nice to have css vars work with SSR. For instance, if I am prerendering and then want to lazily hydrate some components but still have the css vars initially bound so styles show up. Not sure how Astro is doing this but my guess is you can't use css vars unless you are eventually CSR which is limiting.
Now Vue is often being used as an SSR templating language where people may want to use props to set css dynamically during the build but may never hydrate that component. I didn't see much other related discussion so thought I would chime in here. Happy to make a separate issue.
@earlAchromatic
For instance, if I am prerendering and then want to lazily hydrate some components but still have the css vars initially bound so styles show up.
Lazily hydrate is not supported by vue-core, Maybe you have to wait until Q2. The final implementation is uncertain and may not be the same as Astro. Now, CSS vars in SSR do not need hydrate because the initial value of CSS var is calculated in the SSR render phase.
people may want to use props to set css dynamically during the build but may never hydrate that component.
It will have to wait until vue implements lazily hydrate to see if there is a similar problem. If you already have this problem now, please open issues if you encounter them and talk about them more.
FYI this bug is currently happening in vue 3.2.47. I am simply running the vite --host
command.
Given the shortened following component:
<template>
<Icon :icon="icon" />
</template>
<script setup lang="ts">
import { computed, toRefs } from 'vue'
const props = defineProps<{
state: 'pending' | 'running' | 'completed' | 'failed'
}>()
const { bgColor, color, icon } = toRefs(useStatusDisplay(state))
</script>
<style scoped lang="scss">
.run-status :deep(i) {
color: v-bind(color);
}
</style>
it outputs
setup(__props, { expose }) {
expose();
const props = __props;
_useCssVars((_ctx) => ({
"1c89043a-color": _unref(color)
}));
const { bgColor, color, icon } = toRefs(useStatusDisplay(state));
}
with the error: Uncaught (in promise) ReferenceError: Unhandled Promise Rejection: can't access lexical declaration 'color' before initialization
@Lyokolux It can work normally because this code will run after mounting.
(_ctx) => ({
"1c89043a-color": _unref(color)
})
it works fine see demo Would you mind provide a runnable mimimal reproduction?