【Vue3】provide/inject用法以及原理(持續更新)


簡介

provide可以向所有子孫組件提供數據以及提供修改數據的方法,子孫組件用inject使用數據。

使用方法

我們用一個例子來介紹provide, inject的使用。父組件的info, data數據傳遞給子組件以及孫子組件,同時父組件傳遞一個可以更新數據的方法。
父組件:

<template>
    <div>我是父組件 {{info}}
        <son></son>
    </div>
</template>

<script>
import { defineComponent, provide, reactive, ref } from 'vue'
import son from './son.vue'

export default defineComponent({
    setup() {
        let info = ref(123); // 注意這里響應式的必須是ref
        let datas = reactive({ // 這里這里復雜的數據的響應式必須是reactive
            width: 100,
            height: 50,
            bg: 'pink'
        });
        const updateInfo = (val) => {
            info.value = val;  // 注意這里,必須是info.value, 要不然數據不會改變
        }

        provide('info', info);
        provide('datas', datas)
        provide("updateInfo", updateInfo);

        return {
            info
        }
        
    },
    components: {
        son
    }
})
</script>

子組件:

<template>
    <div>我是子組件 {{info}}
        <grand-son></grand-son>
    </div>
</template>

<script>
import { defineComponent, inject } from 'vue'
import grandSon from './grandSon.vue';


export default defineComponent({
    setup() {
        const info = inject('info');
        
        return {
            info
        }
    },
    components: {
        grandSon
    }
})
</script>

孫子組件:

<template>
    <div>我是孫子組件 <button @click="updateInfo('456')">點擊更新info</button>{{info}}  <br />寬度{{datas.width}},  高度:{{datas.height}}, 背景: {{datas.bg}}</div>
</template>

<script>
import { defineComponent , inject, watch} from 'vue'


export default defineComponent({
    setup() {
        const info = inject('info');
        const updateInfo = inject('updateInfo');
        const datas = inject('datas');
        
        
        watch(info, (val) => {
            console.log('變了');
            console.log(val);
        });

        return {
            info,
            datas,
            updateInfo
        }
    },
})
</script>

注意點

**inject() can only be used inside setup() or functional components. **
inject()只能放在setup()生命周期里運行,不能放在別的周期里運行,也不能放在事件周期里運行。
1、這是允許的,因為console.log是setup()生命周期里的同步代碼。

function xx() {
  console.log(inject("abc"))
}
xx()

2、這是禁止的,因為setTimeout是異步函數,執行console.log(inject("abc"))的時候已經不在setup()生命周期里。

function xx() {
  setTimeout(() => {
    console.log(inject("abc"))
  })
}
xx()

3、讓例一的xx函數作為鼠標事件回調,也是禁止的,原因也一樣。
4、放在Promise.then()也是禁止的,比如:

Promise.resolve().then(() => {
  console.log(inject("abc"))
})

readonly

如果要確保通過 provide 傳遞的數據不會被注入的組件更改,可以對提供者的 property 使用 readonly。
比如父組件中的 let info = ref(123); 修改為info = readonly(123);

ref和reactive的區別

ref:由傳入值返回一個響應式的、可變的且只有value一個屬性的ref對象。
當ref被作為render context被返回,在template中使用該ref對象時,自動獲取內部的值,不需要使用.value屬性。
reactive:reactive的作用是將對象包裝成響應式對象——通過 Proxy代理后的對象。

如果只使用 reactive 的問題是,使用組合函數時必須始終保持對這個所返回對象的引用以保持響應性。這個對象不能被解構或展開。

// 組合函數:
function useMousePosition() {
  const pos = reactive({
    x: 0,
    y: 0,
  })

  // ...
  return pos
}
// 消費組件
export default {
  setup() {
    // 這里會丟失響應性!
    const { x, y } = useMousePosition()
    return {
      x,
      y,
    }

    // 這里會丟失響應性!
    return {
      ...useMousePosition(),
    }

    // 這是保持響應性的唯一辦法!
    // 你必須返回 `pos` 本身,並按 `pos.x` 和 `pos.y` 的方式在模板中引用 x 和 y。
    return {
      pos: useMousePosition(),
    }
  },
}



toRefs

toRefs API 用來上面的問題——它將響應式對象的每個 property 都轉成了相應的 ref。

function useMousePosition() {
  const pos = reactive({
    x: 0,
    y: 0,
  })

  // ...
  return toRefs(pos)
}

// x & y 現在是 ref 形式,可以i解構了!
const { x, y } = useMousePosition()

provide/inject原理(待補充)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM