当前位置:   article > 正文

vue3.2 setup语法糖,你值得拥有

vue3.2 setup

0、前言

vue3中删除了vue2中的data函数,因此,vue3.0要在template中使用某些变量就必须在最后return出来,多次声明变量,不太方便,也不太友好。而在vue3.2版本之后,我们只需在script标签上加上setup属性,不需要再写return就可以直接在template中使用,写起代码就很流畅。哎,这就很棒!

vue3.0的写法示例代码。

<template>
  <div>
    <p>{{ `${state.name}发布于${state.age}${msg}` }}</p>

    <button @click="onClick">点击</button>

    <ChildComp />
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, reactive } from "vue"
import ChildComp from './ChildComp.vue'

export default defineComponent({
  // 注册组件
  components: {
    ChildComp
  },
  props: {
    show: {
      type: Boolean,
      default: false
    }
  },
  emits: ['on-confirm'],

  setup(props, { emit }) {
    console.log(props.show) // false

    const msg = ref('');
    msg.value = '哎,这就很不棒!';

    const state = reactive({
      name: 'vue3.0',
      age: '2020年9月18日'
    })
    // 点击事件
    const onClick = () => {
      emit('on-confirm')
    }
    // 必须return出来
    return {
      msg,
      state,
      onClick
    }
  }
})
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

刚开始使用 script setup 语法糖的时候,编辑器会提示这是一个实验属性,要使用的话,需要固定 vue 版本。在2021年 6 月底,该提案被正式定稿,在 vue3.1.3 的版本上,继续使用但仍会有实验性提案的提示,在 vue3.2 中,才会去除提示并移除一些废弃的 API。

script setup 是 vue3.2 的新语法糖,并不是新增的功能模块,主要好处有:(1)更少的模板内容,代码简洁,不需要写return;(2)能够使用ts更好的声明props,以及抛出事件;(3)更好的运行时性能。

1、变量、方法不需要renturn

变量、方法以及import导入的内容不用在 return 暴露出来,不需要写 export default 和 setup 函数,只需在 script 标签加上 setup 属性,直接声明变量、方法以及import导入的内容使用即可,使模板代码更加简洁。

<template>
  <div>
    <!-- 使用变量 -->
    <p>{{ `${state.name}发布于${state.age}${msg}` }}</p>
    <!-- import导入 -->
    <ul>
      <li v-for="item in subjectList" :key="item.value">
        {{item.label}}
      </li>
    </ul>
    <!-- 使用方法 -->
    <button @click="onClick">点击</button>
  </div>
</template>

<!-- 在script标签上添加setup属性 -->
<script setup lang="ts">
import { ref, reactive } from "vue"
import { subjectList } from './utils.js'

const msg = ref('');
msg.value = '哎,这就很棒!';

const state = reactive({
  name: 'vue3',
  age: '2020年9月18日'
})

// 点击事件
const onClick = ():void => {
  console.log('点击了')
}
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

2、引入组件自动注册

在 script setup 语法糖中引入组件,组件不需要在 components 中注册了,引入的组件会自动注册,而且组件无法指定 name属性,主要以文件的名字为主,省略name属性

<template>
  <div>
    <Child />
  </div>
</template>

<!-- 在script标签上添加setup属性 -->
<script setup lang="ts">
import Child from './Child.vue'
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3、defineProps和defineEmits

在script setup中必须使用 defineProps 和 defineEmits API 来声明 props 和 emits ,它们具备完整的类型推断并且在 script setup 中不需要导入是直接可用的。传入到 defineProps 和 defineEmits 的选项会从 setup 中提升到模块的范围,因此,传入的选项不能引用在 setup 范围中声明的局部变量,这样做会引起编译错误。

(1)defineProps

//父组件
<template>
  <div>
    <Child :name="name" />
  </div>
</template>

<!-- 在script标签上添加setup属性 -->
<script setup lang="ts">
import { ref } from 'vue';
import Child from './Child.vue';

const name = ref('张三')
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
//子组件
<template>
  <div>
    <p>{{ `${props.name}在学习JavaScript` }}</p>
  </div>
</template>

<script setup lang="ts">
// defineProps不需要从vue中导入
const props = defineProps({
  name: {
    type: String,
    default: '张三'
  }
})
// 或者
const props = defineProps(['name'])
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

(2)defineEmits

// 父组件
<template>
  <div>
    <Child @on-confirm="onConfirm" />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import Child from './Child.vue';

const show = ref(false)
// 点击确定关闭弹框等操作
const onConfirm = (val: boolean) => {
  show.value = val
} 
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
// 子组件
<template>
  <button type="button" @click="handleConfirm">确定</button>
