vue3的宏到底是什么东西?

ops/2024/11/22 22:26:15/

前言

vue3开始vue引入了宏,比如definePropsdefineEmits等。我们每天写vue代码时都会使用到这些宏,但是你有没有思考过vue中的宏到底是什么?为什么这些宏不需要手动从vueimport?为什么只能在setup顶层中使用这些宏?

vue 文件如何渲染到浏览器上

要回答上面的问题,我们先来了解一下从一个vue文件到渲染到浏览器这一过程经历了什么?

我们的vue代码一般都是写在后缀名为vue的文件上,显然浏览器是不认识vue文件的,浏览器只认识html、css、jss等文件。所以第一步就是通过webpack或者vite将一个vue文件编译为一个包含render函数的js文件。然后执行render函数生成虚拟DOM,再调用浏览器的DOM API根据虚拟DOM生成真实DOM挂载到浏览器上。

 

 

vue3的宏是什么?

我们先来看看vue官方的解释:

宏是一种特殊的代码,由编译器处理并转换为其他东西。它们实际上是一种更巧妙的字符串替换形式。

宏是在哪个阶段运行?

通过前面我们知道了vue 文件渲染到浏览器上主要经历了两个阶段。

第一阶段是编译时,也就是从一个vue文件经过webpack或者vite编译变成包含render函数的js文件。此时的运行环境是nodejs环境,所以这个阶段可以调用nodejs相关的api,但是没有在浏览器环境内执行,所以不能调用浏览器的API

第二阶段是运行时,此时浏览器会执行js文件中的render函数,然后依次生成虚拟DOM和真实DOM。此时的运行环境是浏览器环境内,所以可以调用浏览器的API,但是在这一阶段中是不能调用nodejs相关的api

而宏就是作用于编译时,也就是从vue文件编译为js文件这一过程。

举个defineProps的例子:在编译时defineProps宏就会被转换为定义props相关的代码,当在浏览器运行时自然也就没有了defineProps宏相关的代码了。所以才说宏是在编译时执行的代码,而不是运行时执行的代码。

一个defineProps宏的例子

我们来看一个实际的例子,下面这个是我们的源代码:

<template><div>content is {{ content }}</div><div>title is {{ title }}</div>
</template><script setup lang="ts">
import {ref} from "vue"
const props = defineProps({content: String,
});
const title = ref("title")
</script>

 在这个例子中我们使用defineProps宏定义了一个类型为String,属性名为contentprops,并且在template中渲染content的内容。

我们接下来再看看编译成js文件后的代码,代码我已经进行过简化:

javascript">import { defineComponent as _defineComponent } from "vue";
import { ref } from "vue";const __sfc__ = _defineComponent({props: {content: String,},setup(__props) {const props = __props;const title = ref("title");const __returned__ = { props, title };return __returned__;},
});import {toDisplayString as _toDisplayString,createElementVNode as _createElementVNode,Fragment as _Fragment,openBlock as _openBlock,createElementBlock as _createElementBlock,
} from "vue";function render(_ctx, _cache, $props, $setup) {return (_openBlock(),_createElementBlock(_Fragment,null,[_createElementVNode("div",null,"content is " + _toDisplayString($props.content),1 /* TEXT */),_createElementVNode("div",null,"title is " + _toDisplayString($setup.title),1 /* TEXT */),],64 /* STABLE_FRAGMENT */));
}
__sfc__.render = render;
export default __sfc__;

我们可以看到编译后的js文件主要由两部分组成,第一部分为执行defineComponent函数生成一个 __sfc__ 对象,第二部分为一个render函数。render函数不是我们这篇文章要讲的,我们主要来看看这个__sfc__对象。

看到defineComponent是不是觉得很眼熟,没错这个就是vue提供的API中的 definecomponent函数。这个函数在运行时没有任何操作,仅用于提供类型推导。这个函数接收的第一个参数就是组件选项对象,返回值就是该组件本身。所以这个__sfc__对象就是我们的vue文件中的script代码经过编译后生成的对象,后面再通过__sfc__.render = renderrender函数赋值到组件对象的render方法上面。

我们这里的组件选项对象经过编译后只有两个了,分别是props属性和setup方法。明显可以发现我们原本在setup里面使用的defineProps宏相关的代码不在了,并且多了一个props属性。没错这个props属性就是我们的defineProps宏生成的。

 

我们再来看一个不在setup顶层调用defineProps的例子: 

javascript"><script setup lang="ts">
import {ref} from "vue"
const title = ref("title")if (title.value) {const props = defineProps({content: String,});
}
</script>

运行这个例子会报错:defineProps is not defined

我们来看看编译后的js代码:

