Vue3,setup()函数与<script setup>到底有什么本质区别❓

写在开头

作为一名 Vue 的骨灰级玩家(这里特别感谢一下尤大大祖师爷赏饭吃😋),自从 Vue3 横空出世,其革命性的 Composition API 就给我们带来了前所未有的开发体验。

小编使用 Vue3 也有挺长一段时光了,然而,在 Vue3 的应用中,俺有时候发现团队项目中会发现存在 setup() 函数与 script setup 语法混合使用的情况;这个单文件(SFC)用一个形式,另一个单文件又换一种形式😬。初看之下,它们似乎只是在语法层面上有所差异,但并不会影响具体的功能逻辑。

而,真是这样吗?相信在大多数人的印象里 script setup 仅是一个更加便捷的写法,并没有其他特别作用。但事实上,它们之间的区别远不止于此。

下面,小编将分享两者更加细微的一些区别,咱从实战与源码的角度,通过两个功能相同的组件来展示它们的不同表现情况,Go!

课前准备

先来做点准备,初始化一个 Vue3 项目,这过程...(此处省略xxx),再搞两个功能相同的组件耍耍。😁

新建 FunctionSetup.vue 文件:

javascript 复制代码
<template>
  <h1>{{ message }}</h1>
  <h2>count:{{ count }}</h2>
  <button @click="handleClick">点击</button>
</template>

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

const message = ref('我是script setup形式');
const count = ref(0);
function handleClick() {
  count.value++;
}
</script>

新建 FunctionSetup.vue 文件:

javascript 复制代码
<template>
  <h1>{{ message }}</h1>
  <h2>count:{{ count }}</h2>
  <button @click="handleClick">点击</button>
</template>

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

export default {
  setup() {
    const message = ref('我是setup()形式');
    const count = ref(0);
    function handleClick() {
      count.value++;
    }

    return {
      message, count, handleClick
    };
  }
};
</script>

发现问题

App.vue 文件引入使用:

javascript 复制代码
<template>
  <FunctionSetup ref="functionSetup" />
  <ScriptSetup ref="scriptSetup" />
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import FunctionSetup from './components/FunctionSetup.vue';
import ScriptSetup from './components/ScriptSetup.vue';

const functionSetup = ref(null);
const scriptSetup = ref(null);

onMounted(() => {
  console.log('functionSetup', functionSetup.value)
  console.log('scriptSetup', scriptSetup.value)
})
</script>

由于两个组件功能是一样的,所以在页面上表现也是相同的,这没什么好说的;而语法上,虽然有差异,但这还不是我们观察的重点,那我们就只能来看看它们俩的实例上有啥区别了。

通过 ref 咱们获取到两个组件的实例,并在 onMounted 钩子中将它们打印了出来,经过小编的一阵翻找,确实有点差异存在, 如下:

可以看到通过 setup() 函数编写的组件,组件实例会自动暴露组件的公共方法和属性,而通过 script setup 形式编写的组件却是不会自动暴露。😯

在以前 Vue2 的项目中,你可能有写过如下的代码:

javascript 复制代码
<template>
  <div>
    <button @click="add">添加用户</button>
    <!-- ... -->
    <add-user-dialog ref="addUserDialog" />
  </div>
</template>

<script>
export default {
    methods: {
      add() {
        this.$refs.addUserDialog.open();
      }
    }
}
</script>

以上代码在 Vue2 中是一种较为常用的方式,使用起来非常简单方便,即能够通过组件实例直接调用组件内部的方法。在此,咱们暂且不考量其好坏与否,而来思考一下将其迁移到 Vue3 中是否还能正常使用。

以调用上方两个组件为示例:

js 复制代码
<template>
  <FunctionSetup ref="functionSetup" />
  <ScriptSetup ref="scriptSetup" />
  <button @click="clickHandle">测试组件的调用</button>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import FunctionSetup from './components/FunctionSetup.vue';
import ScriptSetup from './components/ScriptSetup.vue';

const functionSetup = ref(null);
const scriptSetup = ref(null);

onMounted(() => {
  console.log('functionSetup', functionSetup.value)
  console.log('scriptSetup', scriptSetup.value)
})

function clickHandle() {
  // 先不管TS的问题
  (functionSetup.value as any).handleClick();
  (scriptSetup.value as any).handleClick(); // ❌
}
</script>

你会发现通过 setup() 函数编写的组件,父组件是能正常通过组件实例调用其他内部方法的,而以 script setup 形式编写的组件却无法被调用,这个结局也是意料之中的事。

从源码角度看问题

至此,咱们得到了一个 "结论",只要记住这个结论就行了吧,So easy。😊

而且,既然 script setup 是如此设计的,那必然也存在解决办法。Em......当然,这点我们下面再讨论。

这里我们再回过头来说,难道你就不好奇,Vue3 后来出现的 script setup 是怎样做到这一点的吗?它是如何做到不把组件内部的东西暴露出去的呢❓

好吧,反正小编是挺好奇的(不然怎么继续讲下去😋假装我很好奇就是),接下来我们就从源码的角度来探究这个问题。

