Vue 3. Can I use ".value" property of a ref() in the <template> section if I want to?

828 Views Asked by At

TLDR: I expect .value to work in the template section but it doesn't.

Everywhere I look, even in the official Vue 3 docs, I am told that (paraphrasing) "I don't have to use '.value'" when using refs in the template section because, I quote, "For convenience, refs are automatically unwrapped when used inside templates (with a few caveats).".

Nowhere did I find that I am explicitly required not to use the ".value" property when in the template section. However when I try to use {{ myConstant.value }} in the template section it doesn't work and I get errors.

So is there a way how to use ".value" in the template section when using ref() or is the documentation's (and other unofficial tutorials') wording simply misleading? Perhaps a config in Vue somewhere that I can alter or an alternative non-mustache syntax or something?

The reason I ask is that I actually want to use ".value" in the template section. Why? Because of consistency. I want to see the same consistent syntax everywhere, wherever possible. The less syntax exceptions the better in my book. I don't consider the automatic unwrapping to be a convenience feature - I consider it to be the opposite.

Thank you!

2

There are 2 best solutions below

1
Andrei Cristian TUDOR On

No, you can't. The property's value is directly accessed in the <template> when using SFC.

<template>
    <h1>{{ restaurant.value.name }}</h1>
</template>
<script setup>
import { ref } from 'vue'
restaurant = ref({name: "Pizzeria"})
</script>

Calling this component would result in a

Uncaught (in promise) TypeError: $setup.restaurant.value is undefined

The .value only works in the composition API syntax because of how it's actually ran behind the scenes, I think composition API translates to options API which later gets translated into vanilla javascript, and in composition API you access ref values using the keyword this..

0
Zihan Hu On

No, currently and by design.


Let's see what Vue (v3.3.10, prod) did...

Demo 1:

<script setup>
import { ref } from 'vue'

const msg = ref('Hello World!')
</script>

<template>
  <h1>{{ msg }}</h1>
</template>

Compiler output:

/* Analyzed bindings: {
  "ref": "setup-const",
  "msg": "setup-ref"
} */
import { unref as _unref, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from 'vue';
import { ref } from 'vue';
const __sfc__ = {
  __name: 'App',
  setup(__props) {
    let msg = ref('Hello World!');
    return (_ctx, _cache) => {
      _openBlock();
      return _createElementBlock('h1', null, _toDisplayString(msg.value), 1);
//                                                            ~~~~~~~~~ compiler is smart
    };
  }
};
__sfc__.__file = 'src/App.vue';
export default __sfc__;

Compiler parsed SFC and found msg is always a ref, so it made a optimization

To see how the compiler performs on unknown types, I'll use let instead of const.

Demo 2 (const -> let):

<script setup>
import { ref } from 'vue'

let msg =  ref('Hello World!')
</script>

<template>
  <h1>{{ msg }}</h1>
</template>

Compiler output:

// ...
setup(__props) {
  let msg = ref('Hello World!');
  return (_ctx, _cache) => {
    _openBlock();
    return _createElementBlock('h1', null, _toDisplayString(_unref(msg)), 1);
//                                                          ~~~~~~~~~~~ compiler is calm
  };
}
// ...

Obviously, unref automatically deconstructs when the value is a ref.

How about assignment?

Demo 3:

<script setup>
import { ref } from 'vue'

let count = ref(0)
</script>

<template>
  <h1>Count: {{ count }}</h1>
  <button @click="++count">Click Me</button>
</template>

Compiler output:

// ...
setup(__props) {
  let count = ref(0)
  return (_ctx, _cache) => {
    return (_openBlock(), _createElementBlock(_Fragment, null, [
      _createElementVNode("h1", null, "Count: " + 
  _toDisplayString(_unref(count)), 1 /* TEXT */),
      _createElementVNode("button", {
        onClick: _cache[0] || (_cache[0] = $event => (_isRef(count) ? ++count.value : ++count))
//                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ compiler is smart and calm
      }, "Click Me")
    ], 64 /* STABLE_FRAGMENT */))
  }
}
// ...

Implementation is here.


If you have read the demos above, you'll find that this feature was intentionally created by the Vue team, rather than being forced to do so due to certain points.

So, you are able to post an RFC to Vue

Tip: there is already a discussion just posted now