javascript">import { defineComponent as _defineComponent } from "vue";
import { ref } from "vue";const __sfc__ = _defineComponent({setup(__props) {const title = ref("title");if (title.value) {const props = defineProps({content: String,});}const __returned__ = { title };return __returned__;},
});

明显可以看到由于我们没有在setup的顶层调用defineProps宏,在编译时就不会将defineProps宏替换为定义props相关的代码,而是原封不动的输出回来。在运行时执行到这行代码后,由于我们没有任何地方定义了defineProps函数,所以就会报错defineProps is not defined

总结

现在我们能够回答前面提的三个问题了。

  • vue中的宏到底是什么?

    vue3的宏是一种特殊的代码,在编译时会将这些特殊的代码转换为浏览器能够直接运行的指定代码,根据宏的功能不同,转换后的代码也不同。

  • 为什么这些宏不需要手动从vueimport

    因为在编译时已经将这些宏替换为指定的浏览器能够直接运行的代码,在运行时已经不存在这些宏相关的代码,自然不需要从vueimport

  • 为什么只能在setup顶层中使用这些宏?

    因为在编译时只会去处理setup顶层的宏,其他地方的宏会原封不动的输出回来。在运行时由于我们没有在任何地方定义这些宏,当代码执行到宏的时候当然就会报错。

如果想要在vue中使用更多的宏,可以使用 vue macros。这个库是用于在vue中探索更多的宏和语法糖,作者是vue的团队成员 三咲智子 。


http://www.ppmy.cn/ops/135900.html

相关文章

【论文阅读】Adversarial Examples for Handcrafted Features

一篇针对SLAM中图像特征匹配的攻击方法 一、攻击定义 这篇论文的目的是设计一种方法&#xff0c;使得图像在被攻击之后能够在尽可能不影响外观的情况下&#xff0c;干扰匹配的过程。针对这个目标&#xff0c;作者首先指出了对于这篇文章&#xff0c;什么是一个成功的攻击。这…

RedHat Linux服务器Redis三主三从集群部署(普通用户启动)

一、安装redis&#xff1a; 先查看是否安装c的编译器&#xff1b; 执行 sudo yum -y install gcc-c(否则直接安装redis没依赖环境&#xff09; 第一步&#xff1a;上传安装包 第二步&#xff1a;在/mpjava路径下解压sudo tar -zxvf redis-6.2.5.tar.gz 第三步&#xff1a;进入 …

【Android、IOS、Flutter、鸿蒙、ReactNative 】自定义View

Android Java 自定义View 步骤 创建一个新的Java类&#xff0c;继承自View、ViewGroup或其他任何一个视图类。 如果需要&#xff0c;重写构造函数以支持不同的初始化方式。 重写onMeasure方法以提供正确的测量逻辑。 重写onDraw方法以实现绘制逻辑。 根据需要重写其他方法&…

C#之WPF的C1FlexGrid空间的行加载事件和列事件变更处理动态加载的枚举值

列变更&#xff0c;EnumDataItemStackClassTypeList数据源是枚举配置&#xff0c;实时查询到VM缓存的&#xff0c;如果是定义的枚举就不用这个麻烦了&#xff0c;直接在对象里面获取枚举值匹配&#xff0c;即 public string ApplyStatusName { get { retur…

【Oracle篇】SQL性能优化实战案例(从15秒优化到0.08秒)(第七篇,总共七篇)

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌️…

计算机网络 第三章:数据链路层(关于争用期的超详细内容)

数据链路层要干的是&#xff1a;解决在一组网络上&#xff08;一段链路上&#xff09;传输的问题。 &#xff08;在第一章我们讨论过数据是如何经过五层传输的&#xff0c;但在本章我们只考虑数据链路层之间传输数据的问题&#xff0c;所以就先想象成是两个链路层之间在直接传输…

【K8S系列】Kubernetes 中如何调试imagePullSecrets配置详细步骤介绍

调试 imagePullSecrets 配置是确保 Kubernetes 能够成功拉取私有镜像所需的关键步骤。以下是详细的调试步骤和建议。 1. 确认 imagePullSecrets 配置 首先&#xff0c;确保在 Pod 的 YAML 配置中正确引用了 imagePullSecrets。其基本结构如下&#xff1a; apiVersion: v1 kin…

【如何用更少的数据作出更好的决策】-gpt生成

如何用更少的数据作出更好的决策 用更少的数据作出更好的决策是一种能力的体现&#xff0c;需要结合有效的方法、严谨的逻辑以及对问题的深刻理解。以下是一些可以帮助你实现这一目标的策略&#xff1a; 明确目标 在收集和分析数据之前&#xff0c;先明确你的决策目标是什么…