Subscribe on changes!

`defineExpose` attributes are not available from options API methods called from first template render in production build, despite it working in Vite dev mode

avatar
Sep 20th 2022

Vue version

3.2.39

Link to minimal reproduction

https://github.com/segevfiner/vue-define-expose-bug

Steps to reproduce

  1. Run pnpm dev, open the page, The Bar: {{ bar() }} in the components used in HomeView renders correctly.
  2. Run pnpm build & pnpm preview, Bar: {{ bar() }} throws an exception.

What is expected?

It should work. It works in dev, so it should work in build as well.

What is actually happening?

It throws:

index.9acc7ff5.js:1 TypeError: this.foo is not a function
    at Proxy.bar (index.9acc7ff5.js:9:22894)
    at Proxy.<anonymous> (index.9acc7ff5.js:9:23095)
    at Mn (index.9acc7ff5.js:1:15620)
    at rs.w [as fn] (index.9acc7ff5.js:1:34772)
    at rs.run (index.9acc7ff5.js:1:4548)
    at u.update (index.9acc7ff5.js:1:35042)
    at ee (index.9acc7ff5.js:1:35068)
    at pt (index.9acc7ff5.js:1:33922)
    at ht (index.9acc7ff5.js:1:33716)
    at z (index.9acc7ff5.js:1:30557)

System Info

System:
    OS: macOS 12.6
    CPU: (10) arm64 Apple M1 Pro
    Memory: 79.61 MB / 16.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.17.0 - ~/.nvm/versions/node/v16.17.0/bin/node
    Yarn: 1.22.19 - ~/.nvm/versions/node/v16.17.0/bin/yarn
    npm: 8.19.2 - ~/.nvm/versions/node/v16.17.0/bin/npm
  Browsers:
    Chrome: 105.0.5195.125
    Safari: 16.0
  npmPackages:
    vue: ^3.2.38 => 3.2.39

Any additional comments?

This seems to happen due to the exposed attributes not being exposed/defined on this before running the render function/template for the first time in the production build, although they are available properly in dev mode, and they have proper type definitions, which makes it seem like this should work.

There is no warning at all about this not working in a production build, and the type checking and linting passes.

avatar
Sep 20th 2022

Mixing script setup and options API like this is invalid. We will make this fail in dev in a future release.

See a similar discussion in #6677

avatar
Sep 20th 2022

A lint can also help, but might be a bit complex? Oh well, this will make migration difficult for us due to widespread use of mixins that are difficult to convert. Any suggestions on how to ease migration if such dual usage should not be done?

avatar
Sep 20th 2022

you can use a normal setup() function, if that helps.

avatar
Sep 20th 2022

That will help. Although script setup is obviously nicer... Sadly I doubt mixins can be used or will be made available to use with it, as they are "technically deprecated"/"not recommended", so guess that's our only option for the time being.

avatar
Sep 20th 2022

You'll need to convert the mixins sometime, as you can't use mixins in Composition API, yes. I'd likely try and convert one mixin into a composable, then inject that via setup:

// use it just for composables from mixins
setup() {
  return {
    ...useMyComposableFromAMixin()
  }
},
// rest of existing Options API ...

and once all mixins are transformed, then migrate components themselves to composition API - if that make sense for you at all.

avatar
Sep 21st 2022

Yeah. And using them via splatting saves from needing to make a mixin shim for the old code that uses them. Thanks!

avatar
Sep 22nd 2022

I'll close this in favor of #6677 which tracks the underlying issue.