你不知道的vue3:使用runWithContext实现在非 setup 期间使用inject

这篇具有很好参考价值的文章主要介绍了你不知道的vue3:使用runWithContext实现在非 setup 期间使用inject。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

日常开发时有些特殊的场景需要在非 setup 期间调用inject函数,比如app中使用provide注入的配置信息需要在发送http请求时带上传给后端。对此我们希望不在每个发起请求的地方去修改,而是在发起请求前的拦截进行统一处理,对此我们就需要在拦截请求的函数中使用inject拿到app注入的配置信息。

为什么只能在setup 期间调用inject函数

inject的用法大家应该都清楚,是一个用于注入依赖的函数,它可以将父组件或根组件 app 中通过 provide 提供的相同 key 的值注入到当前组件中。

我们先来看看简化后的providerinject的源码,其实非常简单。

provider函数源码

我们先来看看简化后的provider函数源码,其实很简单:

export function provide(
  key,
  value,
) {
  //拿到当前组件的vue实例提供的provides对象
  let provides = currentInstance.provides
  //拿到父组件的vue实例提供的provides对象
  const parentProvides =
    currentInstance.parent && currentInstance.parent.provides
  // 如果父组件和当前组件的provides对象相等
  if (parentProvides === provides) {
    // 基于父组件的provides对象拷贝出一个新的对象
    provides = currentInstance.provides = Object.create(parentProvides)
  }
  // 如果provides对象中有相同的key,那么就会直接覆盖。
  provides[key] = value
}

在初始化一个vue实例的时候会将父组件的provides对象赋值给当前实例的provides对象,所以当第一次provide方法被调用后,会判断当前的provides对象是否等于父组件provides对象,如果相等就会基于父组件实例的provides对象拷贝一个新的provides对象。

此时父组件和子组件的provides对象经过Object.create(parentProvides)后就已经不是同一个对象了。如果子组件和父组件provide对象中都有相同的key,经过provides[key] = value后就会将原本父组件赋值的相同key的值“覆盖”掉。因为父组件的provides对象是从他的父组件provides对象拷贝的而来,所以子组件包含了父组件链上的所有的provide提供的值。

机智如你现在应该能够理解为什么官网会说“父组件链上多个组件对同一个 key 提供了值,那么离得更近的组件将会“覆盖”链上更远的组件所提供的值”。

inject函数源码

现在我们再来看看简化后的inject函数源码,同样也非常简单:

export function inject(
  key,
) {
  //currentInstance是一个存储当前vue实例的全局变量,在vue组件初始化时会赋值。
  //初始化完成后会被重置为null
  const instance = currentInstance

  if (instance || currentApp) {
    // 拿到父组件或者currentApp中提供的provides对象
    const provides = instance
      ? instance.parent.provides
      : currentApp!._context.provides
    // 从provides对象中拿到相同key的值
    if (provides && key in provides) {
      return provides[key]
    }
  } else if (__DEV__) {
    // 不是在setup中或者runWithContext中调用,就会发出警告
    warn(`inject() can only be used inside setup() or functional components.`)
  }
}

我们首先来看看currentInstance这个全局变量,setup只会在初始化vue实例的时候执行一次,在setup期间currentInstance会被赋值为当前组件的vue实例。等vue实例初始化完成后currentInstance就会被赋值为null

前面我们已经介绍了组件的provides对象中是包含了父组件链上的所有provides的key,所以我们这里只需要从当前vue实例instanceparent中的provides对象中就可以取出注入相同key的值。

看到这里相信你已经知道了为什么只能在setup 期间调用调用inject方法了。因为只有在setup期间currentInstance全局变量的值为当前组件的vue实例对象,当vue实例初始化完成后currentInstance已经被赋值为null。所以当我们在非setup 期间调用inject方法会警告:inject() can only be used inside setup() or functional components.

至于currentApp其实是另外一个全局变量,在调用app.runWithContext方法时会给它赋值,这个下一节我们讲app.runWithContext的时候会详细讲。

使用app.runWithContext()打破inject只能在setup 期间调用的限制

app.runWithContext()的官方解释为“使用当前应用作为注入上下文执行回调函数”。这个解释乍一看很容易一脸懵逼,不着急我慢慢给你解释。

我们先来看看runWithContext方法接收的参数和返回的值。这个方法接收一个参数,参数是一个回调函数。这个回调函数会在app.runWithContext()执行时被立即执行,并且app.runWithContext()的返回值就是回调函数的返回值。

我们再来看一个使用runWithContext的例子,这行代码是拦截请求时才执行。作用是拿到app中注入的userType字段,注意不是在setup期间执行。

const userType = app.runWithContext(() => {
  // 拿到app中注入的userType字段
  return inject("userType");
});

按照我们前一节的分析,inject需要在setup中执行才能拿到当前的vue实例。但是之前还有一个currentApp变量我们没有解释,再来回顾一下上一节的inject源码。如果我们拿不到当前的vue实例,就会去看一下全局变量currentApp是否存在,如果存在那么就从currentApp中去拿provides对象。这个currentApp就是官方解释的“注入的上下文”,所以我们才可以在非setup期间执行inject,并且还可以拿到注入的值。

