vue3 组合式 API:setup()

查看vue3官网介绍:组合式 API:setup()

在 Vue 3 中,组合式 API 的 setup() 函数是一个非常重要的特性,它提供了一种更灵活和可维护的方式来组织组件的逻辑。

基本概念

setup() 函数是在组件实例创建之前执行的,它用于组合组件的逻辑,包括响应式数据、方法、计算属性、监听等。它接收两个参数:propscontext,并返回一个对象,其中包含可以在组件模板中使用的属性和方法。

特点

  • setup函数返回的对象中的内容,可直接在模板中使用。
  • setup中访问thisundefined
  • setup函数会在beforeCreate之前调用,它是“领先”所有钩子执行的。

示例:

<template>
  <div>
    <div>姓名:{{name}}</div>
    <div>年龄:{{age}}</div>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
  </div>
</template>
<script lang="ts">
export default {
  name: 'Person',
  setup() {
  	console.log(this);  // undefined
    // 数据(在setup中直接声明变量,此时的name、age、tel都不是响应式的数据)
    // 注意:直接声明变量,数据不是响应式的。
    let name = '张三'
    let age = 36

    // 方法
    function changeName() {
      console.log('change name')
      name = 'John'  // 这样修改name,页面是不会响应的
      console.log('changed name: ' + name); // changed name: John
      //  成功修改 name ,但是页面没有响应
    }
    function changeAge() {
      age--
      console.log(age);
      // 成功修改 age ,但是页面没有响应
    }
    // 在 setup() 函数中返回的对象会暴露给模板和组件实例。
    // 将数据、方法暴露出去,模板中才可以使用
    return { name, age, changeName, changeAge}
  }
}
</script>

setup函数的返回值

  • 若返回一个对象:则对象中的:属性、方法等,在模板中均可以直接使用。
  • 若返回一个渲染函数:则可以自定义渲染内容。代码如下:
// 不论页面模板是什么样,直接渲染返回的内容
setup(){
  return ()=> 'vue3起飞!'
}

参数 Props

setup 函数的第一个参数是组件的 props。和标准的组件一致,一个 setup 函数的 props 是响应式的,并且会在传入新的 props 时同步更新。

通过参数props,组件可以接收来自父组件传递下来的属性值。

<template>
  <div>{{ message }}</div>
</template>

<script>
export default {
  setup(props) {
    // props.message 是响应式的,当父组件传递的 message 属性值变化时,这里也会更新
    return {
      message: props.message,
    };
  },
  props: {
    message: String,
  },
};
</script>

在这个例子中,setup函数接收 props作为参数,并将 props.message的值返回给模板使用。当父组件中修改 message属性的值时,子组件中的显示也会同步更新。

如果解构props 对象,解构出来的变量会失去响应性。因此,通过 props.xxx 的形式来使用其中的 props

如果要解构props 对象,并且要解构出来的变量保持响应性,可以使用 vue3响应式工具 toRefs() 和 toRef()

import { toRefs, toRef } from 'vue'

export default {
  setup(props) {
    // 将 `props` 转为一个其中全是 ref 的对象,然后解构
    const { title } = toRefs(props)
    // `title` 是一个追踪着 `props.title` 的 ref
    console.log(title.value)

    // 或者,将 `props` 的单个属性转为一个 ref
    const title = toRef(props, 'title')
  }
}

更多关于Props的使用,请看vue3 Props的用法(父传子)

参数 context(Setup 上下文)

传入 setup 函数的第二个参数是一个 Setup 上下文对象。上下文对象暴露了其他一些在 setup 中可能会用到的值:

export default {
  setup(props, context) {
    // 透传 Attributes(非响应式的对象,等价于 $attrs)
    console.log(context.attrs)

    // 插槽(非响应式的对象,等价于 $slots)
    console.log(context.slots)
    // 通过 slots.default() 来获取默认插槽的内容
    const slotContent = context.slots.default();

    // 触发自定义事件(函数,等价于 $emit)
    console.log(context.emit)
    // 自定义事件 customEvent
    const triggerEvent = () => {
      context.emit('customEvent', 'Some data');
    };

    // 暴露公共属性(函数)
    console.log(context.expose)
  }
}

在函数内部:

  • 通过 context.attrs 访问了父组件传递的非 prop 属性。
    • 包含了父组件传递给当前组件的所有非 prop 属性,即没有被组件明确声明为 props 的属性。
  • 通过 context.slots 访问了插槽内容。
    • 通过 slots 对象,可以获取到组件中定义的各种插槽的具体内容,并在组件的逻辑中进行处理和渲染。
  • 通过 context.emit 触发了自定义事件。
    • 通过调用 emit 函数,可以向父组件发送事件通知,并传递相应的数据。
  • 通过 context.expose 显式地限制该组件暴露出的属性。

Setup 上下文对象是非响应式的,可以安全地解构:

export default {
  setup(props, { attrs, slots, emit, expose }) {
    ...
  }
}

暴露公共属性

expose 函数允许开发者明确指定一个组件实例应该对外暴露哪些属性和方法。
当父组件通过模板引用访问该组件的实例时,仅能访问 expose 函数暴露出的内容:

<template>
  <!-- 组件的模板内容 -->
</template>

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

