vue3的setup的使用和原理解析

news2025/7/15 11:27:56

1.前言

最近在做vue3相关的项目,用到了组合式api,对于vue3的语法的改进也是大为赞赏,用起来十分方便。对于已经熟悉vue2写法的同学也说,上手还是需要一定的学习成本,有可能目前停留在会写会用的阶段,但是setup带来哪些改变,以及ref,reactive这两api内部实现原理到底是什么,下面先来总结:

setup带来的改变:

1.解决了vue2的data和methods方法相距太远,无法组件之间复用

2.提供了script标签引入共同业务逻辑的代码块,顺序执行

3.script变成setup函数,默认暴露给模版

4.组件直接挂载,无需注册

5.自定义的指令也可以在模版中自动获得

6.this不再是这个活跃实例的引用

7.带来的大量全新api,比如defineProps,defineEmits,withDefault,toRef,toRefs

ref带来的改变:

Vue 提供了一个 ref() 方法来允许我们创建可以使用任何值类型的响应式数据

Ref作TS的类型标注

reactive带来的改变:

可以使用 reactive() 函数创建一个响应式对象或数组

reactive可以隐式地从它的参数中推导类型

使用interface进行类型标注

需要了解vue2和vue3区别的可以查看我的这篇文章:

vue2和vue3的区别(由浅入深)_KinHKin(五年前端)的博客-CSDN博客_vue2开发好还是vue3开发好Vue2使⽤的是选项类型API(Options API),Vue3使⽤的是合成型API(Composition API)Vue3:数据和⽅法都定义在setup中,并统⼀进⾏return{}vue2和vue3比较还是有很多不一样的地方,比如setup语法糖的形式最为便捷而且更符合开发者习惯,未来vue3将会大面积使用这种规则,这样更加符合开发习惯和降低后续维护的成本,还有目前Vue3已经成为了Vue的默认版本,后续维护应该也会以Vue3为主。希望各位同学赶紧学起来吧~......https://blog.csdn.net/weixin_42974827/article/details/126560362?spm=1001.2014.3001.5502

2.setup

在 setup() 函数中手动暴露大量的状态和方法非常繁琐。幸运的是,我们可以通过使用构建工具来简化该操作。当使用单文件组件(SFC)时,我们可以使用 <script setup> 来大幅度地简化代码。

<script setup> 中的顶层的导入和变量声明可在同一组件的模板中直接使用。你可以理解为模板中的表达式和 <script setup> 中的代码处在同一个作用域中。

里面的代码会被编译成组件 setup() 函数的内容。这意味着与普通的 <script> 只在组件被首次引入的时候执行一次不同,<script setup>中的代码会在每次组件实例被创建的时候执行。

官方解答: 

<script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。当同时使用 SFC 与组合式 API 时该语法是默认推荐。相比于普通的 <script> 语法,它具有更多优势:

  • 更少的样板内容,更简洁的代码。
  • 能够使用纯 TypeScript 声明 props 和自定义事件。
  • 更好的运行时性能 (其模板会被编译成同一作用域内的渲染函数,避免了渲染上下文代理对象)。
  • 更好的 IDE 类型推导性能 (减少了语言服务器从代码中抽取类型的工作)。

setup执行是在创建实例之前就是beforeCreate执行,所以setup函数中的this还不是组件的实例,而是undefined,setup是同步的。

setup?: (this: void, props: Readonly<LooseRequired<Props & UnionToIntersection<ExtractOptionProp<Mixin>> & UnionToIntersection<ExtractOptionProp<Extends>>>>, ctx: SetupContext<E>) => Promise<RawBindings> | RawBindings | RenderFunction | void;)

 在上面的代码中我们了解到了第一个参数props,还有第二个参数context。

props是接受父组件传递过来的所有的属性和方法;context是一个对象,这个对象不是响应式的,可以进行解构赋值。存在属性为attrs:instance.slots,slots: instance.slots,emit: instance.emit。

setup(props, { attrs, slots, emit, expose }) {
   ...
 }
 或
 setup(props, content) {
   const { attrs, slots, emit, expose } = content
 }

这里要注意一下,attrs 和 slots 是有状态的对象,它们总是会随组件本身的更新而更新。这意味着你应该避免对它们进行解构,并始终以 attrs.x 或 slots.x 的方式引用 property。请注意,与 props 不同,attrs 和 slots 的 property 是非响应式的。如果你打算根据 attrs 或 slots 的更改应用副作用,那么应该在 onBeforeUpdate 生命周期钩子中执行此操作。

