vue3 中的 ref 在template中使用必须加.value才生效,为什么?

下面按钮,测试3 那个 disabled 不生效,为什么?但是后面 {{options.isDisabled}} 显示是 false,必须要像 测试4 那样加上 .value 才可以,但是 测试1 没有加 .value 确实正常的,这是为什么?

<script setup>
const isDisabled = ref(false);
const options = {
    options: reactive({
        isDisabled: false
    }),
    isDisabled: ref(false),
}
</script>

<template>
<button type="button" :disabled="isDisabled">测试1 {{isDisabled}}</button>
<button type="button" :disabled="options.options.isDisabled">测试2 {{options.options.isDisabled}}</button>
<button type="button" :disabled="options.isDisabled">测试3 {{options.isDisabled}}</button>
<button type="button" :disabled="options.isDisabled.value">测试4 {{options.isDisabled.value}}</button>
</template>

===========================================================================

补充:

大哥,把方法和属性都放在一个ref里面这样会不会存在什么隐藏的问题,这样用感觉看起很好管理,还清晰明了,就是不知道方法放在ref里面会不会有什么隐藏问题?

<script setup lang="ts">
const testOptions = ref({
    disabled: false,
    loading: false,
    id: '#test',
    data: {
        item_id: '',
        title: ''
    },
    data_default: computed(() => testOptions.value.data),
    hasId: false,
    hasUrl: false,
    checkId: () => {
        // ...
    },
    checkUrl: () => {
        // ...
    },
    clackSubmit: () => {
        // ...
    },
    init: (() => {
        console.log(111)

        return true
    })()
})

const test2Options = ref({
    disabled: false,
    loading: false,
    id: '#test',
    data: {
        item_id: '',
        title: ''
    },
    data_default: computed(() => test2Options.value.data),
    hasId: false,
    hasUrl: false,
    checkId: () => {
        // ...
    },
    checkUrl: () => {
        // ...
    },
    clackSubmit: () => {
        // ...
    },
    init: (() => {
        console.log(111)

        return true
    })()
})
</script>

<template>

</template>

<style scoped>

</style>
阅读 1.4k
2 个回答

用法就很奇怪,为啥要用普通的对象来包裹一层呢。
一般来说多个 options 直接用 ref 声明多个变量就可以了呀。

<script setup>
import { ref } from 'vue'
const optionA = ref({
    options: { ... },
    disabled: false
})
const optionB = ref({
    options: { ... },
    disabled: false
})
</script>

<template>
  <div>
    <button type="button" :disabled="optionA.disabled">
      测试A {{optionA.disabled}}
    </button>
    <button type="button" :disabled="optionB.disabled">
      测试B {{optionB.disabled}}
    </button>
  </div>
</template>

如果还是想保留在原本的options的情况下,用 ref 或者 readonly 包裹以下就行了

<script setup>
import { ref, reactive, readonly } from 'vue'
const isDisabled = ref(false);
const options = readonly({
    options: reactive({
        isDisabled: true
    }),
    isDisabled: ref(false)
})

const onChangeA = () => isDisabled.value = true
</script>

<template>
  <button type="button" :disabled="isDisabled">测试1 {{isDisabled}}</button>
  <button type="button" :disabled="options.options.isDisabled">测试2 {{options.options.isDisabled}}</button>
  <button type="button" :disabled="options.isDisabled">测试3 {{options.isDisabled}}</button>
  <button type="button" :disabled="options.isDisabled" @click="onChangeA">测试4 {{options.isDisabled}}</button>
</template>

EDIT

如果说需要有不同的方法给不同的组件调用就是以下这样组合业务逻辑就好了。

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

// 业务块A
const optionA = ref({
    options: { ... },
    disabled: false
})
const onChangeA = () => optionA.value.disabled = true

// 业务块B
const optionB = ref({
    options: { ... },
    disabled: false
})
const onChangeB = () => optionB.value.disabled = true
</script>

<template>
  <button type="button" :disabled="optionA.disabled" @click="onChangeA">测试1 {{optionA.disabled}}</button>
  <button type="button" :disabled="optionB.disabled" @click="onChangeB">测试2 {{optionB.disabled}}</button>
</template>

Vue3中组合式API,其中的一个优势不就是为了解决选项式API中的这个痛点嘛。

62783026-810e6180-ba89-11e9-8774-e7771c8095d6.png

#更灵活的代码组织 - 组合式 API 常见问答 | Vue.js


如果说你觉得这样可能一个SFC文件中会有特别多的业务逻辑,可能会觉得不好维护。
如果放在一个对象中维护,IDE就可以把相关业务逻辑的折叠起来。继续按照你原本的开发习惯使用 readonly 包裹起来。
或者是把业务逻辑都提取到一个单独的JS文件中,然后 import 使用:

<script setup>
import { optionA, onChangeA } from './optionA'
import { optionB, onChangeB } from './optionB'
</script>

<template>
  <button type="button" :disabled="optionA.disabled" @click="onChangeA">测试1 {{ optionA.disabled }}</button>
  <button type="button" :disabled="optionB.disabled" @click="onChangeB">测试2 {{ optionB.disabled }}</button>
</template>
// optionA.js
import { ref } from 'vue'
// 业务逻辑A
const optionA = ref({
  options: {},
  disabled: false
})
const onChangeA = () => optionA.value.disabled = true
export { optionA, onChangeA }

// optionB.js
import { ref } from 'vue'
// 业务逻辑B
const optionB = ref({
  options: {},
  disabled: false
})
const onChangeB = () => optionB.value.disabled = true
export { optionB, onChangeB }

🔗 Vue SFC Playground

https://blog.csdn.net/gtLBTNq9mr3/article/details/124937788


把方法和属性都放在一个ref里面这样会不会存在什么隐藏的问题

不会有什么问题,不过要注意,用 ref 包裹后,内部的 ref 会自动解包,也就是说

const options= ref({
    disabled: ref(false)
})

const disabled = computed(() => options.value.disabled)
                                                // ^ 不需要再 disabled.value 了

如果你想要将变量和方法放在一起管理,那直接提取出去成为要给 hook 就好了。

function useData() {
    const disabled = ref(false)

    function init() {
    
    }

    return {
        disabled,
        init
    }
}
<script setup lang="ts">
const { disabled, init } = useData()
</script>

甚至如果你这个 useData 需要两份数据,还可以这样

<script setup lang="ts">
const data1 = reactive(useData())
const data2 = reactive(useData())
</script>
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