if (instance || currentApp) {
  // 拿到父组件或者currentApp中提供的provides对象
  const provides = instance
    ? instance.parent.provides
    : currentApp!._context.provides
  // 从provides对象中拿到相同key的值
  if (provides && key in provides) {
    return provides[key]
  }
}

我们再来看看runWithContext的源码,其实非常简单。

runWithContext(fn) {
  // 将调用runWithContext方法的对象赋值给全局对象currentApp
  currentApp = app
  try {
    // 立即执行传入的回调函数
    return fn()
  } finally {
    currentApp = null
  }
}

这里的app就是调用runWithContext方法的对象,你可以简单的理解为this。调用app.runWithContext()就会将app对象赋值给全局变量currentApp,然后会立即执行传入的回调fn。当执行到回调中的inject("userType")时,由于我们在上一行代码已经给全局变量currentApp赋值为app了,所以就可以从app中拿到对应key的provider值。

总结

这篇文章我们先介绍了由于inject执行期间需要拿到当前的vue实例,然后才能从父组件提供的provides对象中找到相同key的值。如果我们在非 setup 期间执行,那么就拿不到当前vue实例。也找不到父组件,当然inject也没法拿到注入的值。

在一些场景中我们确实需要在非 setup 期间执行inject,这时我们就可以使用app.runWithContext()app对象作为注入上下文执行回调函数。然后在inject执行期间就能从app中拿到提供的provides对象中相同key的值。

如果我的文章对你有点帮助,欢迎关注公众号:【欧阳码农】,文章在公众号首发。你的支持就是我创作的最大动力,感谢感谢!文章来源地址https://www.toymoban.com/news/detail-798231.html

到了这里,关于你不知道的vue3:使用runWithContext实现在非 setup 期间使用inject的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue3的setup的使用和原理解析

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

    2024年02月02日
    浏览(28)
  • 在Vue3的setup中如何使用this

    在Vue2中,可以通过this来获取当前组件实例; 但是Vue3中,在setup中无法通过this获取组件实例,console.log(this)打印出来的值是undefined。 所以想要在Vue3中使用this, Vue为我们提供了getCurrentInstance()方法,这个方法返回了ctx和proxy。  1、概述:一个很重要的方法,获取当前组件的

    2024年02月12日
    浏览(42)
  • vue3的setup函数中定义data数据,使用data数据

    vue3保留了vue2的语法,但是不能混着用,setup中定义原有vue2中data数据的方法有所改变 直接上代码 对比原有vue2代码 data() {             return {                 name: \\\"张三\\\",                 age:18             }     } 在setup函数中使用数据也无需 this.xxx, 而是用你定义的名字

    2024年02月12日
    浏览(30)
  • vue3自定义dialog createApp setup语法组件使用element

    目录  index.vue mapDialog.js

    2024年02月14日
    浏览(34)
  • Vue3 项目中使用setup()函数报错,script setup cannot contain ES module exports

    当使用vue3+vite使用语法糖setup时,要注意写法. 第一种写法就是 script 标签里面配置 setup,另一种是:export default 类里配置 setup() 方法, 我们只需要使用一种方法即可,混用了就会报错了。 解决: 第一种 script setup import {ref} from \\\'vue\\\' import { Toast } from \\\'vant\\\'; import Index from \\\'../pag

    2023年04月08日
    浏览(40)
  • vue3 setup语法糖 使用组件内的路由守卫beforeRouteEnter使用方法

    由于beforeRouteEnter在setup语法糖中是无法使用的,所以需要再起一个script标签 使用defineComponent方式来使用就可以了

    2024年02月11日
    浏览(44)
  • 【vue3】组合式API之setup()介绍与reactive()函数的使用

    ==😉博主:初映CY的前说(前端领域) ,📒本文核心:setup()概念、 reactive()的使用 【前言】vue3作为vue2的升级版,有着很多的新特性,其中就包括了组合式API,也就是是 Composition API。学习组合式API有什么优点呢?之前的vue2中结构不是挺不错的吗?那么接下来的事件,我将带着你

    2023年04月09日
    浏览(32)
  • vue3+element-plus Dialog对话框的使用 与 setup 写法的使用

    一. 传统写法不使用setup语法糖 方式一:通过 v-model 的方式实现子组件的显示与隐藏 父组件的内容 子组件内容 方式二:通过为元素绑定 ref 的方式实现子组件的显示与隐藏 父组件的内容 子组件内容 2. setup 语法糖写法 父组件 子组件 总结: 对于传统写法两种方式来看,都有

    2024年02月09日
    浏览(41)
  • element - - - - - 你不知道的loading使用方式

    求人不如求己 关于页面交互,最害怕的就是接口等待时间太长,用户体验不好。 而如何提高用户体验呢? 接口返回速度 这个是后端同学去优化,前端同学也可通过 加载loading 来优化体验 Element 提供了两种调用 Loading 的方法:指令和服务 详情可查看官网 : Element Loading 加载

    2024年02月02日
    浏览(29)
  • 你不知道的 Git 技巧:如何实现核心代码保护

    大家好,我是 shixin。 前段时间完成了一个核心代码保护的功能,目标是在关键代码被修改及时同步给其他人,避免没经过 review 就上线导致问题,提示的效果图如下: 在实现的过程中,用到一些平时使用不多的 Git 技巧,这篇文章来总结一下。 这个比较简单,通过 git confi

    2023年04月15日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包