3.源码分析

在vue的3.2.3x版本中,处理setup函数源码文件位于:node_moudles/@vue/runtime-core/dist/runtime-core.cjs.js文件中。

setupStatefulComponent

下面开始解析一下setupStatefulComponent的执行过程:

function setupStatefulComponent(instance, isSSR) {
    var _a;
    const Component = instance.type;
    {
        if (Component.name) {
            validateComponentName(Component.name, instance.appContext.config);
        }
        if (Component.components) {
            const names = Object.keys(Component.components);
            for (let i = 0; i < names.length; i++) {
                validateComponentName(names[i], instance.appContext.config);
            }
        }
        if (Component.directives) {
            const names = Object.keys(Component.directives);
            for (let i = 0; i < names.length; i++) {
                validateDirectiveName(names[i]);
            }
        }
        if (Component.compilerOptions && isRuntimeOnly()) {
            warn(`"compilerOptions" is only supported when using a build of Vue that ` +
                `includes the runtime compiler. Since you are using a runtime-only ` +
                `build, the options should be passed via your build tool config instead.`);
        }
    }
    // 0. create render proxy property access cache
    instance.accessCache = Object.create(null);
    // 1. create public instance / render proxy
    // also mark it raw so it's never observed
    instance.proxy = reactivity.markRaw(new Proxy(instance.ctx, PublicInstanceProxyHandlers));
    {
        exposePropsOnRenderContext(instance);
    }
    // 2. call setup()
    const { setup } = Component;
    if (setup) {
        const setupContext = (instance.setupContext =
            setup.length > 1 ? createSetupContext(instance) : null);
        setCurrentInstance(instance);
        reactivity.pauseTracking();
        const setupResult = callWithErrorHandling(setup, instance, 0 /* ErrorCodes.SETUP_FUNCTION */, [reactivity.shallowReadonly(instance.props) , setupContext]);
        reactivity.resetTracking();
        unsetCurrentInstance();
        if (shared.isPromise(setupResult)) {
            setupResult.then(unsetCurrentInstance, unsetCurrentInstance);
            if (isSSR) {
                // return the promise so server-renderer can wait on it
                return setupResult
                    .then((resolvedResult) => {
                    handleSetupResult(instance, resolvedResult, isSSR);
                })
                    .catch(e => {
                    handleError(e, instance, 0 /* ErrorCodes.SETUP_FUNCTION */);
                });
            }
            else {
                // async setup returned Promise.
                // bail here and wait for re-entry.
                instance.asyncDep = setupResult;
                if (!instance.suspense) {
                    const name = (_a = Component.name) !== null && _a !== void 0 ? _a : 'Anonymous';
                    warn(`Component <${name}>: setup function returned a promise, but no ` +
                        `<Suspense> boundary was found in the parent component tree. ` +
                        `A component with async setup() must be nested in a <Suspense> ` +
                        `in order to be rendered.`);
                }
            }
        }
        else {
            handleSetupResult(instance, setupResult, isSSR);
        }
    }
    else {
        finishComponentSetup(instance, isSSR);
    }
}

函数接受两个参数,一个是组建实例,另一个是是否ssr渲染,接下来是验证过程,这里的文件是开发环境文件, DEV 环境,则会开始检测组件中的各种选项的命名,比如 name、components、directives 等,如果检测有问题,就会在开发环境报出警告。

检测完成之后,进行初始化,生成一个accessCached的属性对象,该属性用以缓存渲染器代理属性,以减少读取次数。然后在初始化一个代理的属性,instance.proxy = reactivity.markRaw(new Proxy(instance.ctx, PublicInstanceProxyHandlers));这个代理属性代理了组件的上下文,并且将它设置为观察原始值,这样这个代理对象将不会被追踪。

接下来便是setup的核心逻辑了,如果组件上有setup 函数,继续执行,如果不存在跳到尾部,执行finishComponentSetup(instance, isSSR),完成组件的初始化,否则就会进入 if (setup) 之后的分支条件中。是否执行setup生成上下文取决于setup.length > 1 ?createSetupContext(instance) : null。

来看一下setup执行上下文究竟有哪些东西:

