亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue3中的依賴注入與組件定義怎么實現

發布時間:2023-03-22 09:17:46 來源:億速云 閱讀:99 作者:iii 欄目:編程語言

本篇內容主要講解“Vue3中的依賴注入與組件定義怎么實現”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Vue3中的依賴注入與組件定義怎么實現”吧!

provide() & inject()

provide()

提供一個值,可以被后代組件注入。

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

接收兩個參數:

  • 要注入的 key,字符串或者 Symbol

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 /*#__PURE__*/ 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 = /* #__PURE__ */ 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
): Component
type 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: [`/* inlined css */`],
})
// 注冊自定義元素
// 注冊之后,所有此頁面中的 `<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中的依賴注入與組件定義怎么實現”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

鸡东县| 郁南县| 罗山县| 涡阳县| 武邑县| 西贡区| 西宁市| 永登县| 毕节市| 高唐县| 乌什县| 峨眉山市| 温州市| 桦川县| 麻城市| 德惠市| 昭觉县| 五大连池市| 伊川县| 温宿县| 中西区| 天津市| 遂平县| 揭东县| 苍溪县| 平利县| 理塘县| 遵义市| 山东省| 武义县| 怀安县| 瓦房店市| 旅游| 班戈县| 犍为县| 穆棱市| 谢通门县| 信阳市| 禄劝| 涟源市| 札达县|