export default {
  setup() {
    const internalData = ref('Internal data');
    const internalMethod = () => {
      console.log('Internal method called');
    };

    const exposedData = ref('Exposed data');
    const exposedMethod = () => {
      console.log('Exposed method called');
    };

    // 使用 expose 函数限制暴露的属性和方法
    expose({
      exposedData,
      exposedMethod,
    });

    return {
      exposedData,
      exposedMethod,
    };
  },
};
</script>

在这个例子中,组件内部有 internalDatainternalMethodexposedDataexposedMethod方法。
通过 expose 函数,明确地限制了组件只暴露 exposedDataexposedMethod。父组件只能访问 exposedDataexposedMethod,不能访问 internalDatainternalMethod

setup()与选项式API的区别

  • setup 与 选项式API可以在vue3项目中同时存在。
  • 选项式API(如 datamethodscomputed 等)中可以访问到 setup中的属性、方法。
    但在setup中不能访问到选项式API(如 datamethodscomputed 等)。
    • 在选项式 API 中可以访问到 setup 中定义的属性和方法,这是因为 Vue 在处理组件时,会先执行 setup 函数,然后将其返回值与选项式 API 的内容进行合并,最终形成组件的实例。
    • setup 函数中不能直接访问选项式 API(如 datamethodscomputed 等),这是因为 setup 函数在组件实例创建之前执行,此时选项式 API 的内容还未被处理和合并到组件实例中。
  • 如果与选项式API冲突,则setup优先。
    • setup 的执行优先级更高,setup 返回的属性会覆盖选项式 API 中同名的属性。

示例:

<template>
  <div>
    <div>姓名:{{name}}</div>
    <div>年龄:{{age}}</div>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>

    <br />

    <div>选项式API data 里的数据:{{ otherName }}</div>
    <div>选项式API data 里读取setup函数中声明的变量: {{ setupName }}</div>
    <button @click="handleShow">选项式API methods 定义的方法:点击显示消息</button>
  </div>
</template>
<script lang="ts">
export default {
  name: 'Person',
  data() {
    return {
      otherName: '李四',
      setupName: this.name
    }
  },
  methods: {
    handleShow() {
      // 通过 this 访问 setup()中暴露的变量
      console.log(this.name)
    }
  },
  setup() {
  	console.log(this);  // undefined
    // 数据(在setup中直接声明变量,此时的name、age、tel都不是响应式的数据)
    // 注意:直接声明变量,数据不是响应式的。
    let name = '张三'
    let age = 36

    // 方法
    function changeName() {
      console.log('change name')
      name = 'John'  // 这样修改name,页面是不会响应的
      console.log('changed name: ' + name); // changed name: John
      //  成功修改 name ,但是页面没有响应
    }
    function changeAge() {
      age--
      console.log(age);
      // 成功修改 age ,但是页面没有响应
    }
    // 将数据、方法暴露出去,模板中才可以使用
    return { name, age, changeName, changeAge}
  }
}
</script>

在这里插入图片描述

与传统的选项式 API(如 datamethodscomputed 等)相比,组合式 API 更加灵活和可组合。它允许将相关的逻辑封装在函数中,提高了代码的复用性和可读性。同时,组合式 API 也更好地支持 TypeScript,提供了更好的类型推断和代码提示。

setup 语法糖

在 Vue 3 中,<script setup>是一种语法糖,它极大地简化了组合式 API 的使用方式。

<script setup>语法糖,可以把setup独立出去:
在 Vue 组件中,使用<script setup>标签来代替传统的<script>标签。
<script setup>中,不需要使用传统的export default语法来包裹组件的逻辑。可以直接定义响应式数据、计算属性、方法等,并且这些定义会自动暴露给模板使用(不需要return语句来暴露数据、方法)。

把上面例子中的setup函数改为<script setup>语法糖:

<script setup lang="ts">
console.log(this) // undefined
// 数据(在setup中直接声明变量,此时的name、age、tel都不是响应式的数据)
// 注意:直接声明变量,数据不是响应式的。
let name = '张三'
let age = 36

// 方法
function changeName() {
  console.log('change name')
  name = 'John' // 这样修改name,页面是不会响应的
  console.log('changed name: ' + name) // changed name: John
  //  成功修改 name ,但是页面没有响应
}
function changeAge() {
  age--
  console.log(age)
  // 成功修改 age ,但是页面没有响应
}
</script>

注意:用<script setup>改写setup函数后,现在有两个 <script> 标签:

  • 一个用于使用组合式 API 的 <script setup>
  • 一个用于传统的选项式 API 和组件配置:
<script lang="ts">
export default {
  name: 'Person'
}
</script>

这个<script> 标签目前只用来配置组件的名称。

是否可以在<script setup>标签上写组件的名称呢?

插件vite-plugin-vue-setup-extend

vite-plugin-vue-setup-extend是一个用于增强 Vue 3 在 Vite 项目中使用<script setup>语法的插件。

插件vite-plugin-vue-setup-extend 中,有更详细的用法示例。

安装插件:

npm i vite-plugin-vue-setup-extend -D

vite.config.js文件中,引入并使用该插件:

import VueSetupExtend from 'vite-plugin-vue-setup-extend'
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    VueSetupExtend(),
  ]
})

修改<script setup>标签:

<script setup lang="ts" name="Person">
<script>

<script setup>更多用法,请看 vue3 语法糖<script setup>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值