function createSetupContext(instance) {
    const expose = exposed => {
        if (instance.exposed) {
            warn(`expose() should be called only once per setup().`);
        }
        instance.exposed = exposed || {};
    };
    let attrs;
    {
        // We use getters in dev in case libs like test-utils overwrite instance
        // properties (overwrites should not be done in prod)
        return Object.freeze({
            get attrs() {
                return attrs || (attrs = createAttrsProxy(instance));
            },
            get slots() {
                return reactivity.shallowReadonly(instance.slots);
            },
            get emit() {
                return (event, ...args) => instance.emit(event, ...args);
            },
            expose
        });
    }
}

 expose解析:

可以在 setup() 中使用该 API 来清除地控制哪些内容会明确地公开暴露给组件使用者。

当你在封装组件时,如果嫌 ref 中暴露的内容过多,不妨用 expose 来约束一下输出。

import { ref } from 'vue'
export default {
  setup(_, { expose }) {
    const count = ref(0)

    function increment() {
      count.value++
    }
    
    // 仅仅暴露 increment 给父组件
    expose({
      increment
    })

    return { increment, count }
  }
}

例如当你像上方代码一样使用 expose 时,父组件获取的 ref 对象里只会有 increment 属性,而 count 属性将不会暴露出去。

执行setup函数 

在处理完 createSetupContext 的上下文后,组件会停止依赖收集,并且开始执行 setup 函数。

const setupResult = callWithErrorHandling(setup, instance, 0 /* ErrorCodes.SETUP_FUNCTION */, [reactivity.shallowReadonly(instance.props) , setupContext]); 

Vue 会通过 callWithErrorHandling 调用 setup 函数,组件实例instance传入,这里我们可以看最后一行,是作为 args 参数传入的,与上文描述一样,props 会始终传入,若是 setup.length <= 1 , setupContext 则为 null。

调用玩setup之后,会重置收集的状态,reactivity.resetTracking(),接下来是判断setupResult的类型。

     if (shared.isPromise(setupResult)) {
            setupResult.then(unsetCurrentInstance, unsetCurrentInstance);
            if (isSSR) {
                // return the promise so server-renderer can wait on it
                return setupResult
                    .then((resolvedResult) => {
                    handleSetupResult(instance, resolvedResult, isSSR);
                })
                    .catch(e => {
                    handleError(e, instance, 0 /* ErrorCodes.SETUP_FUNCTION */);
                });
            }
            else {
                // async setup returned Promise.
                // bail here and wait for re-entry.
                instance.asyncDep = setupResult;
                if (!instance.suspense) {
                    const name = (_a = Component.name) !== null && _a !== void 0 ? _a : 'Anonymous';
                    warn(`Component <${name}>: setup function returned a promise, but no ` +
                        `<Suspense> boundary was found in the parent component tree. ` +
                        `A component with async setup() must be nested in a <Suspense> ` +
                        `in order to be rendered.`);
                }
            }
        }

如果 setup 函数的返回值是 promise 类型,并且是服务端渲染的,则会等待继续执行。否则就会报错,说当前版本的 Vue 并不支持 setup 返回 promise 对象。

如果不是 promise 类型返回值,则会通过 handleSetupResult 函数来处理返回结果。

else {
            handleSetupResult(instance, setupResult, isSSR);
        }
function handleSetupResult(instance, setupResult, isSSR) {
    if (shared.isFunction(setupResult)) {
        // setup returned an inline render function
        if (instance.type.__ssrInlineRender) {
            // when the function's name is `ssrRender` (compiled by SFC inline mode),
            // set it as ssrRender instead.
            instance.ssrRender = setupResult;
        }
        else {
            instance.render = setupResult;
        }
    }
    else if (shared.isObject(setupResult)) {
        if (isVNode(setupResult)) {
            warn(`setup() should not return VNodes directly - ` +
                `return a render function instead.`);
        }
        // setup returned bindings.
        // assuming a render function compiled from template is present.
        {
            instance.devtoolsRawSetupState = setupResult;
        }
        instance.setupState = reactivity.proxyRefs(setupResult);
        {
            exposeSetupStateOnRenderContext(instance);
        }
    }
    else if (setupResult !== undefined) {
        warn(`setup() should return an object. Received: ${setupResult === null ? 'null' : typeof setupResult}`);
    }
    finishComponentSetup(instance, isSSR);
}

 在 handleSetupResult 这个结果捕获函数中,首先判断 setup 返回结果的类型,如果是一个函数,并且又是服务端的行内模式渲染函数,则将该结果作为 ssrRender 属性;而在非服务端渲染的情况下,会直接当做 render 函数来处理。

