亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Vue3實(shí)現(xiàn)provide/inject的示例詳解

 更新時(shí)間:2022年11月22日 16:17:37   作者:FE雜志社  
Vue3 的 Provide / Inject 的實(shí)現(xiàn)原理其實(shí)就是巧妙利用了原型和原型鏈來(lái)實(shí)現(xiàn)的。本文將通過(guò)示例為大家介紹下provide/inject的具體實(shí)現(xiàn),需要的可以參考一下

實(shí)現(xiàn)思路

場(chǎng)景分析

可以在全局父組件里通過(guò)provide將所有需要對(duì)外提供的全局屬性方法進(jìn)行跨組件透?jìng)鳎瑹o(wú)論嵌套多深的子組件都可以進(jìn)行inject注入使用,包括不限于計(jì)算屬性、方法等,甚至將整個(gè)app.vue實(shí)例進(jìn)行相應(yīng)的透?jìng)鳎?/p>

測(cè)試用例

// example/apiinject/App.js
import { h, provide, inject } from "../../lib/guide-mini-vue.esm.js";

const Provider = {
    name: 'Provider',
    setup(){
        provide("foo","fooVal");
        provide("bar","barVal");
        
    },
    render() {
        return h("div",{},[
            h("hr",{},""),
            h("p",{},"Provider"),
            h("hr",{},""),
            h(Provider2)]
        )
    }
}

const Provider2 = {
    name: 'Provider2',
    setup(){
        provide("foo","Provider2-foo");
        const foo = inject("foo","default Provider2-foo")

        return{
            foo
        }
    },
    render() {
        return h("div",{},[
            h("p",{},`Provider2 foo:${this.foo}`),
            h("hr",{},""),
            h(Consumer)]
        )
    }
}

const Consumer = {
    name: "Consumer",
    setup() {
        const foo = inject("foo");
        const bar = inject("bar");
        const bars = inject("bars",'bares default');
        const barfn = inject("barss",() => 'bares default fn');

        return {
            foo,
            bar,
            bars,
            barfn
        }
    },
    render(){
        return h("div",{},`Consumer: - ${this.foo} - ${this.bar} - ${this.bars} - ${this.barfn}`)
    }
}

export default {
    name: "App",
    setup() {},
    render() {
        return h("div",{},[
            h("h1",{},"apiInject - apiProvide"),
            h(Provider)
        ])
    }
}
// example/apiinject/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>API-Inject+provide</title>
</head>
<body>
    <div id="app"></div>
    <!-- <script src="main.js" type="module"></script> -->
    <script type="module">
      import { createApp } from "../../lib/guide-mini-vue.esm.js" 
      import App from "./App.js";

      const rootContainer = document.querySelector("#app");
      createApp(App).mount(rootContainer);
    </script>
</body>
</html>

渲染結(jié)果

步驟解析

當(dāng)調(diào)用provide的時(shí)候需要將provide中的key/value掛載到組件實(shí)例的provides屬性上,當(dāng)子組件調(diào)用Inject的時(shí)候,通過(guò)獲取到子組件的實(shí)例進(jìn)而通過(guò)parent得到父組件實(shí)例,然后通過(guò)父組件實(shí)例上的provides對(duì)象獲取到相應(yīng)key對(duì)應(yīng)的value

1.實(shí)現(xiàn)getCurrentInstance函數(shù)

用于獲取組件實(shí)例實(shí)例,父組件掛載provide到實(shí)例上的provides屬性對(duì)象上,子組件獲取到自身和父組件實(shí)例,然后獲取對(duì)應(yīng)的屬性值

2.給組件實(shí)例拓展provides屬性,類型是對(duì)象類型

  • 保存父組件提供的provide數(shù)據(jù)
  • 供子組件獲取指定數(shù)據(jù)

3.給組件實(shí)例拓展parent屬性,用于獲取父組件實(shí)例

子組件獲取父組件實(shí)例,然后獲取對(duì)應(yīng)數(shù)據(jù)

4.創(chuàng)建并實(shí)現(xiàn)provide/Inject函數(shù)

源碼實(shí)現(xiàn)

步驟解析

拓展實(shí)現(xiàn)getCurrentInstance,用于獲取組件實(shí)例

let currentInstance = null
export function getCurrentInstance(){
  return currentInstance
}

export function setCurrentInstance(instance){
  // 方便后續(xù)跟蹤 currentInstance 被誰(shuí)更改  - 斷點(diǎn)調(diào)試  中間層概念
  currentInstance = instance
}

export function setupComponent(instance) {
  // 處理setup的信息 初始化props  初始化Slots等
  initProps(instance,instance.vnode.props),
  initSlots(instance,instance.vnode.children),
  setupStatefulComponent(instance);
}

