Skip to content

setup() 和 的区别

🕒 Published at:

使用 setup() 函数的效果:

html
<template>
  <button @click="change">+1</button>
  <div>{{ text }}</div>
  {{ flag }}
</template>

<script lang="ts">
import { ref,defineComponent } from 'vue'
export default defineComponent({
  setup() {
    const flag = ref(false)
    let text = 111

    const change = () => {
      text ++
      flag.value = !flag.value
    }

    return {
      flag,
      text,
      change
    }
  }
})
</script>

使用 <script setup> 时的效果:

html
<template>
  <button @click="change">+1</button>
  <div>{{ text }}</div>
  {{ flag }}
</template>

<script setup lang="ts">
import { ref } from 'vue'

const flag = ref(false)
let text = 111

const change = () => {
  text ++
  flag.value = !flag.value
}
</script>

两段代码看上去几乎一模一样,但在运行时却会发现,使用 setup() 函数的版本中页面中 text 的值不会变化,而 <script setup> 版本中页面中 text 的值会变化。为了理解这种现象,我决定查看编译后的产物。

setup()产物

js
export default {
  setup() {
    const flag = ref(false);
    let text = 111;

    const change = () => {
      text++;
      flag.value = !flag.value;
    };

    return {
      flag,
      text,  // 这里是值传递
      change
    };
  }
}

setup() 函数中,返回的 text 是一个普通的局部变量。通过值传递的方式在返回对象中暴露出来。因此,当 text 的值变化时,由于它不是响应式的,Vue 无法追踪到这种变化,也就不会触发视图的更新。

<script setup>产物

js
const __sfc__ = /*#__PURE__*/defineComponent({
  setup(__props, { expose }) {
    expose();

    const flag = ref(false);
    let text = 111;

    const change = () => {
      text++;
      flag.value = !flag.value;
    };

    return { flag, get text() { return text }, set text(v) { text = v }, change };
  }
})

<script setup> 中,编译器会为顶层变量自动生成 getter 和 setter,即使这些变量本身不是响应式的。这样一来,每次渲染时模板会通过 getter 获取最新值,因此表现出类似响应式的效果。