接着会判断 setup 返回结果如果是对象,就会将这个对象转换成一个代理对象,并设置为组件实例的 setupState 属性。

最终还是会跟其他没有 setup 函数的组件一样,调用 finishComponentSetup 完成组件的创建。

finishComponentSetup

function finishComponentSetup(instance, isSSR, skipOptions) {
    const Component = instance.type;
    // template / render function normalization
    // could be already set when returned from setup()
    if (!instance.render) {
        // only do on-the-fly compile if not in SSR - SSR on-the-fly compilation
        // is done by server-renderer
        if (!isSSR && compile && !Component.render) {
            const template = Component.template;
            if (template) {
                {
                    startMeasure(instance, `compile`);
                }
                const { isCustomElement, compilerOptions } = instance.appContext.config;
                const { delimiters, compilerOptions: componentCompilerOptions } = Component;
                const finalCompilerOptions = shared.extend(shared.extend({
                    isCustomElement,
                    delimiters
                }, compilerOptions), componentCompilerOptions);
                Component.render = compile(template, finalCompilerOptions);
                {
                    endMeasure(instance, `compile`);
                }
            }
        }
        instance.render = (Component.render || shared.NOOP);
        // for runtime-compiled render functions using `with` blocks, the render
        // proxy used needs a different `has` handler which is more performant and
        // also only allows a whitelist of globals to fallthrough.
        if (installWithProxy) {
            installWithProxy(instance);
        }
    }
    // support for 2.x options
    {
        setCurrentInstance(instance);
        reactivity.pauseTracking();
        applyOptions(instance);
        reactivity.resetTracking();
        unsetCurrentInstance();
    }
    // warn missing template/render
    // the runtime compilation of template in SSR is done by server-render
    if (!Component.render && instance.render === shared.NOOP && !isSSR) {
        /* istanbul ignore if */
        if (!compile && Component.template) {
            warn(`Component provided template option but ` +
                `runtime compilation is not supported in this build of Vue.` +
                (``) /* should not happen */);
        }
        else {
            warn(`Component is missing template or render function.`);
        }
    }
}

这个函数的主要作用是获取并为组件设置渲染函数,对于模板(template)以及渲染函数的获取方式有以下三种规范行为:

1、渲染函数可能已经存在,通过 setup 返回了结果。例如我们在上一节讲的 setup 的返回值为函数的情况。

2、如果 setup 没有返回,则尝试获取组件模板并编译,从 Component.render 中获取渲染函数,

3、如果这个函数还是没有渲染函数,则将 instance.render 设置为空,以便它能从 mixins/extend 等方式中获取渲染函数。

这个在这种规范行为的指导下,首先判断了服务端渲染的情况,接着判断没有 instance.render 存在的情况,当进行这种判断时已经说明组件并没有从 setup 中获得渲染函数,在进行第二种行为的尝试。从组件中获取模板,设置好编译选项后调用Component.render = compile(template, finalCompilerOptions);进行编译,编译过程不再赘述。

最后将编译后的渲染函数赋值给组件实例的 render 属性,如果没有则赋值为 NOOP 空函数。

接着判断渲染函数是否是使用了 with 块包裹的运行时编译的渲染函数,如果是这种情况则会将渲染代理设置为一个不同的 has handler 代理陷阱,它的性能更强并且能够去避免检测一些全局变量。

至此组件的初始化完毕,渲染函数也设置结束了。

4.总结

在vue3中,新的setup函数属性给我们提供了书写的便利,其背后的工作量无疑是巨大的,有状态的组件的初始化的过程,在 setup 函数初始化部分我们讨论的源码的执行过程,我们不仅学习了 setup 上下文初始化的条件,也明确的知晓了 setup 上下文究竟给我们暴露了哪些属性,并且从中学到了一个新的 RFC 提案属性: expose 属性

我们学习了 setup 函数执行的过程以及 Vue 是如何处理捕获 setup 的返回结果的。

