Vue3之script-setup全面解析

全局编译器宏

前言 在 script-setup 模式下,新增了 4 个全局编译器宏,他们无需 import 就可以直接使用。
下边都是非常有意思的内容~~ 请接着往下看 ~~~

  • // 项目根目录下的 .eslintrc.js
    module.exports = {
    // 原来的lint规则,补充下面的globals…
    globals: {
    defineProps: ‘readonly’,
    defineEmits: ‘readonly’,
    defineExpose: ‘readonly’,
    withDefaults: ‘readonly’,
    },
    }

1. 变量无需进行 return
标准组件模式下,setup 里定义的变量,需要 return 后,在 template 部分才可以正确拿到:
eg:

<template>
  <p>{{ msg }}</p>
</template>
<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  setup () {
    const msg: string = 'Hello World!';
    
    // 要给 template 用的数据需要 return 出来才可以
    return {
      msg
    }
  }
})
</script>

在 script-setup 模式下,你定义了就可以直接使用。

<template>
  <p>{{ msg }}</p>
</template>

<script setup lang="ts">
const msg: string = 'Hello World!';
</script>

2. 子组件无需手动注册
子组件的挂载,在标准组件里的写法是需要 import 后再放到 components 里才能够启用:
eg:

<template>
  <Child />
</template>

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

// 导入子组件
import Child from '@cp/Child.vue'

export default defineComponent({
  // 需要启用子组件作为模板
  components: {
    Child
  },

  // 组件里的业务代码
  setup () {
    // ...
  }
})
</script>

script-setup 模式下,只需要导入组件即可,编译器会自动识别并启用。

<!-- 使用 script-setup 格式 -->
<template>
  <Child />
</template>

<script setup lang="ts">
import Child from '@cp/Child.vue'
</script>

3. props 的接收方式变化

  • 由于整个 script 都变成了一个大的 setup function ,没有了组件选项,也没有了 setup 入参,所以没办法和标准写法一样去接收 props 了。这里需要使用一个全新的 API :defineProps 。defineProps 是一个方法,内部返回一个对象,也就是挂载到这个组件上的所有 props ,它和普通的 props 用法一样,如果不指定为 prop, 则传下来的属性会被放到 attrs 那边去。

defineProps 的基础用法:

const props = defineProps([
  'name',
  'userInfo',
  'tags'
])

console.log(props.name);

有两种方式来处理类型定义props:

1.通过构造函数检查 prop
defineProps({
  name: String,
  userInfo: Object,
  tags: Array
});
2.使用类型注解检查 prop (使用 TypeScript 的类型注解)
defineProps<{ name: string }>();

比如字符串是 string,而不是 String

如果有多个 prop ,就跟写 interface 一样:
defineProps<{
  name: string;
  phoneNumber: number;
  userInfo: object;
  tags: string[];
}>();
如果你想对某个数据设置为可选,也是遵循 TS 规范,通过英文问号 ? 来允许可选:
// name 是可选
defineProps<{
  name?: string;
  tags: string[];
}>();
如果你想设置可选参数的默认值,需要借助 withDefaults(props, defaultValues) API。

eg:

withDefaults(defineProps<{
  size?: number
  labels?: string[]
}>(), {
  size: 3,
  labels: () => ['default label']
})

4. emits 的接收方式变化

和 props 一样,emits 的接收也是需要使用一个全新的 API 来操作,这个 API 就是 defineEmits
// 获取 emit
const emit = defineEmits(['chang-name']);

// 调用 emit
emit('chang-name', 'Tom');

5 attrs 的接收方式变化

attrsprops 很相似,也是基于父子通信的数据,如果父组件绑定下来的数据没有被指定为 props ,那么就会被挂到 attrs 这边来。
在标准组件里, attrs 的数据是通过 setup 的第二个入参 context 里的 attrs API 获取的

eg:

