Subscribe on changes!
avatar
Oct 22nd 2022

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

avatar
Oct 27th 2022

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.

https://github.com/vuejs/core/blob/830454ae56a3935538b22a871a0ce9cab3b1d1e5/packages/compiler-sfc/src/compileScript.ts#L1381-L1393

avatar
Jan 29th 2023

@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.

avatar
Jan 29th 2023

@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.

avatar
Apr 25th 2023

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

avatar
Apr 25th 2023

@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?