然后我们讲解了组件初始化时,不论是否使用 setup 都会执行的 finishComponentSetup 函数,通过这个函数内部的逻辑我们了解了一个组件在初始化完毕时,渲染函数设置的规则。

最后,如果本文对你了解setup过程有所帮助,希望三连支持一波哈~~~❤️

你也可以关注我的vue其他文章:

https://blog.csdn.net/weixin_42974827/category_11970323.html?spm=1001.2014.3001.5482icon-default.png?t=M85Bhttps://blog.csdn.net/weixin_42974827/category_11970323.html?spm=1001.2014.3001.5482

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/405001.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Swagger-的使用(详细教程)

文章目录前言一、简介二、基本使用1. 导入相关依赖2. 编写配置文件2.1 配置基本信息2.2 配置接口信息2.3 配置分组信息3. 控制 Swagger 的开启4. 常用注解使用ApiModelApiModelPropertyApiOperationApiParam5. 接口调用三、进阶使用1. 添加请求头四、项目下载前言 作为后端开放…

37.JavaScript对象与JSON格式的转换,JSON.stringify、JSON.parse方法的使用方法和注意事项

文章目录JSON处理JSON.stringifystringify的限制排除和替换映射函数格式化使用的空格数量自定义toJSON方法JSON.parse使用reviver总结JSON处理 JSON&#xff08;JavaScript Object Notation&#xff09;是JavaScript表达值和对象的通用数据格式&#xff0c;其本质就是符合一定…

2023前端面试题及答案整理(JavaScript)

JS类型 string&#xff0c;number&#xff0c;boolean&#xff0c;undefined&#xff0c;null&#xff0c;symbol&#xff08;es6&#xff09;&#xff0c;BigInt&#xff08;es10&#xff09;&#xff0c;object 值类型和引用类型的区别 两种类型的区别是&#xff1a;存储位…

Js各种时间转换问题(YYYY-MM-DD 时间戳 中国标准时间)

