返回顶部
首页 > 资讯 > 精选 >Vue3中的依赖注入与组件定义怎么实现
  • 658
分享到

Vue3中的依赖注入与组件定义怎么实现

2023-07-05 14:07:24 658人浏览 泡泡鱼
摘要

本篇内容主要讲解“vue3中的依赖注入与组件定义怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Vue3中的依赖注入与组件定义怎么实现”吧!provide() & inject()

本篇内容主要讲解“vue3中的依赖注入与组件定义怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习Vue3中的依赖注入与组件定义怎么实现”吧!

provide() & inject()

provide()

提供一个值,可以被后代组件注入。

function provide<T>(key: InjectionKey<T> | string, value: T): void

接收两个参数:

export interface InjectionKey<T> extends Symbol {}

  • 对应注入的值

与注册生命周期钩子的 api 类似,provide() 必须在组件的 setup() 阶段同步调用。

inject()

注入一个由祖先组件或整个应用 (通过 app.provide()) 提供的值。

// 没有默认值function inject<T>(key: InjectionKey<T> | string): T | undefined// 带有默认值function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T// 使用工厂函数function inject<T>(  key: InjectionKey<T> | string,  defaultValue: () => T,  treatDefaultAsFactory: true): T

  • 第一个参数是注入的 keyVue 会遍历父组件链,通过匹配 key 来确定所提供的值。如果父组件链上多个组件对同一个 key 提供了值,那么离得更近的组件将会“覆盖”链上更远的组件所提供的值。如果没有能通过 key 匹配到值,inject() 将返回 undefined,除非提供了一个默认值。

  • 第二个参数是可选的,即在没有匹配到 key 时使用的默认值。它也可以是一个工厂函数,用来返回某些创建起来比较复杂的值。如果默认值本身就是一个函数,那么你必须将 false 作为第三个参数传入,表明这个函数就是默认值,而不是一个工厂函数。

provide() & inject() - 官方示例

// provide<script setup>  import {(ref, provide)} from 'vue' import {fooSymbol} from  './injectionSymbols' // 提供静态值 provide('foo', 'bar') // 提供响应式的值  const count = ref(0) provide('count', count) // 提供时将 Symbol 作为 key  provide(fooSymbol, count)</script>
// inject<script setup>import { inject } from 'vue'import { fooSymbol } from './injectionSymbols'// 注入值的默认方式const foo = inject('foo')// 注入响应式的值const count = inject('count')// 通过 Symbol 类型的 key 注入const foo2 = inject(fooSymbol)// 注入一个值,若为空则使用提供的默认值const bar = inject('foo', 'default value')// 注入一个值,若为空则使用提供的工厂函数const baz = inject('foo', () => new Map())// 注入时为了表明提供的默认值是个函数,需要传入第三个参数const fn = inject('function', () => {}, false)</script>

provide() & inject() - ElementUI Plus 示例 Breadcrumb 组件

<script setup>import { onMounted, provide, ref } from 'vue'import { useNamespace } from '@element-plus/hooks'import { breadcrumbKey } from './constants'import { breadcrumbProps } from './breadcrumb'defineOptions({  name: 'ElBreadcrumb',})const props = defineProps(breadcrumbProps)const ns = useNamespace('breadcrumb')const breadcrumb = ref<htmlDivElement>()// 提供值provide(breadcrumbKey, props)onMounted(() => {  ......})</script>
<script setup>import { getCurrentInstance, inject, ref, toRefs } from 'vue'import ElIcon from '@element-plus/components/icon'import { useNamespace } from '@element-plus/hooks'import { breadcrumbKey } from './constants'import { breadcrumbItemProps } from './breadcrumb-item'import type { Router } from 'vue-router'defineOptions({  name: 'ElBreadcrumbItem',})const props = defineProps(breadcrumbItemProps)const instance = getCurrentInstance()!// 注入值const breadcrumbContext = inject(breadcrumbKey, undefined)!const ns = useNamespace('breadcrumb') ......</script>

provide() & inject() - VueUse 示例

createInjectionState 源码 / createInjectionState 使用

package/core/computedInject 源码