// 調(diào)用創(chuàng)建組件實(shí)例
function setupStatefulComponent(instance: any) {
  // 調(diào)用組件的setup
  // const Component = instance.vNode.type
  const Component = instance.type;
  instance.proxy = new Proxy(
    { _: instance },
    PublicInstanceProxyHandlers
    // {
    //     get(target,key){
    //         const { setupState } = instance
    //         if(key in setupState){
    //             return setupState[key]
    //         }

    //         if(key === '$el'){
    //             return instance.vnode.el
    //         }
    //     }
    // }
  );
  const { setup } = Component;

  if (setup) {
    // currentInstance = instance
    setCurrentInstance(instance)
    // setup可以返回函數(shù)或?qū)ο?函數(shù)-是組件的render函數(shù) 對(duì)象-將對(duì)象返回的對(duì)象注入到這個(gè)組件上下文中
    const setupResult = setup(shallowReadonly(instance.props),{
      emit: instance.emit
    });

    // currentInstance = null
    setCurrentInstance(null)
    // setup返回當(dāng)前組件的數(shù)據(jù)
    handleSetupResult(instance, setupResult);
  }
}

組件實(shí)例拓展provides和parent屬性

export function createComponentInstance(vnode,parent) {
  const component = {
    vnode,
    type: vnode.type,
    props: {},
    slots: {},
    isMounted: false, // 標(biāo)識(shí)是否是初次加載還是后續(xù)依賴數(shù)據(jù)的更新操作
    subTree: null,
    emit: ()=>{},
    provides:{}, //常規(guī)的provide 無(wú)法實(shí)現(xiàn)跨級(jí)的父子組件provide和inject
    parent, //子組件獲取到父組件實(shí)例 取得父組件中 provide 的數(shù)據(jù)
    render: vnode.render,
    setupState: {},
  };
  component.emit = emit.bind(null,component) as any
  return component;
}

按照createComponentInstance的新添屬性parent進(jìn)行舊邏輯兼容

function processComponent(vnode: any, container: any, parentComponent) {
  // 掛載組件
  mountComponent(vnode, container, parentComponent);
}
function mountComponent(initialVNode: any, container, parentComponent) {
  // 通過(guò)虛擬節(jié)點(diǎn)創(chuàng)建組件實(shí)例
  const insatnce = createComponentInstance(initialVNode,parentComponent);
  const { data } = insatnce.type

  // 通過(guò)data函數(shù)獲取原始數(shù)據(jù),并調(diào)用reactive函數(shù)將其包裝成響應(yīng)式數(shù)據(jù)
  // const state = reactive(data())

  // 為了使得自身狀態(tài)值發(fā)生變化時(shí)組件可以實(shí)現(xiàn)更新操作,需要將整個(gè)渲染任務(wù)放入到Effect中進(jìn)行收集
  effect(() => {
    setupComponent(insatnce); //處理setup的信息 初始化props  初始化Slots等
    setupRenderEffect(insatnce, initialVNode, container); // 首次調(diào)用App組件時(shí)會(huì)執(zhí)行  并將render函數(shù)的this綁定為創(chuàng)建的代理對(duì)象

  })
}
// ...
export function render(vnode, container) {
  // 調(diào)用patch函數(shù) 方便進(jìn)行后續(xù)遞歸處理
  patch(vnode, container, null);
}
// ...
// 省略其他兼容邏輯,如patch、mountElement、processElement、mountChildren等

檢測(cè)parent是否配置成功

export function createComponentInstance(vnode,parent) {
  console.log(parent,'parent=========')
  // ...
}

實(shí)現(xiàn)provide和Inject

import { getCurrentInstance } from "./component";

export function provide (key,value){
    const currentInstance:any = getCurrentInstance(); //在在setup中
    if(currentInstance){
        let { provides } = currentInstance
        provides[key] = value
    }
}

export function inject (key,defaultValue){
    const currentInstance:any = getCurrentInstance(); //在在setup中  獲取當(dāng)前組件的實(shí)例
    if(currentInstance){

        // 獲取到父組件的實(shí)例上的provides 然后根據(jù)inject的key值進(jìn)行查找對(duì)應(yīng)的值并返回
        const parentProvides = currentInstance.parent.provides
        return parentProvides[key]
        
    }
}

跨級(jí)組件數(shù)據(jù)傳遞

在進(jìn)行provides提供時(shí),優(yōu)先讀取父組件的provide數(shù)據(jù)