1. 类型总结 指定格式 YYYY-MM-DD HH:MM:SS时间戳中国标准时间 Sat Jan 30 2022 08:26:26 GMT0800 (中国标准时间) new Date()获得系统当前时间就会是这种形式 2. 类型之间的转换 时间戳转换为 yyyy-mm-dd或yyyy-MM-dd HH-mm-ss function timestampToTime(timestamp) {var …

【vue】 配置代理

文章目录参考文档跨域问题引入配置代理解决跨域问题&#xff1a;方法一&#xff1a;方法二&#xff1a;使用方法二最终的文件&#xff1a;总结参考文档 尚硅谷视频&#xff1a;https://www.bilibili.com/video/BV1Zy4y1K7SH?p95 axios官网教程&#xff1a;https://axios-htt…

uniapp分包,小程序分包处理,详解(图解),手把手从0开始

一、为什么要分包 因小程序有体积和资源加载限制&#xff0c;优化小程序的下载和启动速度。 二、主包和分包 所谓的主包&#xff0c;即放置默认启动页面/TabBar 页面&#xff0c;以及一些所有分包都需用到公共资源/JS 脚本&#xff1b;而分包则是根据pages.json的配置进行划…

微信小程序详细教程(建议收藏)

一.小程序的开发准备 1. 小程序的安装与创建 第一步 打开小程序官网第二步 找到开发管理&#xff0c;找到开发设置&#xff0c;下面有一个AppID&#xff0c;复制即可&#xff0c;后面开发小程序需要用 新建项目 &#xff0c;需要先下载微信开发工具下载网址&#xff0c;安装完…

九个前端神奇库,让你的前端项目瞬间美化,甲方看了都落泪

九个前端神奇库&#xff0c;让你的前端项目瞬间美化&#xff0c;甲方看了都落泪 九个前端神奇库 文章目录九个前端神奇库&#xff0c;让你的前端项目瞬间美化&#xff0c;甲方看了都落泪1. Lottie-web/Bodymovin2. Parallax.js3. Magic Grid4. webslides5. SVG.js6. rellax7. …

【Vue面试专题】50+道经典Vue面试题详解!

目录前言相关学习资源01-Vue组件之间通信方式有哪些02-v-if和v-for哪个优先级更高&#xff1f;03-能说一说双向绑定使用和原理吗&#xff1f;04-Vue中如何扩展一个组件05-子组件可以直接改变父组件的数据么&#xff0c;说明原因06-Vue要做权限管理该怎么做&#xff1f;控制到按…

hexo主题应用

可以在hexo主题官网自己选择,官网网址:主题,选择哪个全凭自己的喜好。 我选择的一个主题是stun,官网效果图 安装主题stun git clone https://github.com/liuyib/hexo-theme-stun.git themes/stun安装依赖 git clone -b dist https://github.com/liuyib/hexo-theme-stun…

前端常见面试八股文

HTML篇 1、H5新增标签有哪些&#xff1f; 一、语义化标签 header、footer、nav、aside、section、article 语义化的意义&#xff1f; 1、更适合搜索引擎的爬虫爬取有效的信息&#xff0c;利于SEO。 2、对开发团队很友好&#xff0c;增加了标签的可读性&#xff0c;结构更…

VScode 看这一篇就够了

VScode 小白入门教程 VScode 小白入门教程 VScode简介VScode的下载与安装VScode的常用设置 基础设置禁用自动更新自动保存设置Vscode更换主题 VScode的常用快捷键开发人员常用的VScode插件使用VScode开始你的第一行C/C代码 VScode简介 VScode全称是Visual Studio Code&…

一文吃透JavaScript程序控制流与函数

文章目录思维导图&#x1f496;程序控制流⛳️顺序结构⛳️分支结构&#x1f4ab;比较运算符&#x1f4ab;逻辑运算符&#x1f4ab;if语句&#x1f4ab;switch语句⛳️循环结构&#x1f4ab;while语句&#x1f4ab;do...while语句&#x1f4ab;for语句&#x1f4ab;break和cont…

【小程序开发】uniapp引入iconfont图标及使用方式

&#x1f9ca; 前言 本文主要讲述的是在使用uniapp中如何引入iconfont图标&#xff0c;以及两种常用的位置。 位置一&#xff1a;App下原生导航栏的按钮使用字体图标。位置二&#xff1a;页面中的任意位置使用iconfont图标。 &#x1f33a; 正文 第一步&#xff1a;打开iconfo…

HTML爱心网页制作[樱花+爱心]

HTMLCSSJavaScript实现 先点赞后观看,养成好习惯 “不想动手的小伙伴可以直接拿网盘成品” 阿里云盘------提取码: 0d5j 效果图 注:任意浏览器都可以,建议使用谷歌浏览器 代码部分 代码如下仅供参考 可以直接拿去复制粘贴 <!DOCTYPE html> <html><head>…

2022最新Web前端经典面试试题及答案-史上最全前端面试题(含答案)

程序员面试题库分享 1、前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 2、前端技术导航大全 推荐&#xff1a;★★★★★ 地址&#xff1a;前端技术导航大全 3、开发者颜色值转换工具 推荐&…

【vite·4】vite在多环境下的配置集成方案及defineConfig(非常详细)

vite的默认配置文件是vite.config.js&#xff0c;最基础的配置文件格式如下&#xff1a; export default {// 配置选项 };我们也可以通过 –config 命令行选项指定一个配置文件&#xff0c;命令行输入&#xff1a; vite --config my-config.js vite运行在node环境&#xff0c;…

【前端小技能】Vue集成百度离线地图

Vue项目集成百度离线地图 工作中遇到了一个需求&#xff0c;要在内网使用百度地图&#xff0c;那么肯定就是离线的地图了&#xff0c;查阅了一些博文&#xff0c;开发过程中也遇到了各种各样的问题&#xff0c;在此做下记录&#xff0c;希望带大家避坑&#xff0c;也给自己这两…

若依:如何去掉首页,设置登录后跳转到第一个路由菜单

若依系统是一个很好用的&#xff0c;开源的前端后台管理系统。 最近公司有一个需求&#xff0c;需要把默认的首页隐藏&#xff0c;登录后直接跳转到路由菜单返回的第一个页面。 查找了不少资料&#xff0c;但是都没有实际的解决方案。 不过最好自己还是实现了这个功能。 步骤…

Vue/React实现路由鉴权/导航守卫/路由拦截(react-router v6)

欢迎来到我的博客 &#x1f4d4;博主是一名大学在读本科生&#xff0c;主要学习方向是前端。 &#x1f36d;目前已经更新了【Vue】、【React–从基础到实战】、【TypeScript】等等系列专栏 &#x1f6e0;目前正在学习的是&#x1f525;React框架React框架React框架&#x1f525…