vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

Home Page:https://vuejs.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

https://stackblitz.com/edit/vitejs-vite-auh6nk?file=src%2FApp.vue,src%2Fcomponents%2FTestA.vue,src%2Fcomponents%2FTestB.vue&terminal=dev

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 variable listOfB.

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 the listOfB 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.