vue3 - script-setup组件实践

1,349 阅读2分钟

写在前面:

vue 3推出了组合式api。当我们在使用组合式api构建组件时,通常会使用如下较为经典的写法

<template>
  <button @click="handler">按钮</button>
  <Components_1 :propsA="a" :propsB="b"/>
  <Components_2 :propsC="c" :propsD="d"/>
</template>

<script>
import { ref } from "vue";
import Components_1 from "./Components_1.vue";
import Components_2 from "./Components_2.vue";
import { handler } from "./utils.js"
export default {
  name: "Test",
  components: {
      Components_1,
      Components_2
  },
  setup() {
      const a = ref()
      const b = ref()
      const c = ref()
      const d = ref()
    return {
        a,
        b,
        c,
        d,
        handler
    };
  },
};
</script>

<style lang="scss" scoped>
...
</style>

现实情况

在较为复杂业务组件中,状态,组件,方法会比例子中的多很多。状态可能会几十个,加上组件方法等,会增加很多无意义的代码行数,给维护审阅代码时带来困惑,也增加了bug出现的概率

主要体现在:

  • 如果使用到了某个外部文件的工具方法,需要将这个方法先引入,再通过return导出才可被模板使用
  • 引入的组件,必须通过components注册,才可以在模板中使用
  • 要显式声明setup函数,并且必须在setup中显式通过return返回方可在模板中使用
  • 有时写了变量又忘记导出,引入了方法又忘记在return暴露,从而导致bug

解决方案

在vue 版本v3.1.4之后,vue3 提出了单文件组件 script-setup解决方案。

改写为setup写法:

<template>
  <button @click="handler">按钮</button>
  <Components_1 :propsA="a" :propsB="b" />
  <Components_2 :propsC="c" :propsD="d"/>
</template>
<script setup>
import { ref } from "vue";
import Components_1 from "./Components_1.vue";
import Components_2 from "./Components_2.vue";
import { handler } from "./utils.js"
const a = ref()
const b = ref()
const c = ref()
const d = ref()
</script>

<style lang="scss" scoped>
...
</style>
  • 模板中不再出现export default 、setup函数声明
  • 不需要再去手动注册组件
  • 不需要手动return出模板中的变量
  • 模板中需要的方法也可以直接使用

模板中的代码量明显减少许多,当组件大时效果更为明显。减少了“由于忘记return”类似问题出现的几率

setup语法如何收集参数、事件

收集props - defineProps

如子组件Child.vue中:

<template>
  <div>{{childProps}}</div>
</template>

<script setup>
// 获取父组件传入的props
const props = defineProps({
  childProps: { type: String, default: 'a' }
});
</script>

父组件中:

<template>
    <Child :childProps="childProps"/>
</template>

<script setup>
import { ref } from "vue"
import Child from "./Child.vue"

const childProps = ref('1')
</script>

这样,子组件就可以接收到父组件的响应式数据了

向父组件触发事件 - defineEmits

如子组件Child.vue中:

<template>
 <button @click="sendMessage"></button>
</template>

<script setup>
// 定义emit
const emit = defineEmits(["acceptMessage"]);

const sendMessage = () => emit('acceptMessage')
</script>

父组件中:

<template>
    <Child ref="child" @acceptMessage="alert(1)"/>
</template>

<script setup>
import Child from "./Child.vue"
</script>

这样,子组件内部触发出来的事件,在父组件中就可以接收到了

暴露出来的属性 - defineExpose

某些情况下,我们需要在父组件中调用子组件方法。(vue2 中通常使用$ref去获取子组件)

如子组件Child.vue中定义一个跳转页面的方法


<script setup>
const goSomePage = () => {
    window.location.href = 'https://a.b.cn'
}
// 暴露给父组件
defineExpose({
  goSomePage
});
</script>

父组件:

<template>
    <button @click="goSomePage">跳转</button>
    <Child ref="child" />
</template>

<script setup>
import { ref } from "vue"
import Child from "./Child.vue"

const child = ref("child");
const { goSomePage } = child.value;

</script>

如此,当在父组件中点击按钮是,将会调用子组件中的方法,跳转至另一个页面了

结合 Typescript

如果工程中使用了 Typescript,需要事先声明defineEmits、defineExpose、defineProps来避免类型系统抛出的错误

如 project.d.ts

declare let defineProps: any;
declare let defineEmits: any;
declare let defineExpose: any;

其他

vue 3 生态一直处于不断健全更新的状态,script-setup 也一样。如果使用,建议锁定版本。

总结

另外关于script-setup的相关内容还有很多,详细内容请参考官方文档。此处仅是本人在实际项目中使用到的相关内容。如有错误,还请斧正