给大家推荐一个插件: vite-plugin-inspect

它能帮我们直接看穿 Vue 在背后到底做了些什么事情,这可真是一个神器啊,五星推荐必备。(⭐⭐⭐⭐⭐)

安装插件:

javascript 复制代码
npm i -D vite-plugin-inspect

vite.config.ts 配置文件中引入插件:

javascript 复制代码
// ...
import Inspect from 'vite-plugin-inspect';

export default {
  plugins: [
    // ...
    Inspect()
  ],
}

重启一下服务,然后访问 http://localhost:5173/__inspect/#/ ,你会看到如下页面:

咱们可以点击想查看文件,它会给我们展示 Vue 文件编译后的情况。

如以 setup() 函数编写的组件情况:

能看到 template 模版直接被编译成一个 render 函数了,当然,这不是我们关注的重点,略过略过。来看 script 部分,大体还是与我们原来写的逻辑是一致的。

再来瞧瞧,以 script setup 形式编写的组件情况:

template 模块部分还是一样,主要是 script 部分,可以看到最终还是套上了 setup() 函数,所以,咱们经常说 script setup 就是一个语法糖,这下就有了最有力的铁证。😀

然后,看看蓝色的框框,这是 script setup 形式与 setup() 函数的主要区别了❗

__isScriptSetup

  • 在 Vue3 中,__isScriptSetup 是一个内部标记,主要用于识别组件是否是通过 <script setup> 语法来定义的。这个标记对于 Vue 的编译器和运行时来说非常重要。
  • 当 Vue 编译器处理组件时,它会根据这个标记来应用特殊的编译规则。例如,对于使用 <script setup> 的组件,变量和函数的暴露方式与传统的 setup 函数不同。在 <script setup> 中,定义的变量和函数会自动在模板中可用,而不需要像传统 setup 函数那样显式地返回一个对象来暴露它们。 __isScriptSetup 标记帮助 Vue 识别这种特殊的组件定义方式,从而正确地处理组件的变量和函数的暴露,以及其他相关的编译步骤。

__expose

  • __expose 在 Vue3 中用于控制组件内部内容的暴露。在 <script setup> 组件中,可以使用defineExpose 来指定要暴露给外部的属性和方法。虽然 __isScriptSetup__expose 有不同的功能,但它们在组件的暴露机制方面是相关的。
  • __isScriptSetup 标记为真(即组件是 <script setup> 组件)时,__expose 的处理方式会受到影响。具体来说, <script setup> 组件默认情况下会自动暴露在 <script setup> 内部定义的响应式数据和函数,而 defineExpose 可以用于更精细地控制这种暴露。__isScriptSetup 标记作为一个前提条件,使得 Vue 能够正确地识别这种特殊的暴露机制,并根据__expose(通过 defineExpose 来控制)来确定最终暴露给外部的内容。

Vue3 相关源码位置:传送门

解决问题

上面这段内容或许会相对复杂一些,因为它涉及到 Vue 的整体源码。然而,即便咱们不去深究源码部分,我们也能够较为直观地感受到两者之间的本质差异。相信呢,这下你对它们已经有了一个更为清晰的认识了。😀

然后,咱们再来说说如何解决 script setup 形式编写组件带来的问题。

上面小编提到可以通过 defineExpose 这个"宏"来解决,它允许开发者更精细地控制组件的API,只暴露需要让父组件访问的属性和方法,隐藏内部实现细节,从而增强了组件的封装性。

ScriptSetup.vue 文件:

javascript 复制代码
<template>
  <h1>{{ message }}</h1>
  <h2>count:{{ count }}</h2>
  <button @click="handleClick">点击</button>
</template>

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

const message = ref('我是script setup形式');
const count = ref(0);
function handleClick() {
  count.value++;
}

// 主动暴露给外部调用
defineExpose({
  message,
  count,
  handleClick
})
</script>

如此之后,咱们在 App.vue 中通过组件实例调用组件内部方法就不会报错了。

组件实例身上也能看到主动暴露出来的东西:


至此,本篇文章就写完啦,撒花撒花。

相关推荐
Martin -Tang16 分钟前
vite和webpack的区别
前端·webpack·node.js·vite
迷途小码农零零发17 分钟前
解锁微前端的优秀库
前端
王解1 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
老码沉思录1 小时前
写给初学者的React Native 全栈开发实战班
javascript·react native·react.js
我不当帕鲁谁当帕鲁1 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂1 小时前
工程化实战内功修炼测试题
前端·javascript
放逐者-保持本心,方可放逐2 小时前
微信小程序=》基础=》常见问题=》性能总结
前端·微信小程序·小程序·前端框架
毋若成4 小时前
前端三大组件之CSS,三大选择器,游戏网页仿写
前端·css
红中马喽4 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习
Black蜡笔小新5 小时前
网页直播/点播播放器EasyPlayer.js播放器OffscreenCanvas这个特性是否需要特殊的环境和硬件支持
前端·javascript·html