Skip to content

响应式基础 {#reactivity-fundamentals}

:::tip API 参考 本页和后面很多页面中都分别包含了选项式 API 和组合式 API 的示例代码。现在你选择的是 选项式 API组合式 API。你可以使用左侧侧边栏顶部的“API 风格偏好”开关在 API 风格之间切换。 :::

## 声明响应式状态 \* {#declaring-reactive-state} 选用选项式 API 时,会用 `data` 选项来声明组件的响应式状态。此选项的值应为返回一个对象的函数。Vue 将在创建新组件实例的时候调用此函数,并将函数返回的对象用响应式系统进行包装。此对象的所有顶层属性都会被代理到组件实例 (即方法和生命周期钩子中的 `this`) 上。 ```js{2-6} export default { data() { return { count: 1 } }, // `mounted` 是生命周期钩子,之后我们会讲到 mounted() { // `this` 指向当前组件实例 console.log(this.count) // => 1 // 数据属性也可以被更改 this.count = 2 } }

[在演练场中尝试一下](https://play.vuejs.org/#eNpFUNFqhDAQ/JXBpzsoHu2j3B2U/oYPpnGtoetGkrW2iP/eRFsPApthd2Zndilex7H8mqioimu0wY16r4W+Rx8ULXVmYsVSC9AaNafz/gcC6RTkHwHWT6IVnne85rI+1ZLr5YJmyG1qG7gIA3Yd2R/LhN77T8y9sz1mwuyYkXazcQI2SiHz/7iP3VlQexeb5KKjEKEe2lPyMIxeSBROohqxVO4E6yV6ppL9xykTy83tOQvd7tnzoZtDwhrBO2GYNFloYWLyxrzPPOi44WWLWUt618txvASUhhRCKSHgbZt2scKy7HfCujGOqWL9BVfOgyI=)

这些实例上的属性仅在实例首次创建时被添加,因此你需要确保它们都出现在 `data` 函数返回的对象上。若所需的值还未准备好,在必要时也可以使用 `null`、`undefined` 或者其他一些值占位。

虽然也可以不在 `data` 上定义,直接向组件实例添加新属性,但这个属性将无法触发响应式更新。

Vue 在组件实例上暴露的内置 API 使用 `$` 作为前缀。它同时也为内部属性保留 `_` 前缀。因此,你应该避免在顶层 `data` 上使用任何以这些字符作前缀的属性。

### 响应式代理 vs. 原始值 \* {#reactive-proxy-vs-original}

在 Vue 3 中,数据是基于 [JavaScript Proxy (代理)](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy) 实现响应式的。使用过 Vue 2 的用户可能需要注意下面这样的边界情况:

```js
export default {
  data() {
    return {
      someObject: {}
    }
  },
  mounted() {
    const newObject = {}
    this.someObject = newObject

    console.log(newObject === this.someObject) // false
  }
}
当你在赋值后再访问 `this.someObject`,此值已经是原来的 `newObject` 的一个响应式代理。**与 Vue 2 不同的是,这里原始的 `newObject` 不会变为响应式:请确保始终通过 `this` 来访问响应式状态。**
## 声明响应式状态 \*\* {#declaring-reactive-state-1} ### `ref()` \*\* {#ref} 在组合式 API 中,推荐使用 [`ref()`](/api/reactivity-core#ref) 函数来声明响应式状态:
import { ref } from 'vue'

const count = ref(0)
`ref()` 接收参数,并将其包裹在一个带有 `.value` 属性的 ref 对象中返回:
const count = ref(0)

console.log(count) // { value: 0 }
console.log(count.value) // 0

count.value++
console.log(count.value) // 1
> 参考:[为 refs 标注类型](/guide/typescript/composition-api#typing-ref) 要在组件模板中访问 ref,请从组件的 `setup()` 函数中声明并返回它们: ```js{5,9-11} import { ref } from 'vue' export default { // `setup` 是一个特殊的钩子,专门用于组合式 API。 setup() { const count = ref(0) // 将 ref 暴露给模板 return { count } } }

```vue-html
<div>{{ count }}</div>
注意,在模板中使用 ref 时,我们**不**需要附加 `.value`。为了方便起见,当在模板中使用时,ref 会自动解包 (有一些[注意事项](#caveat-when-unwrapping-in-templates))。 你也可以直接在事件监听器中改变一个 ref: ```vue-html{1}

对于更复杂的逻辑,我们可以在同一作用域内声明更改 ref 的函数,并将它们作为方法与状态一起公开:

```js{7-10,15}
import { ref } from 'vue'

export default {
  setup() {
    const count = ref(0)

    function increment() {
      // 在 JavaScript 中需要 .value
      count.value++
    }

    // 不要忘记同时暴露 increment 函数
    return {
      count,
      increment
    }
  }
}
然后,暴露的方法可以被用作事件监听器: ```vue-html{1}

这里是 [Codepen](https://codepen.io/vuejs-examples/pen/WNYbaqo) 上的例子,没有使用任何构建工具。

### `<script setup>` \*\* {#script-setup}

在 `setup()` 函数中手动暴露大量的状态和方法非常繁琐。幸运的是,我们可以通过使用[单文件组件 (SFC)](/guide/scaling-up/sfc) 来避免这种情况。我们可以使用 `<script setup>` 来大幅度地简化代码:

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

const count = ref(0)

function increment() {
  count.value++
}
</script>

<template>
  <button @click="increment">
    {{ count }}
  </button>
</template>
[在演练场中尝试一下](https://play.vuejs.org/#eNo9jUEKgzAQRa8yZKMiaNcllvYe2dgwQqiZhDhxE3L3jrW4/DPvv1/UK8Zhz6juSm82uciwIef4MOR8DImhQMIFKiwpeGgEbQwZsoE2BhsyMUwH0d66475ksuwCgSOb0CNx20ExBCc77POase8NVUN6PBdlSwKjj+vMKAlAvzOzWJ52dfYzGXXpjPoBAKX856uopDGeFfnq8XKp+gWq4FAi) `