看完就知道setup() 与 <script setup> 的区别了

118 阅读2分钟

setup(props,contenxt)

setup() 钩子是在组件中使用组合式 API 的入口,官方解释只在以下情况下使用:

  1. 需要在非单文件组件中使用组合式 API 时;
  2. 需要在基于选项式 API 的组件中集成基于组合式 API 的代码时;

其他情况下,都应优先使用 <script setup> 语法;

基本语法

<script>
import { ref } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const foo = function(){...}
    // 返回值会暴露给模板和其他的选项式 API 钩子
    return {
      count,
      foo
    }
  },

}
</script>

<template>
  <button @click="count++">{{ count }}</button>
</template>

参数props

第一个参数 props 是一个对象接收父组件传递过来的数据,并且 props 是响应式的,在传入新的 props 时同步更新。 需要注意一点是在使用前必须接收,否则值为undefined;

export default {
  props: {
    title: String
  },
  setup(props) {
    console.log(props.title)
  }
}

如果通过解构 props 对象,则解构出来的变量将会丢失响应性。在非解构情况下通过 props.xxx 的形式来使用接收的 props;

如果需要解构 props 对象,或者需要将某个 prop 传到一个外部函数中并保持响应性,那么可以使用 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')
  }
}

toReftoRefs就是为了方便把响应式数据 ref、reactive 展开丢出去,方便在模版中应用;

参数contenxt

第二个参数是一个 Setup 上下文对象。上下文对象暴露了其他一些在 setup 中可能会用到的值attrs, slots, emit, expose...,该上下文对象是非响应式的,可以直接解构;

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

    // 插槽(非响应式的对象,等价于 $slots)
    console.log(context.slots)

    // 触发事件(函数,等价于 $emit)
    console.log(context.emit)

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

<script setup>

<script setup> 其实就是 组合式 API 的语法糖。相比于普通的 <script> 语法,它具有更多优势:

  • 更少的样板内容,更简洁的代码。
  • 能够使用纯 TypeScript 声明 props 和自定义事件。
  • 更好的运行时性能 (其模板会被编译成同一作用域内的渲染函数,避免了渲染上下文代理对象)。
  • 更好的 IDE 类型推导性能 (减少了语言服务器从代码中抽取类型的工作)。

基本语法

要启用该语法,需要在 <script> 代码块上添加 setup attribute:

// 官网例子
<script setup>
console.log('hello script setup')
</script>

上面的代码在编译时会转换为 setup() API的语法内容,代码会在每次组件实例被创建的时候执行

顶层的绑定会被暴露给模板

其实也就是变量、方法都像vue2中的data()函数中的数据,<template>可以直接使用,不用注册

<script setup>
  const msg = 'Hello!' //变量
  import { capitalize } from './helpers' //第三方库     
  import son from './son.vue' //子组件
  function log() {console.log(msg)} //函数
</script>
<template>
  <button @click="log">{{ msg }}</button>
  <div>{{ capitalize('hello') }}</div>
    <son></son>  
<template>

defineProps() 和 defineEmits()

setup()函数相比,<script setup>只能通过defineProps 和 defineEmits方法来使接收传递数据。他们不需要导入,且会随着 <script setup> 的处理过程一同被编译掉;

defineProps() 使用语法

script setup.png

defineEmits() 使用语法

//父组件
<template>
   <props-value @sEmit="sEmit"></props-value>
</template>
<script setup>
   import propsValue from "./components/pages/propsValue.vue"
   const sEmit = function(vals){
       console.log(vals)
   }
</script>
// 子组件
<template>
    <button @click="send">子组件发送"hello word"</button>
</template>
<script setup>
    const emits = defineEmits(["sEmit"])
    const send = function(){
        emits("sEmit","hello word")
    }
</script>

defineExpose

<script setup> 对外暴露属性需要使用defineExpose(), 不需要导入 直接使用

//父组件
<template>
    <props-value ref="children"></props-value>
</template>
<script setup>
    import propsValue from "./components/pages/propsValue.vue"

    let children = ref(null)
    console.log(children)
</script>

在父组件中使用ref绑定子组件暴露的数据

//子组件
<template>
</template>

<script setup>
    import {ref,reactive} from "vue"
    const a = ref(100)
    const b = reactive("name")
    defineExpose({
        a,
        b
    })
</script>

暴露数据.png

useSlots() 和 useAttrs()

slot.png

顶层 await

<script setup> 中可以使用顶层 await。结果代码会被编译成 async setup()

<script setup>
const post = await fetch(`/api/post/1`).then((r) => r.json())
</script>

另外,await 的表达式会自动编译成在 await 之后保留当前组件实例上下文的格式。