Inconsistent behavior of template refs between development server and production builds
Nicolas-Yazzoom opened this issue · comments
Vue version
3.4.31
Link to minimal reproduction
Steps to reproduce
Observe behavior during dev:
- (Dev server starts automatically when opening repro)
- Click "button"
- An alert is shown and component
TestB
can be inspected in the console using the logged variablelistOfB
.
Observe behavior after building:
- Stop dev server in terminal
- Run "npm run build"
- Run "npm run preview"
- Click "button"
- The alert is not shown and
TestB
is not included in thelistOfB
variable.
What is expected?
In both cases the exposed method from TestB.vue
should be called, showing the alert. In both cases listOfB should contain a reference to the TestB
instance.
What is actually happening?
In the production build, the alert is not shown because listOfB
does not contain a reference to the instance of TestB
. Because of this, it cannot call the exposed method.
System Info
(Ran this in the StackBlitz terminal)
System:
OS: Linux 5.0 undefined
CPU: (8) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
Memory: 0 Bytes / 0 Bytes
Shell: 1.0 - /bin/jsh
Binaries:
Node: 18.20.3 - /usr/local/bin/node
Yarn: 1.22.19 - /usr/local/bin/yarn
npm: 10.2.3 - /usr/local/bin/npm
pnpm: 8.15.6 - /usr/local/bin/pnpm
npmPackages:
vue: ^3.4.31 => 3.4.31
Any additional comments?
The docs do not mention dynamically setting a template ref (using :ref=" "
instead of ref=" "
) is not allowed.
a minimal reproduction: Playground
as a workaround, use instance.refs
(see the playground demo above) or function refs.
I could not get your workaround to work at first. After some investigation I found a few more inconsistencies which might be related to this issue: Playground. Click the button in both DEV and PROD modes and compare the result in the console.
What I was doing first was init'ing the ref as an empty array according to the docs. Your playground init'ed them like this: ref()
. I'm unsure what this does as the docs don't mention this.
Here are the results in a table:
Variable | ref in <template> refers to variable |
ref in <script> init'ed as array |
ref.value in DEV |
instance.refs in DEV |
ref.value in PROD |
instance.refs in PROD |
---|---|---|---|---|---|---|
testA | Yes | Yes | ✔️ | Missing | Empty array | ✔️ |
testB | Yes | No | ✔️ | ✔️ | undefined |
✔️ |
testC | No | Yes | ✔️ | Missing | ✔️ | Missing |
testD | No | No | ✔️ | ✔️ | ✔️ | Present as proxy. A and B are not a proxy and D was not a proxy during DEV. |
This is a duplicate of #4866
As said, this is mostly a design flaw and there's no real fix for it. I am thinking of providing a dedicate useTemplateRef
function in 3.5.
I was able to work around this issue like this. I updated my code to look like the testB
case. I'm using getCurrentInstance()
as suggested by @jh-leong which works both in DEV and PROD as long as you DON'T define const testB = ref([])
. Defining the ref in <script setup>
puts you in case A which breaks during DEV.