最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

場(chǎng)景分析

可以在全局父組件里通過provide將所有需要對(duì)外提供的全局屬性方法進(jìn)行跨組件透?jìng)?,無論嵌套多深的子組件都可以進(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í)候,通過獲取到子組件的實(shí)例進(jìn)而通過parent得到父組件實(shí)例,然后通過父組件實(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 被誰更改  - 斷點(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 無法實(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) {
  // 通過虛擬節(jié)點(diǎn)創(chuàng)建組件實(shí)例
  const insatnce = createComponentInstance(initialVNode,parentComponent);
  const { data } = insatnce.type

  // 通過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 無法實(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í)的覆蓋問題 -> 只會(huì)讀到最上層

父組件沒有自己維護(hù)的provides對(duì)象,導(dǎo)致只保存做外部的組件provides數(shù)據(jù),so每個(gè)組件都應(yīng)該維護(hù)一個(gè)專屬于自己的provides屬性供子組件使用,當(dāng)父組件中有值則直接返回,沒有則繼續(xù)向父組件的父組件進(jìn)行循環(huán)查找,直到到達(dá)頂層組件,頂層組件也沒有時(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)
        // 上述注釋的邏輯存在的問題是每次調(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)值問題

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)父組件中沒有提供數(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)原理是利用了原型和原型鏈來進(jìn)行數(shù)據(jù)的繼承和獲取

原型和原型鏈

prototype和__proto__

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

原型鏈

當(dāng)訪問一個(gè)JS對(duì)象屬性的時(shí)候,JS會(huì)先在這個(gè)對(duì)象定義的屬性上查找,找不到會(huì)沿著這個(gè)對(duì)象的__proto__這個(gè)隱式原型關(guān)聯(lián)起來的鏈條向上一個(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)文章

  • Vue3中操作ref的四種使用方式示例代碼(建議收藏)

    Vue3中操作ref的四種使用方式示例代碼(建議收藏)

    這篇文章主要介紹了Vue3中操作ref的四種使用方式示例代碼(建議收藏),本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04
  • Vue動(dòng)態(tài)綁定Class的幾種常用方式

    Vue動(dòng)態(tài)綁定Class的幾種常用方式

    在vue框架開發(fā)中,有時(shí)候我們需要對(duì)元素的樣式進(jìn)行動(dòng)態(tài)控制,比如tab按鈕的切換,下面這篇文章主要給大家介紹了關(guān)于Vue動(dòng)態(tài)綁定Class的幾種常用方式,需要的朋友可以參考下
    2023-03-03
  • 分享vue.js devtools遇到一系列問題

    分享vue.js devtools遇到一系列問題

    這篇文章主要為大家詳細(xì)介紹了vue.js devtools遇到問題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • vue3模塊創(chuàng)建runtime-dom源碼解析

    vue3模塊創(chuàng)建runtime-dom源碼解析

    這篇文章主要為大家介紹了vue3模塊創(chuàng)建runtime-dom源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • vue的@change的用法及操作代碼

    vue的@change的用法及操作代碼

    @change 是 Vue.js 中用于監(jiān)聽表單元素值變化的事件處理器,這篇文章主要介紹了vue的@change的用法,需要的朋友可以參考下
    2023-10-10
  • vue 基于element-ui 分頁組件封裝的實(shí)例代碼

    vue 基于element-ui 分頁組件封裝的實(shí)例代碼

    這篇文章主要介紹了vue 基于element-ui 分頁組件封裝的實(shí)例代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-12-12
  • 詳解基于Vue-cli搭建的項(xiàng)目如何和后臺(tái)交互

    詳解基于Vue-cli搭建的項(xiàng)目如何和后臺(tái)交互

    這篇文章主要介紹了詳解基于Vue-cli搭建的項(xiàng)目如何和后臺(tái)交互,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-06-06
  • 使用vux實(shí)現(xiàn)上拉刷新功能遇到的坑

    使用vux實(shí)現(xiàn)上拉刷新功能遇到的坑

    最近公司在研發(fā)app,選擇了基于Vue框架的vux組件庫,現(xiàn)總結(jié)在實(shí)現(xiàn)上拉刷新功能遇到的坑,感興趣的朋友參考下吧
    2018-02-02
  • Vue3屬性綁定方法解析

    Vue3屬性綁定方法解析

    這篇文章主要介紹了Vue3屬性綁定方法解析,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09
  • vue實(shí)現(xiàn)圖書管理系統(tǒng)

    vue實(shí)現(xiàn)圖書管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)圖書管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-12-12

最新評(píng)論

紫阳县| 大埔区| 和硕县| 盘山县| 修水县| 兖州市| 巴东县| 密山市| 河津市| 庆阳市| 福贡县| 项城市| 文安县| 庄浪县| 金沙县| 高安市| 鞍山市| 石阡县| 连云港市| 客服| 安顺市| 西丰县| 甘洛县| 石楼县| 天门市| 星座| 修水县| 清涧县| 洪雅县| 科技| 温州市| 富源县| 兴和县| 襄樊市| 昌都县| 白城市| 吉首市| 雅安市| 武定县| 乌什县| 区。|