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

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

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

實現(xiàn)思路

場景分析

可以在全局父組件里通過provide將所有需要對外提供的全局屬性方法進(jìn)行跨組件透傳,無論嵌套多深的子組件都可以進(jìn)行inject注入使用,包括不限于計算屬性、方法等,甚至將整個app.vue實例進(jìn)行相應(yīng)的透傳;

測試用例

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

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

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

2.給組件實例拓展provides屬性,類型是對象類型

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

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

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

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

源碼實現(xiàn)

步驟解析

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

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)建組件實例
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ù) 對象-將對象返回的對象注入到這個組件上下文中
    const setupResult = setup(shallowReadonly(instance.props),{
      emit: instance.emit
    });

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

組件實例拓展provides和parent屬性

export function createComponentInstance(vnode,parent) {
  const component = {
    vnode,
    type: vnode.type,
    props: {},
    slots: {},
    isMounted: false, // 標(biāo)識是否是初次加載還是后續(xù)依賴數(shù)據(jù)的更新操作
    subTree: null,
    emit: ()=>{},
    provides:{}, //常規(guī)的provide 無法實現(xiàn)跨級的父子組件provide和inject
    parent, //子組件獲取到父組件實例 取得父組件中 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)建組件實例
  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ā)生變化時組件可以實現(xiàn)更新操作,需要將整個渲染任務(wù)放入到Effect中進(jìn)行收集
  effect(() => {
    setupComponent(insatnce); //處理setup的信息 初始化props  初始化Slots等
    setupRenderEffect(insatnce, initialVNode, container); // 首次調(diào)用App組件時會執(zhí)行  并將render函數(shù)的this綁定為創(chuàng)建的代理對象

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

檢測parent是否配置成功

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

實現(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)前組件的實例
    if(currentInstance){

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

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

在進(jìn)行provides提供時,優(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)識是否是初次加載還是后續(xù)依賴數(shù)據(jù)的更新操作
    subTree: null,
    emit: ()=>{},
    // provides:{}, //常規(guī)的provide 無法實現(xiàn)跨級的父子組件provide和inject
    provides:parent?parent.provides:{}, 
    // 實現(xiàn)跨級父子組件之間的provide和inject
    //相當(dāng)于是一個容器 當(dāng)調(diào)用 provide 的時候會往這個容器里存入數(shù)據(jù) 供子組件的數(shù)據(jù)讀取
    parent, //子組件獲取到父組件實例 取得父組件中 provide 的數(shù)據(jù)
    render: vnode.render,
    setupState: {},
  };
  component.emit = emit.bind(null,component) as any
  return component;
}

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

父組件沒有自己維護(hù)的provides對象,導(dǎo)致只保存做外部的組件provides數(shù)據(jù),so每個組件都應(yīng)該維護(hù)一個專屬于自己的provides屬性供子組件使用,當(dāng)父組件中有值則直接返回,沒有則繼續(xù)向父組件的父組件進(jìn)行循環(huán)查找,直到到達(dá)頂層組件,頂層組件也沒有時則采用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)前組件實例的provides指向一個空對象 且該對象以父組件的 provides 為原型
        // currentInstance.provides = Object.create(parentProvides)
        // 上述注釋的邏輯存在的問題是每次調(diào)用provide時都會將組件實例的provides置為空對象,導(dǎo)致以前提供的數(shù)據(jù)被清空
        // 所以清空邏輯只適合在首次加載時進(jìn)行調(diào)用 而首次加載即是組件實例的provides是初始化父組件實例的provides 此時可以進(jìn)行初始化
        if(provides === parentProvides){
            provides = currentInstance.provides = Object.create(parentProvides)
            // Object.create可以理解為繼承一個對象,添加的屬性是在原型下 此處是將父組件的provides屬性設(shè)置到當(dāng)前組件實例對象的provides屬性的原型對象上
        }
        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)前組件的實例
    if(currentInstance){

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

測試用例

// 省略父組件邏輯...
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}`)
    }
}

拓展

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

原型和原型鏈

prototype和__proto__

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

原型鏈

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

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

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

相關(guān)文章

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

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

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

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

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

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

    這篇文章主要為大家詳細(xì)介紹了vue.js devtools遇到問題的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    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 分頁組件封裝的實例代碼

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

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

    詳解基于Vue-cli搭建的項目如何和后臺交互

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

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

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

    Vue3屬性綁定方法解析

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

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

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

最新評論

石台县| 石泉县| 娱乐| 沁阳市| 阿拉善右旗| 喀什市| 绍兴市| 康保县| 五台县| 乌兰县| 山阴县| 淅川县| 鹤峰县| 杨浦区| 秀山| 称多县| 阿坝县| 远安县| 土默特左旗| 南城县| 涡阳县| 乌恰县| 微博| 海盐县| 大邑县| 滦平县| 清水河县| 车致| 吉首市| 岳普湖县| 巧家县| 郎溪县| 崇阳县| 霍州市| 巴楚县| 保亭| 高碑店市| 宁国市| 新竹市| 云林县| 平凉市|