vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
https://vuejs.org/
MIT License
47.8k stars 8.35k forks source link

select v-model does not set 'selected' attribute on option with v-for directive using SSR #12395

Closed tirojal closed 1 week ago

tirojal commented 1 week ago

Vue version

3.5.12

Link to minimal reproduction

https://play.vuejs.org/#__SSR__eNqFUstOwzAQ/BXLl4BUWsSbKiAB4gAHQIC4YA4l3Ra3jm3Zm1AU5d/Z2E0fCEoiZeKdyWbG64pfWNstC+B9nvrMSYvMAxb2XGiZW+OQVczBiNVs5EzOEpImQgudGe2R4Qe8DFQB7KwRbSWHu7vJdksOlAqkn7OvyeyfK+mwZI9aEOxHOIjQ9CU4inAc4STCKcEb/TTtRf/knBYIuVUDBFqlQ1k2RaQ7PFMPCjJk5U5uhqDOBG9zCN4KMTUWpaFWcSmw3BkZR1prvJfvah5c6mVOwZfq/hS+forX+LKp/K1Y+BBYVWz9n3W9MNmLLudq2oKQLCzTXsyd9lb2gnc4ehrPSI67E280jb0SmjHBM5NbqcDdh4aUpc8C03CU0Hzehhq6AjptPfuAbPpLfeJnTU3wBwceXEm5FhwO3Bgw0tdPdzCj9wVJ8ygUqTeQj+CNKsJoguyy0EOyvaILbm/C4ZV6/OyvZwjat6Eao42yDnrB6UBfbYi+tLvfPQjfCV3z+hu9thho

Steps to reproduce

Click on refresh button.

What is expected?

option with value === '500' should have selected attribute on SSR

What is actually happening?

option receives selected attribute only on CSR, you may see 'xxxxxxxx' option is selected for a moment after pressing 'refresh'

System Info

No response

Any additional comments?

Found similar issue, but in my case it happens only when option have v-for attribute https://github.com/vuejs/core/issues/5339

tirojal commented 1 week ago

and another reproduction:

import { createSSRApp } from 'vue'
import { renderToString } from 'vue/server-renderer'

const appOk = createSSRApp({
    data: () => ({ theValue: '400' }),
    template: `<select v-model="theValue">
            <option value="100">100</option>
            <option value="200">200</option>
            <option value="300">300</option>
            <option value="400">400</option>
        </select>`,
})
const appNotOk = createSSRApp({
    data: () => ({ theValue: '400', allValues: ['100', '200', '300', '400'] }),
    template: `<select v-model="theValue">
            <option
                v-for="possibleValue in allValues"
                :key="possibleValue"
                :value="possibleValue"
            >
                {{ possibleValue }}
            </option>
        </select>`,
})

renderToString(appOk).then((html) => {
    console.log('appOk')
    console.log('-----')
    console.log(html) // <select><option value="100">100</option><option value="200">200</option><option value="300">300</option><option value="400" selected>400</option></select>
    console.log('-----')
    console.log('-----')
})
renderToString(appNotOk).then((html) => {
    console.log('appNotOk')
    console.log('-----')
    console.log(html) //<select><!--[--><option value="100">100</option><option value="200">200</option><option value="300">300</option><option value="400">400</option><!--]--></select>
    console.log('-----')
    console.log('-----')
})
tirojal commented 1 week ago

https://github.com/vuejs/core/blob/a49858f3eecba55ab188e3bdb8afed7db70ff335/packages/compiler-ssr/src/transforms/ssrVModel.ts#L166-L168

processOption on line 167 is skipped because child.type is not NodeTypes.ELEMENT but NodeTypes.FOR