vue3.2 <script setup>语法糖以及编译效果

2,719 阅读1分钟

image.png

vue3.2已经发了一段时间了,也用了一段时间,特别setup语法用起来特顺手,有种回不去的感觉,下面就分享我这一段时间的感受以及体验的效果,总结学习,共同进步。

下来说下好处:此处偷个懒,直接复制官网的

  • 更少的样板内容,更简洁的代码。
  • 能够使用纯 Typescript 声明 props 和抛出事件。
  • 更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。
  • 更好的 IDE 类型推断性能 (减少语言服务器从代码中抽离类型的工作)。

我们首先看下setup语法:

1. props

先来看下3.2之前的声明props写法

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

export default defineComponent({
  props: {
    name: {
      type: String,
      default: '',
    },
  },
  setup(props, context) {
    console.log(props, context);
  },
});
</script>

3.2 <script setup>语法

defineProps 和 defineEmits 都是只在 <script setup> 中才能使用的编译器宏
他们不需要导入且会随着 <script setup> 处理过程一同被编译掉。
在 <script setup> 中必须使用 defineProps 和 defineEmits API 来声明 props 和 emits

<script setup>
const props = defineProps({
  name: {
    type: String,
    default: '',
  },
});
console.log(props);

const testlick = () => {
  console.log('test');
};
</script>

这样写是不是简洁很多,使用也更加方便了。我们先看下编译后的文件

image.png

里面的代码会被编译成组件 setup() 函数的内容。这意味着与普通的 <script> 只在组件被首次引入的时候执行一次不同,<script setup> 中的代码会在每次组件实例被创建的时候执行

我们看到最后的编译结果和我们之前的Vue3.2之前语法是一样,他会把我们的声明变量,函数,以及 import 引入的内容都会暴露在最顶层,所以任何在 <script setup> 声明的顶层的绑定都能在模板中直接使用。

如果使用eslint报错的话,可以在eslint.js添加

// eslint.js

module.exports = {
  globals: {
    defineEmits: true,
    defineProps: true,
  }
}

2. emit

在 <script setup> 中必须使用 defineProps 和 defineEmits API 来声明 props 和 emits

我们在使用emit必须声明我们使用的事件名,例如:['change','update:modelValue'],可以多个声明,用都好隔开

我们使用最常用的双向绑定例子,来看下代码以及编译后的代码,看过后你就会更加清晰。

<script setup>
import { computed } from 'vue';

const props = defineProps({
  modelValue: {
    type: String,
    default: '',
  },
});

const emit = defineEmits(['update:modelValue']);
const inputValue = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value),
});

</script>

image.png

3. 组件

<script setup> 中的组件可以直接使用引入标签名,不用在使用components声明

<template>
  <Test />
</template>

<script setup>
import Test from './index.vue';
</script>

<style lang="scss" scoped>

</style>

是不是很方便,不用担心组件没有声明报错了,我们来看下编译的代码

image.png

我们来看下createBlock方法,是创建是一个vnode节点,在组件打开的时候把这个vnode添加到父组件中,关闭的时候删除节点,这样就实现了直接可以使用引入的标签名使用组件。有兴趣的可以去研究下源码

image.png image.png

4. useSlots 和 useAttrs

如果想要获取slots和attrs,可以使用这两个api,这个不是经常使用

<script setup>
import { useSlots, useAttrs } from 'vue'

const slots = useSlots()
const attrs = useAttrs()
</script>

image.png

5. defineExpose

使用 <script setup> 的组件是默认关闭的,也即通过模板 ref 或者 $parent 链获取到的组件的公开实例,不会暴露任何在 <script setup> 中声明的绑定。

vue2我们可以用$ref.获取设置ref组件的方法以及属性,如果使用script setup是获取不到,我们先打印下数据看下效果.

通过getCurrentInstance()获取实例,首先看下没有使用defineExpose之前的打印效果

image.png

我们打印下ref,看下vue3的打印结果

image.png

看下使用defineExpose后的实例 image.png

ref获取的效果

image.png

可以看到如果没有导出, $ref方式是获取不到的其方法或者属性的。

image.png

6. 和普通的<script>一起使用

我们有时候要用的另外的scritp处理其他的逻辑
例如:

<script>
export default {
  name: 'Test',
  inheritAttrs: false,
  customOptions: {},
  ...
}
</script>

image.png

我们看到使用Object.assign合并的方式,这种方式优先取<scritpt setup>的方法和属性。

<script>
const a = 123;
const confirmHandler = ()=> {
  console.log(6666)
}
</script>

<script setup>
import { ref } from 'vue';
const emit = defineEmits(['change', 'confirm']);

const confirmHandler = () => {
  emit('confirm', multipleSelection.value);
};

const search = ref('');
const changeHandle = () => {
  const list = { page: currentPage.value };
  if (search.value) list.search = search.value;
  emit('change', list);
};

defineExpose({
  changeHandle,
  confirmHandler
})
</script>

image.png