// 标准组件的写法
export default defineComponent({
setup (props, { attrs }) {
  // attrs 是个对象,每个 Attribute 都是它的 key
  console.log(attrs.class);

  // 如果传下来的 Attribute 带有短横线,需要通过这种方式获取
  console.log(attrs['data-hash']);
}
})
但和 props 一样,由于没有了 context 参数,需要使用一个新的 API 来拿到 attrs 数据。这个 API 就是 useAttrs

6. useAttrs 的基础用法

顾名思义, useAttrs 可以是用来获取 attrs 数据的,它的用法非常简单:

eg:

// 导入 useAttrs 组件
import { useAttrs } from 'vue'

// 获取 attrs
const attrs = useAttrs()

// attrs是个对象,和 props 一样,需要通过 key 来得到对应的单个 attr
console.log(attrs.msg);

7. slots 的接收方式变化

slots 是 Vue 组件的插槽数据,也是在父子通信里的一个重要成员。
<template>
  <div>
    <!-- 插槽数据 -->
    <slot />
    <!-- 插槽数据 -->
  </div>
</template>
新版本的 Vue 也提供了一个全新的 useSlots API 来帮助 script-setup 用户获取插槽

eg:

先来看看父组件,父组件先为子组件传入插槽数据,支持 “默认插槽” 和 “命名插槽” :
<template>
  <!-- 子组件 -->
  <ChildTSX>
    <!-- 默认插槽 -->
    <p>I am a default slot from TSX.</p>
    <!-- 默认插槽 -->

    <!-- 命名插槽 -->
    <template #msg>
      <p>I am a msg slot from TSX.</p>
    </template>
    <!-- 命名插槽 -->
  </ChildTSX>
  <!-- 子组件 -->
</template>

<script setup lang="ts">
import ChildTSX from '@cp/context/Child.tsx'
</script>
在使用 JSX / TSX 编写的子组件里,就可以通过 useSlots 来获取父组件传进来的 slots 数据进行渲染:
// 注意:这是一个 .tsx 文件
import { defineComponent, useSlots } from 'vue'

const ChildTSX = defineComponent({
  setup() {
    // 获取插槽数据
    const slots = useSlots()

    // 渲染组件
    return () => (
      <div>
        {/* 渲染默认插槽 */}
        <p>{ slots.default ? slots.default() : '' }</p>

        {/* 渲染命名插槽 */}
        <p>{ slots.msg ? slots.msg() : '' }</p>
      </div>
    )
  },
})

export default ChildTSX

8. ref 的通信方式变化
在标准组件写法里,子组件的数据都是默认隐式暴露给父组件的,也就是父组件可以通过 childComponent.value.foo 这样的方式直接操作子组件的数据(参见:DOM 元素与子组件 - 响应式 API 之 ref)。

但在 script-setup 模式下,所有数据只是默认隐式 return 给 template 使用,不会暴露到组件外,所以父组件是无法直接通过挂载 ref 变量获取子组件的数据。

script-setup 模式下,如果要调用子组件的数据,需要先在子组件显示的暴露出来,才能够正确的拿到,这个操作,就是由 defineExpose 来完成。

defineExpose 的基础用法:
eg:

<script setup lang="ts">
// 定义一个想提供给父组件拿到的数据
const msg: string = 'Hello World!';

// 显示暴露的数据,才可以在父组件拿到
defineExpose({
  msg
});
</script>

9. 顶级 await 的支持
在 script-setup 模式下,不必再配合 async 就可以直接使用 await 了,这种情况下,组件的 setup 会自动变成 async setup 。

<script setup lang="ts">
const post = await fetch(`/api/post/1`).then((r) => r.json())
</script>
它转换成标准组件的写法就是:
<script lang="ts">
import { defineComponent, withAsyncContext } from 'vue'

export default defineComponent({
  async setup() {
    const post = await withAsyncContext(
      fetch(`/api/post/1`).then((r) => r.json())
    )

    return {
      post
    }
  }
})
</script>
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值