这就是一个语法
<script setup>
这是vue的一个语法,大家都这样用。写在里面的变量/函数,都相当于包在setup函数中一样。下面是一个简单的对比
setup函数
<script> export default { setup() { const count = ref(0) const msg = ref('setupScript') const increase = () => { count.value++ console.log(count.value) } return { count, msg, increase } } } </script>
setup 语法糖(当然如果需要子组件的属性/变量,这里需要defineExpose 做导出)
<script setup> const count = ref(0) const msg = ref('setupScript') const increase = () => { count.value++ } </script>
通过Inspect 来观察 script setup和 setup 函数的区别(vite-plugin-inspect:查看编译的结果)
安装 vite-plugin-inspect 插件 https://www.npmjs.com/package/vite-plugin-inspect
setup 函数
template 模版
经过编译,成了render 函数
setup 函数
经过编译,什么也没有改变
script setup
template 模版
经过编译,成了render 函数
script setup
的形式,编译成了 setup函数,去执行,最终通过 expose 导出
,所以<script setup>
就是一个语法糖,本质还是 setup 函数,只不过里面自动做了一个expose
expose 是什么(摘自vue 官方文档)
用于声明当组件实例被父组件通过模板引用访问时暴露的公共属性。
默认情况下 setup 函数,会将组件实例向父组件暴露所有的实例属性,这可能不是我们希望看到的
,而expose 就是限定子组件只能向外暴露哪些属性/方法
组件具体实例观察
<template> <SetupFun ref="setupFunRef" /> <SetupScript ref="setupScriptRef" /> </template> <script setup> import SetupFun from './components/setupFun.vue' import SetupScript from './components/setupScript.vue' const setupFunRef = ref(null) const setupScriptRef = ref(null) onMounted(() => { console.log('%c [ setup 函数 ]-15', 'font-size:16px; background:pink; color:#bf2c9f;', setupFunRef.value) console.log('%c [setup 语法糖 ]-23', 'font-size:16px; background:pink; color:#bf2c9f;', setupScriptRef.value) console.log('%c [setup 语法糖 ]-23', 'font-size:16px; background:pink; color:#bf2c9f;', setupScriptRef.value.$el) }) </script>
通过 setup 函数
,会将组件的属性/实例, 相关隐藏属性,全部暴露出去
通过 script setup
,什么都没有暴露出去
setup 函数不香吗,为什么还要 script setup ?
setup 函数,会将 组件实例属性/API 全部暴露给父组件
。好是好,使用起来变得超级方便,但是这也存在一个潜在的巨大问题,父子组件完全没有边界,$root,$parent,$el 随便用,组件很可能拥有一些应保持私有的内部状态或方法,以避免紧耦合, 也会导致单项数据流被打破
,时间久了,容易造成数据混乱,变成一堆石山。这里为了方便没有举实例属性,举了一个方法来说明如何破坏单项数据流
比如:
onMounted(() => { // 在父组件调用子组件的increase, //而子组件的increase 里写了修改 count的逻辑 console.log('%c [ setup 函数 ]-15', setupFunRef.value.increase()) console.log('%c [ setup 函数 ]-16', setupFunRef.value.count) // count由 0 -》1 在 }
可以指定特定暴露属性/方法吗?->expose
<script> export default { setup(props, { expose }) { const count = ref(0) const msg = ref('setupScript') const increase = () => { count.value++ console.log(count.value) } // 通过 expost 指定暴露当法,同时不会向外暴露子组件实例上的所有实例属性 expose({ count, msg, increase }) } } </script>
script setup 如何暴露 方法/属性
vue提供来一个全局的宏来做属性/方法的暴露。在编译后,也是使用 expose 做属性/方法暴露
<script setup> const msg = ref('setupScript') const count = ref(0) const increase = () => { count.value++ } defineExpose({ msg, count, increase }) </script>
但是如果不使用defineExpose做暴露 ,就不会暴露出任何属性方法, 就像使用 setup 不用return ,就不会暴露出任何属性方法,就像西方不能没有耶路撒冷 !!!
全局的宏 defineExpose 为什么不需要导入,就能直接使用?
通过插件,可以看到,script setup 经过编译 会变成setup 函数
,而 defineExpose 经过编译
会变成expose
。所以 defineExpose 并不参与运行, 只参与编译
。做一个对应关系
,所以不需要引入
编译时? 运行时 傻傻分不清 ?
我们写的template, script setup 不会直接运行
,而是经过一层编译
。将template 模版编译成render 函数,将 script setup 编译成 setup 函数,将 defineExpose
编译成 expose
,或者其他的宏函数defineModel
编译成 props
,defineEmits
编译成 emits
等等
这些宏函数
,只在编译时起作用,和相关函数做一个对应关系
。并不会在运行时执行这些宏函数
,所以不需要引入这些宏函数
至此,我们已经明白了script setup 到底干了什么
总结
script setup 就是一个语法糖,经过 编译,最终还是 setup 函数。
在 script setup 如果想获取子组件的属性/方法,需要通过 defineExpose 暴露
defineExpose 最终也会编译成 expose, defineExpose不会参与运行时,所以不用做导入。