export function createComponentInstance(vnode,parent) {
  console.log(parent,'parent=========')
  const component = {
    vnode,
    type: vnode.type,
    props: {},
    slots: {},
    isMounted: false, // 標(biāo)識(shí)是否是初次加載還是后續(xù)依賴數(shù)據(jù)的更新操作
    subTree: null,
    emit: ()=>{},
    // provides:{}, //常規(guī)的provide 無(wú)法實(shí)現(xiàn)跨級(jí)的父子組件provide和inject
    provides:parent?parent.provides:{}, 
    // 實(shí)現(xiàn)跨級(jí)父子組件之間的provide和inject
    //相當(dāng)于是一個(gè)容器 當(dāng)調(diào)用 provide 的時(shí)候會(huì)往這個(gè)容器里存入數(shù)據(jù) 供子組件的數(shù)據(jù)讀取
    parent, //子組件獲取到父組件實(shí)例 取得父組件中 provide 的數(shù)據(jù)
    render: vnode.render,
    setupState: {},
  };
  component.emit = emit.bind(null,component) as any
  return component;
}

利用原型鏈思想解決父組件和爺爺組件都provide相同key值的數(shù)據(jù)時(shí)的覆蓋問(wèn)題 -> 只會(huì)讀到最上層

父組件沒(méi)有自己維護(hù)的provides對(duì)象,導(dǎo)致只保存做外部的組件provides數(shù)據(jù),so每個(gè)組件都應(yīng)該維護(hù)一個(gè)專屬于自己的provides屬性供子組件使用,當(dāng)父組件中有值則直接返回,沒(méi)有則繼續(xù)向父組件的父組件進(jìn)行循環(huán)查找,直到到達(dá)頂層組件,頂層組件也沒(méi)有時(shí)則采用Inject配置的默認(rèn)值進(jìn)行渲染

在進(jìn)行組件專屬provides維護(hù)

export function provide (key,value){
    const currentInstance:any = getCurrentInstance(); //在在setup中
    if(currentInstance){
        let { provides } = currentInstance
        const parentProvides = currentInstance.parent.provides

        // 讓當(dāng)前組件實(shí)例的provides指向一個(gè)空對(duì)象 且該對(duì)象以父組件的 provides 為原型
        // currentInstance.provides = Object.create(parentProvides)
        // 上述注釋的邏輯存在的問(wèn)題是每次調(diào)用provide時(shí)都會(huì)將組件實(shí)例的provides置為空對(duì)象,導(dǎo)致以前提供的數(shù)據(jù)被清空
        // 所以清空邏輯只適合在首次加載時(shí)進(jìn)行調(diào)用 而首次加載即是組件實(shí)例的provides是初始化父組件實(shí)例的provides 此時(shí)可以進(jìn)行初始化
        if(provides === parentProvides){
            provides = currentInstance.provides = Object.create(parentProvides)
            // Object.create可以理解為繼承一個(gè)對(duì)象,添加的屬性是在原型下 此處是將父組件的provides屬性設(shè)置到當(dāng)前組件實(shí)例對(duì)象的provides屬性的原型對(duì)象上
        }
        provides[key] = value
    }
}

Inject默認(rèn)值問(wèn)題

Inject函數(shù)的第二參數(shù)即為默認(rèn)值

默認(rèn)值可以是函數(shù)或者普通值

export function inject (key,defaultValue){
    const currentInstance:any = getCurrentInstance(); //在在setup中  獲取當(dāng)前組件的實(shí)例
    if(currentInstance){

        // 獲取到父組件的實(shí)例上的provides 然后根據(jù)inject的key值進(jìn)行查找對(duì)應(yīng)的值并返回
        const parentProvides = currentInstance.parent.provides
        if(key in parentProvides){
            return parentProvides[key]
        } else if(defaultValue){
            // 支持inject的默認(rèn)值 當(dāng)父組件中沒(méi)有提供數(shù)據(jù)時(shí)進(jìn)行采取默認(rèn)值  默認(rèn)值可以是一個(gè)函數(shù)或者普通值
            if(typeof defaultValue === 'function'){
                return defaultValue()
            }
            return defaultValue
        }
        
    }
}

測(cè)試用例

// 省略父組件邏輯...
const Consumer = {
    name: "Consumer",
    setup() {
        const foo = inject("foo");
        const bar = inject("bar");
        const bars = inject("bars",'bares default');
        const barfn = inject("barss",() => 'bares default fn');

        return {
            foo,
            bar,
            bars,
            barfn
        }
    },
    render(){
        return h("div",{},`Consumer: - ${this.foo} - ${this.bar} - ${this.bars} - ${this.barfn}`)
    }
}

拓展

實(shí)現(xiàn)原理是利用了原型和原型鏈來(lái)進(jìn)行數(shù)據(jù)的繼承和獲取