import { type InjectionKey, inject, provide } from 'vue-demi'export function createInjectionState<Arguments extends Array<any>, Return>(  composable: (...args: Arguments) => Return): readonly [  useProvidingState: (...args: Arguments) => Return,  useInjectedState: () => Return | undefined] {  const key: string | InjectionKey<Return> = Symbol('InjectionState')  const useProvidingState = (...args: Arguments) => {    const state = composable(...args)    provide(key, state)    return state  }  const useInjectedState = () => inject(key)  return [useProvidingState, useInjectedState]}

nextTick()

等待下一次 DOM 更新刷新的工具方法。

function nextTick(callback?: () => void): Promise<void>

说明:当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。

nextTick() 可以在状态改变后立即使用,以等待 DOM 更新完成。你可以传递一个回调函数作为参数,或者 await 返回的 Promise

nextTick() 官网示例

<script setup>import { ref, nextTick } from 'vue'const count = ref(0)async function increment() {  count.value++  // DOM 还未更新  console.log(document.getElementById('counter').textContent) // 0  await nextTick()  // DOM 此时已经更新  console.log(document.getElementById('counter').textContent) // 1}</script><template>  <button id="counter" @click="increment">{{ count }}</button></template>

nextTick() - ElementUI Plus 示例

ElCascaderPanel 源码

export default defineComponent({  ......  const syncMenuState = (    newCheckednodes: CascaderNode[],    reserveExpandingState = true  ) => {    ......    checkedNodes.value = newNodes    nextTick(scrollToExpandingNode)  }  const scrollToExpandingNode = () => {    if (!isClient) return    menuList.value.forEach((menu) => {      const menuElement = menu?.$el      if (menuElement) {        const container = menuElement.querySelector(`.${ns.namespace.value}-scrollbar__wrap`)        const activeNode = menuElement.querySelector(`.${ns.b('node')}.${ns.is('active')}`) ||          menuElement.querySelector(`.${ns.b('node')}.in-active-path`)        scrollIntoView(container, activeNode)      }    })  }  ......})

nextTick() - VueUse 示例

useInfiniteScroll 源码

export function useInfiniteScroll(  element: MaybeComputedRef<HTMLElement | SVGElement | Window | Document | null | undefined>  ......) {  const state = Reactive(......)  watch(    () => state.arrivedState[direction],    async (v) => {      if (v) {        const elem = resolveUnref(element) as Element        ......        if (options.preserveScrollPosition && elem) {          nextTick(() => {            elem.scrollTo({              top: elem.scrollHeight - previous.height,              left: elem.scrollWidth - previous.width,            })          })        }      }    }  )}

使用场景:
  • 当你需要在修改了某些数据后立即对 DOM 进行操作时,可以使用 nextTick 来确保 DOM 已经更新完毕。例如,在使用 $ref 获取元素时,需要确保元素已经被渲染才能够正确获取。

  • 在一些复杂页面中,有些组件可能会因为条件渲染或动态数据而频繁地变化。使用 nextTick 可以避免频繁地进行 DOM 操作,从而提高应用程序的性能。

  • 当需要在模板中访问某些计算属性或者监听器中的值时,也可以使用 nextTick 来确保这些值已经更新完毕。这样可以避免在视图中访问到旧值。

总之,nextTick 是一个非常有用的 API,可以确保在正确的时机对 DOM 进行操作,避免出现一些不必要的问题,并且可以提高应用程序的性能。

defineComponent()

在定义 Vue 组件时提供类型推导的辅助函数。

function defineComponent(  component: ComponentOptions | ComponentOptions['setup']): ComponentConstructor

第一个参数是一个组件选项对象。返回值将是该选项对象本身,因为该函数实际上在运行时没有任何操作,仅用于提供类型推导。

注意返回值的类型有一点特别:它会是一个构造函数类型,它的实例类型是根据选项推断出的组件实例类型。这是为了能让该返回值在 TSX 中用作标签时提供类型推导支持。

const Foo = defineComponent()// 提取出一个组件的实例类型 (与其选项中的 this 的类型等价)type FooInstance = InstanceType<typeof Foo>

参考:Vue3 - defineComponent 解决了什么?

defineComponent() - ElementUI Plus 示例

ConfigProvider 源码

import { defineComponent, renderSlot, watch } from 'vue'import { provideGlobalConfig } from './hooks/use-global-config'import { configProviderProps } from './config-provider-props'......const ConfigProvider = defineComponent({  name: 'ElConfigProvider',  props: configProviderProps,  setup(props, { slots }) {    ......  },})export type ConfigProviderInstance = InstanceType<typeof ConfigProvider>export default ConfigProvider

defineComponent() - Treeshaking

因为 defineComponent() 是一个函数调用,所以它可能被某些构建工具认为会产生副作用,如 webpack。即使一个组件从未被使用,也有可能不被 tree-shake

为了告诉 WEBpack 这个函数调用可以被安全tree-shake,我们可以在函数调用之前添加一个 /_#**PURE**_/ 形式的注释:

export default  defineComponent()

请注意,如果你的项目中使用的是 Vite,就不需要这么做,因为 Rollup (Vite 底层使用的生产环境打包工具) 可以智能地确定 defineComponent() 实际上并没有副作用,所以无需手动注释。

defineComponent() - VueUse 示例

OnClickOutside 源码

import { defineComponent, h, ref } from 'vue-demi'import { onClickOutside } from '@vueuse/core'import type { RenderableComponent } from '../types'import type { OnClickOutsideOptions } from '.'export interface OnClickOutsideProps extends RenderableComponent {  options?: OnClickOutsideOptions}export const OnClickOutside =  defineComponent<OnClickOutsideProps>({    name: 'OnClickOutside',    props: ['as', 'options'] as unknown as undefined,    emits: ['trigger'],    setup(props, { slots, emit }) {      ... ...      return () => {        if (slots.default)          return h(props.as || 'div', { ref: target }, slots.default())      }    },  })

defineAsyncComponent()

定义一个异步组件,它在运行时是懒加载的。参数可以是一个异步加载函数,或是对加载行为进行更具体定制的一个选项对象。

function defineAsyncComponent(  source: AsyncComponentLoader | AsyncComponentOptions): Componenttype AsyncComponentLoader = () => Promise<Component>interface AsyncComponentOptions {  loader: AsyncComponentLoader  loadinGComponent?: Component  errorComponent?: Component  delay?: number  timeout?: number  suspensible?: boolean  onError?: (    error: Error,    retry: () => void,    fail: () => void,    attempts: number  ) => any}

defineAsyncComponent() - 官网示例

<script setup>import { defineAsyncComponent } from 'vue'const AsyncComp = defineAsyncComponent(() => {  return new Promise((resolve, reject) => {    resolve()  })})const AdminPage = defineAsyncComponent(() =>  import('./components/AdminPageComponent.vue'))</script><template>  <AsyncComp />  <AdminPage /></template>

ES 模块动态导入也会返回一个 Promise,所以多数情况下我们会将它和 defineAsyncComponent 搭配使用。类似 ViteWebpack 这样的构建工具也支持此语法 (并且会将它们作为打包时的代码分割点),因此我们也可以用它来导入 Vue 单文件组件。

defineAsyncComponent() - VitePress 示例

<script setup>import { defineAsyncComponent } from 'vue'import type { DefaultTheme } from 'vitepress/theme'defineProps<{ carbonAds: DefaultTheme.CarbonAdsOptions }>()const VPCarbonAds = __CARBON__  ? defineAsyncComponent(() => import('./VPCarbonAds.vue'))  : () => null</script><template>  <div>    <VPCarbonAds :carbon-ads="carbonAds" />  </div></template>

defineAsyncComponent()使用场景:

  • 当你需要异步加载某些组件时,可以使用 defineAsyncComponent 来进行组件懒加载,这样可以提高应用程序的性能。

  • 在一些复杂页面中,有些组件可能只有在用户执行特定操作或进入特定页面时才会被使用到。使用 defineAsyncComponent 可以降低初始页面加载时的资源开销。

  • 当你需要动态地加载某些组件时,也可以使用 defineAsyncComponent。例如,在路由中根据不同的路径加载不同的组件。

Vue3 之外,许多基于 Vue 3 的库和框架也开始使用 defineAsyncComponent 来实现组件的异步加载。例如:

  • VitePress: Vite 的官方文档工具,使用 defineAsyncComponent 来实现文档页面的异步加载。

  • Nuxt.js: 基于 vue.js 的静态网站生成器,从版本 2.15 开始支持 defineAsyncComponent

  • Quasar Framework: 基于 Vue.js 的 UI 框架,从版本 2.0 开始支持 defineAsyncComponent

  • Element UI Plus: 基于 Vue 3 的 UI 库,使用 defineAsyncComponent 来实现组件的异步加载。

总之,随着 Vue 3 的普及,越来越多的库和框架都开始使用 defineAsyncComponent 来提高应用程序的性能。

defineCustomElement()

这个方法和 defineComponent 接受的参数相同,不同的是会返回一个原生自定义元素类的构造器。

function defineCustomElement(  component:    | (ComponentOptions & { styles?: string[] })    | ComponentOptions['setup']): {  new (props?: object): HTMLElement}

除了常规的组件选项,defineCustomElement() 还支持一个特别的选项 styles,它应该是一个内联 CSS 字符串的数组,所提供的 CSS 会被注入到该元素的 shadow root 上。返回值是一个可以通过 customElements.define() 注册的自定义元素构造器。

import { defineCustomElement } from 'vue'const MyVueElement = defineCustomElement({  })// 注册自定义元素customElements.define('my-vue-element', MyVueElement)

使用 Vue 构建自定义元素

import { defineCustomElement } from 'vue'const MyVueElement = defineCustomElement({  // 这里是同平常一样的 Vue 组件选项  props: {},  emits: {},  template: `...`,  // defineCustomElement 特有的:注入进 shadow root 的 CSS  styles: [``],})// 注册自定义元素// 注册之后,所有此页面中的 `<my-vue-element>` 标签// 都会被升级customElements.define('my-vue-element', MyVueElement)// 你也可以编程式地实例化元素:// (必须在注册之后)document.body.appendChild(  new MyVueElement({    // 初始化 props(可选)  }))// 组件使用<my-vue-element></my-vue-element>

除了 Vue 3 之外,一些基于 Vue 3 的库和框架也开始使用 defineCustomElement 来将 Vue 组件打包成自定义元素供其他框架或纯 HTML 页面使用。例如:

  • Ionic Framework: 基于 Web Components 的移动端 UI 框架,从版本 6 开始支持使用 defineCustomElementIonic 组件打包成自定义元素。

  • LitElement: Google 推出的 Web Components 库,提供类似 Vue 的模板语法,并支持使用 defineCustomElementLitElement 组件打包成自定义元素。

  • Stencil: 由 Ionic Team 开发Web Components 工具链,可以将任何框架的组件转换为自定义元素,并支持使用 defineCustomElement 直接将 Vue 组件打包成自定义元素。

到此,相信大家对“Vue3中的依赖注入与组件定义怎么实现”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

--结束END--

本文标题: Vue3中的依赖注入与组件定义怎么实现

本文链接: https://lsjlt.com/news/352475.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Vue3中的依赖注入与组件定义怎么实现
    本篇内容主要讲解“Vue3中的依赖注入与组件定义怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Vue3中的依赖注入与组件定义怎么实现”吧!provide() & inject()...
    99+
    2023-07-05
  • 聊聊Vue3中的依赖注入与组件定义
    本次主要分享Vue3中依赖注入以及组件定义相关的几个API,以及在常用库ElementUI Plus和Vueuse中的使用情况,通过示例来理解使用场景。让我们聊聊 Vue 3中依赖注入与组件定义相关的那点事儿。provide() &...
    99+
    2023-05-14
    Vue vue3
  • VSCode中的依赖注入怎么实现
    这篇文章主要讲解了“VSCode中的依赖注入怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“VSCode中的依赖注入怎么实现”吧!依赖注入介绍如果有这样一个模块 A,它的实现依赖另一个...
    99+
    2023-07-04
  • Laravel的依赖注入怎么实现
    这篇文章主要介绍了Laravel的依赖注入怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Laravel的依赖注入怎么实现文章都会有所收获,下面我们一起来看看吧。在Laravel中,依赖注入的实现原理是利...
    99+
    2023-06-29
  • Angular依赖注入怎么实现
    今天小编给大家分享一下Angular依赖注入怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解...
    99+
    2024-04-02
  • python依赖注入怎么实现
    在Python中实现依赖注入有多种方法,下面介绍两种常用的实现方式:1. 使用第三方库:可以使用流行的依赖注入框架,例如Dagger...
    99+
    2023-08-30
    python
  • golang依赖注入怎么实现
    在Go语言中实现依赖注入可以使用两种常见的方式:构造函数注入和接口注入。1. 构造函数注入:构造函数注入是通过在结构体中定义一个包含...
    99+
    2023-09-17
    golang
  • Spring IoC中怎么实现依赖注入
    本篇文章为大家展示了Spring IoC中怎么实现依赖注入,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一、依赖注入(DI)DI(Dependency Inject...
    99+
    2024-04-02
  • Vue 3.0中怎么实现依赖注入
    这篇文章将为大家详细讲解有关Vue 3.0中怎么实现依赖注入,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。使用过 Angular 的小伙伴对 依赖注入 应该...
    99+
    2024-04-02
  • PHP中怎样实现依赖注入
    今天就跟大家聊聊有关PHP中怎样实现依赖注入,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。我们来看一段代码:class A{ public functi...
    99+
    2023-06-04
  • spring怎么实现依赖注入DI
    这篇文章主要介绍了spring怎么实现依赖注入DI的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇spring怎么实现依赖注入DI文章都会有所收获,下面我们一起来看看吧。spring依赖注入DI1、创建一个mav...
    99+
    2023-06-29
  • Asp.net core中依赖注入的实现
    使用服务 在Asp.net core的Controller中,可以通过如下两种方式获取系统注入的服务: 构造函数 可以直接在构造函数中传入所依赖的服务,这是非常常见的DI注入方式。 ...
    99+
    2024-04-02
  • 使用Spring自定义实现IOC和依赖注入(注解方式)
    目录大致思路:注解实现方式:xml实现方式:1. 引入相关jar2. 定义注解类ExtService是注解类的, ExtResource是注解属性的3.定义一个借口4. 接口和使用注...
    99+
    2024-04-02
  • ASP.NET Core依赖关系注入怎么实现
    本篇内容主要讲解“ASP.NET Core依赖关系注入怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“ASP.NET Core依赖关系注入怎么实现”吧!1.前言面向对象...
    99+
    2023-06-29
  • 使用PHP怎么实现依赖注入模式
    使用PHP怎么实现依赖注入模式?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。用法DatabaseConfiguration 被注入 DatabaseConnection 并...
    99+
    2023-06-15
  • ASP.NET Core依赖注入DI容器怎么实现
    这篇文章主要介绍“ASP.NET Core依赖注入DI容器怎么实现”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“ASP.NET Core依赖注入DI容器怎么实现”文章能帮助大家解...
    99+
    2023-07-05
  • PHP控制反转与依赖注入的实现介绍
    本篇文章给大家带来了关于PHP的相关知识,IOC-Inversion of Control,即控制反转。它不是什么技术,而是一种设计思想。下面介绍了php控制反转与依赖注入的实现,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望对大家...
    99+
    2024-04-02
  • 如何理解Angular中的组件通讯和依赖注入
    这篇文章给大家介绍如何理解Angular中的组件通讯和依赖注入,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。 Angular组件间怎么进行通讯?依赖注入是什...
    99+
    2024-04-02
  • android依赖注入的实现方式是什么
    Android中依赖注入的实现方式有以下几种:1. 构造函数注入:通过在类的构造函数中传入依赖对象的实例来实现注入。这种方式最为简单...
    99+
    2023-10-09
    android
  • laravel的依赖注入和控制反转怎么实现
    这篇文章主要介绍“laravel的依赖注入和控制反转怎么实现”,在日常操作中,相信很多人在laravel的依赖注入和控制反转怎么实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”laravel的依赖注入和控制...
    99+
    2023-06-30
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作