写在前面:
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的相关内容还有很多,详细内容请参考官方文档。此处仅是本人在实际项目中使用到的相关内容。如有错误,还请斧正