</template>

<script setup lang="ts">
const emit = defineEmits(['on-confirm'])

const handleConfirm = () => {
  // 此处也可以传入参数
  emit('on-confirm', false)
}
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

4、defineExpose

defineExpose可以主动暴露出组件的属性和方法。

// 子组件示例代码
// 子组件
<template>
  <div v-if="show">
    <p>{{ count }}</p>
  </div>
</template>

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

const count = ref(0)
const show = ref(false)

const onShow = () => {
  show.value = true
}
// defineExpose暴露出count属性和onShow方法
defineExpose({
  count,
  onShow
})
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
// 父组件示例代码
// 父组件
<template>
  <div>
    <button type="button" @click="onClick">父组件点击</button>
    <Child ref="childRef" />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import Child from './Child.vue';
// Child组件的ref
const childRef = ref(null)

// 在父组件操作子组件的暴露出的属性或方法
const onClick = () => {
  childRef.value.count += 1;
  childRef.value.onShow();
}
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

5、useSlots和useAttrs

在 script setup 使用 slots 和 attrs 的情况应该是很比较少见的,大部分人是(SFC)模式开发,在<template/>通过<slot/>标签就可以渲染插槽,可以在模板中通过 $slots 和 $attrs 来访问它们。主要在JSX /TSX使用比较多。

(1)useSlots

slots可以获取父组件中插槽传递的虚拟Dom对象。

// 父组件
<template>
  <Child>
    <span>默认插槽</span>
    <template v-slot:footer>
      <div>具名插槽footer</div>
    </template>
  </Child>
</template>

<script setup>
import Child from './Child.vue'
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
// 子组件
<template>
  <div>
    <!-- 在模板中使用插槽 -->
    <slot></slot>
    <slot name="footer"></slot>
  </div>
</template>

<script setup lang="ts">
import { useSlots } from 'vue'
 
const slots = useSlots()
// 访问插槽默认插槽default、具名插槽footer
console.log(slots.default)
console.log(slots.footer)
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

(2)useAttrs

attrs用来获取父组件中非props的传递到子组件的属性,包括class和style属性。

// 父组件
<template>
  <Child class="child-class" title="子组件title" />
</template>

<script setup>
import Child from './Child.vue'
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
// 子组件
<template>
  <!-- 在模板中使用 $attrs 访问属性 -->
  <div>{{ $attrs.title }}</div>
</template>

<script setup lang="ts">
import { useAttrs } from 'vue'
 
const attrs = useAttrs()
// 使用
console.log(attrs.class)  // child-class
console.log(attrs.title)  // 子组件title
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在jsx/tsx中使用

<script lang="tsx">
import { defineComponent, ref, useSlots } from 'vue';
export default defineComponent({
  setup(){
    const slots = useSlots();
    const str = ref<string>('tsx的使用');

    return () => (
      <>
        <div class='async'>{str.value}</div>
        <div>{ slots.default ? slots.default() : 'foo' }</div>
        <div>{ slots.bar?.() }</div>
      </>
    );
  }
})
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

6、顶层await

<script setup> 中可以使用顶层 await。结果代码会被编译成 async setup(),await 的表达式会自动编译成在 await 之后保留当前组件实例上下文的格式。

<script setup lang="ts">
import { getUserInfo } from '@/api/system'

const userInfo = await getUserInfo();
console.log(userInfo)
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
// api/system
export const getUserInfo = async () => {
  const res: any = await request.get(`/api/user/info`)
  return res
}
  • 1
  • 2
  • 3
  • 4
  • 5

7、与普通的

<script setup> 可以和普通的 <script> 一起使用。普通的 <script> 在有这些需要的情况下或许会被使用到:

  • 无法在 <script setup> 声明的选项,例如 inheritAttrs 或通过插件启用的自定义的选项。
  • 声明命名导出。
  • 运行副作用或者创建只需要执行一次的对象
<script>
// 普通 <script>, 在模块范围下执行(只执行一次)
runSideEffectOnce()

// 声明额外的选项
export default {
  inheritAttrs: false,
  customOptions: {}
}
</script>

<script setup>
// 在 setup() 作用域中执行 (对每个实例皆如此)
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

8、总结

script setup语法糖确实很香!模板的内容更少,代码更简介,因此很有学习的必要,小伙伴们赶紧行动学起来吧。这里写了几个常用的语法糖用法,其他的一些用法可以自己学习,文章有写的不当的地方,欢迎指正修改。如果感觉文章实用对你有帮助,欢迎点赞收藏和关注,你的点赞关注就是我动力,大家一起学习进步。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/118968
推荐阅读
相关标签
  

闽ICP备14008679号