原型和原型鏈

prototype和__proto__

prototype一般是顯式原型,__proto__一般稱為隱式原型,一個(gè)函數(shù)在創(chuàng)建之后,就會(huì)擁有一個(gè)名為prototype的屬性,這個(gè)屬性表示函數(shù)的原型對(duì)象;

原型鏈

當(dāng)訪問(wèn)一個(gè)JS對(duì)象屬性的時(shí)候,JS會(huì)先在這個(gè)對(duì)象定義的屬性上查找,找不到會(huì)沿著這個(gè)對(duì)象的__proto__這個(gè)隱式原型關(guān)聯(lián)起來(lái)的鏈條向上一個(gè)對(duì)象查找,這個(gè)鏈條就叫做原型鏈;

原型鏈在某種意義上是讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法

以上就是Vue3實(shí)現(xiàn)provide/inject的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Vue3 provide inject的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 如何測(cè)量vue應(yīng)用運(yùn)行時(shí)的性能

    如何測(cè)量vue應(yīng)用運(yùn)行時(shí)的性能

    這篇文章主要介紹了如何測(cè)量vue應(yīng)用運(yùn)行時(shí)的性能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下
    2019-06-06
  • vue實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)和參數(shù)傳遞的兩種方式

    vue實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)和參數(shù)傳遞的兩種方式

    這篇文章主要介紹了vue頁(yè)面跳轉(zhuǎn)和參數(shù)傳遞的兩種方式,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09
  • Vue中使用ECharts與v-if的問(wèn)題和解決方案

    Vue中使用ECharts與v-if的問(wèn)題和解決方案

    在Vue項(xiàng)目中使用v-if指令控制ECharts圖表顯示時(shí),可能會(huì)遇到圖表無(wú)法正常渲染或顯示錯(cuò)誤的問(wèn)題,下面這篇文章主要介紹了Vue中使用ECharts與v-if的問(wèn)題和解決方案,需要的朋友可以參考下
    2024-10-10
  • Vue3中reactive函數(shù)toRef函數(shù)ref函數(shù)簡(jiǎn)介

    Vue3中reactive函數(shù)toRef函數(shù)ref函數(shù)簡(jiǎn)介

    這篇文章主要介紹了Vue3中的三種函數(shù),分別對(duì)reactive函數(shù)toRef函數(shù)以及ref函數(shù)原理及使用作了簡(jiǎn)單介紹,有需要的朋友可以借鑒參考下
    2021-09-09
  • 使用FileReader API創(chuàng)建Vue文件閱讀器組件

    使用FileReader API創(chuàng)建Vue文件閱讀器組件

    這篇文章主要介紹了使用FileReader API創(chuàng)建一個(gè)Vue的文件閱讀器組件,需要的朋友可以參考下
    2018-04-04
  • Vue package.json配置深入分析

    Vue package.json配置深入分析

    這篇文章主要介紹了Vue package.json配置,package.json是每個(gè)前端項(xiàng)目都會(huì)有的json文件,位于項(xiàng)目的根目錄中。很多腳手架在創(chuàng)建項(xiàng)目的時(shí)候會(huì)幫我們自動(dòng)初始化好 package.json
    2023-01-01
  • vue自定義指令之面板拖拽的實(shí)現(xiàn)

    vue自定義指令之面板拖拽的實(shí)現(xiàn)

    這篇文章主要介紹了vue自定義指令之面板拖拽的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • vue中created、watch和computed的執(zhí)行順序詳解

    vue中created、watch和computed的執(zhí)行順序詳解

    由于vue的雙向數(shù)據(jù)綁定,自動(dòng)更新數(shù)據(jù)的機(jī)制,在數(shù)據(jù)變化后,對(duì)此數(shù)據(jù)依賴?的所有數(shù)據(jù),watch事件都會(huì)被更新、觸發(fā),下面這篇文章主要給大家介紹了關(guān)于vue中created、watch和computed的執(zhí)行順序,需要的朋友可以參考下
    2022-11-11
  • vue 項(xiàng)目代碼拆分的方案

    vue 項(xiàng)目代碼拆分的方案

    這篇文章主要介紹了vue 項(xiàng)目代碼拆分的方案,幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下
    2021-03-03
  • vue中g(shù)et請(qǐng)求如何傳遞數(shù)組參數(shù)的方法示例

    vue中g(shù)et請(qǐng)求如何傳遞數(shù)組參數(shù)的方法示例

    這篇文章主要介紹了vue中g(shù)et請(qǐng)求如何傳遞數(shù)組參數(shù)的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11

最新評(píng)論