Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
Y
yifu-study-front-share
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
CI / CD
CI / CD
Pipelines
Schedules
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
yifu-study
front
yifu-study-front-share
Commits
4573ed34
Commit
4573ed34
authored
Nov 03, 2022
by
zhangfangli
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
vue中的Provide和Inject
parent
0bf24f1c
Pipeline
#17062
failed with stage
Changes
11
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
279 additions
and
0 deletions
+279
-0
vue中的Provide和Inject.md
source/_posts/vue中的Provide和Inject.md
+279
-0
Provide.png
source/images/Provide/Provide.png
+0
-0
Provide10.png
source/images/Provide/Provide10.png
+0
-0
Provide2.png
source/images/Provide/Provide2.png
+0
-0
Provide3.png
source/images/Provide/Provide3.png
+0
-0
Provide4.png
source/images/Provide/Provide4.png
+0
-0
Provide5.png
source/images/Provide/Provide5.png
+0
-0
Provide6.png
source/images/Provide/Provide6.png
+0
-0
Provide7.png
source/images/Provide/Provide7.png
+0
-0
Provide8.png
source/images/Provide/Provide8.png
+0
-0
Provide9.png
source/images/Provide/Provide9.png
+0
-0
No files found.
source/_posts/vue中的Provide和Inject.md
0 → 100644
View file @
4573ed34
---
title
:
vue中的Provide _ Inject
date
:
2022/10/31
tags
:
-
Provide
-
Inject
author
:
张芳利
---
<a
name=
"wqAvz"
></a>
# 介绍
在vue项目中我们会经常用到组件之间的相互传参,我们最常用的就是:
1.
prop(父组件像子组件传参)
2.
emits (子组件向父组件传参)
3.
vuex/pinia (组件间传参)
4.
provide/inject
5.
以及路由传参等
在对等级之间传参中,像是是父子,我们使用props是ok的,但是有一些深度嵌套的组件,而深层的子组件只需要父组件的部分内容。在这种情况下,如果仍然将 prop 沿着组件链逐级传递下去,可能会很麻烦。这里就要用到上面的第四种方法(provide/inject )了,其实vue2中就已经有这个概念了,但是这里讲的是以vue3为主的🌼~
<br
/>
Vue3.0 中,对于组件间的传参,我们可以使用一对 provide 和 inject。无论组件层次结构有多深,父组件都可以作为其所有子组件的数据提供者。父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来接受并使用它。
<br
/>
几点:
1.
provide和inject是成对出现的,
2.
用于父组件向子孙组件传递数据,
3.
provide在父组件中返回要传给下级的数据,inject在需要使用这个数据的子辈组件或者孙辈等下级组件中注入数据。
![
1.png
](
/yifu-study-front-share/images/Provide/Provide.png
)
<a
name=
"QgVpj"
></a>
# 基本使用
父组件中,引入provide
<br
/>
provide 函数有两个参数 provide(key, value)
1.
key:是一个字符串,用来标识这个数据
2.
value:是一个数据,可以是任意类型的数据,这个数据可以在任意子组件中通过inject函数获取到
例如:父组件
```
// 引入provide
import { provide,ref } from 'vue'
const msg = '我的父组件中的provide'
provide('msg',msg)
```
子组件中,引入inject
<br
/>
inject函数也有两个参数 inject(key, defaultValue)
<br
/>
key: 传入provide的key
<br
/>
defaultValue: 默认值,如果没有找到key,那就只用默认值的数据
<br
/>
子组件:
```
// 引入 inject
import { ref,inject,provide } from 'vue'
// 获取父组件中的provide
const msg = inject('msg','wahaha')
....
<h2>我是子组件</h2>
<p>子:{{msg}}</p>
```
孙组件:
```
import { ref,inject,provide } from 'vue'
const msg = inject('msg')
----------------
<h3>我是孙组件</h3>
<p>孙:{{msg}}</p>
```
![
image2.png
](
/yifu-study-front-share/images/Provide/Provide2.png
)
<br
/>
但是上面的代码有个问题:如果父组件里面的按钮改变msg 的文字,却发现,子组件和孙组件并没有跟着一起改变??
<a
name=
"gc2nC"
></a>
## 响应式
在vue3中,大家都知道下响应式的话是用ref或者reactive ,在这里也是一样的,给数据添加ref即可
<br
/>
父组件
```
// 引入provide
import { provide,ref } from 'vue'
// ref 创建响应式数据
const msg = ref('我是父组件的数据')
provide('msg',msg)
const changeFun = () => {
msg.value = '我是父组件的数据,我被改变了'
}
```
![
image3.png
](
/yifu-study-front-share/images/Provide/Provide3.png
)
这上面我们实现了父像子孙组件传递数据,那如过子孙组件想改变数据呢?
<a
name=
"UgGOz"
></a>
## 子孙组件改变数据
如果想子孙组件改变数据,很简单既然上面我们已经是响应式数据了,我们可以直接在子组件更改,然后赋值即可,就像父组件里面的流程一样
子组件:
```
import { ref,inject,provide } from 'vue'
const msg = inject('msg','wahaha')
const changeFun = () => {
msg.value = '我是子组件的数据,我被改变了'
}
provide('msg',msg)
```
![
image4.png
](
/yifu-study-front-share/images/Provide/Provide4.png
)
<br
/>
但是这样操作好像有点混乱,更好的做法应该是在父组件创建一个专门改变数据的方法,然后传递下去,让子孙组件去调用
<br
/>
父组件:
```
const changeMsg = (value) => {
msg.value = '我是msg,我被改变成'+ value
}
provide('changeMsg',changeMsg)
```
子孙组件:
```
import { ref,inject } from 'vue'
const msg = inject('msg','wahaha')
const changeMsg = inject('changeMsg')
const changeFun = () => {
changeMsg('娃哈哈')
}
```
通过上面的方法我们可以进行数据修改,但是有时候我们发现可能一不小心,还是会去在子孙组件中直接修改msg 的数据,改如何禁止呢?
<a
name=
"ZL8Wl"
></a>
## 子孙组件只使用
如何让子孙组件只用数据呢?而不会一不下更改数据。这里我们借助 readonly 只读属性
<br
/>
父组件:
```
// 引入provide
import { provide,ref,readonly } from 'vue'
// ref 创建响应式数据
const msg = ref('我是父组件的数据')
provide('msg',readonly(msg))
const changeFun = () => {
changeMsg('父级改变了')
}
const changeMsg = (value) => {
msg.value = '我是msg,我被改变成'+ value
}
provide('changeMsg',changeMsg)
```
子组件
```
import { ref,inject,provide } from 'vue'
const msg = inject('msg','wahaha')
const changeMsg = inject('changeMsg')
const changeFun = () => {
msg.value= 'haha'
//changeMsg('父级改变了')
}
provide('msg',msg) // 并不会被改变
```
我们发现点击的时候数据并不会改变,并且会出现一个警告:
<br
/>
![
image5.png
](
/yifu-study-front-share/images/Provide/Provide5.png
)
<a
name=
"VG2tK"
></a>
# 源码
通过上面的学习,我们已经能在项目中使用provide/inject了,现在让我们来看一下他们俩的源码是什么样的。
<br
/>
下载源码的地址:
[
https://github.com/vuejs
](
https://github.com/vuejs
)
,vue的源码是在core中。
<br
/>
provide/inject的源码地址:/core/packages/runtime-core/src>/apiInject.ts 中,因为ts 不是很熟悉,所以我这边转换为js 大家来看一下;
<a
name=
"FBuRi"
></a>
### provide
```
import { currentInstance } from './component';
export function provide(key, value) {
if (!currentInstance) {
if (__DEV__) {
warn(`provide() can only be used inside setup().`); // 不能在setup之外使用
}
}
else {
let provides = currentInstance.provides; // 获取当前实例的provides
// by default an instance inherits its parent's provides object
//默认情况下,实例继承其父对象的providers
// but when it needs to provide values of its own, it creates its
// 但当它需要提供自己的价值时,它就会创造自己的价值
// own provides object using parent provides object as prototype.
// 提供对象使用父提供对象作为原型。
// this way in `inject` we can simply look up injections from direct
// 这样,在“注入”中,我们可以简单地从直接查找注入
// parent and let the prototype chain do the work.
// 让原型链做工作。
const parentProvides = currentInstance.parent && currentInstance.parent.provides;
// 获取当前实例的父实例的provides
if (parentProvides === provides) {
// 如果当前实例的provides和父实例的provides相同
provides = currentInstance.provides = Object.create(parentProvides);
// Object.create(parentProvides) 会创建一个新对象,这个对象的原型是parentProvides
}
provides[key] = value;
}
}
```
源码里面我们看到
1.
若currentInstance无值即没有当前活动的实例,那开发环境会报警 且不能在setup之外使用
2.
取当前实例上的provides对象,再取父实例对象上的provides对象
3.
两者相比较,如果相等,则继承父实例对象上的provides
4.
赋值
provide API就是通过获取当前组件的实例对象,将传进来的数据存储在当前的组件实例对象上的provides上,并且通过ES6的新API Object.create把父组件的provides属性设置到当前的组件实例对象的provides属性的原型对象上。
<br
/>
我们再来看一下组件实例的 provides
<br
/>
![
image6.png
](
/yifu-study-front-share/images/Provide/Provide6.png
)
<br
/>
位置在:runtime-core/src/component.ts
<br
/>
![
image7.png
](
/yifu-study-front-share/images/Provide/Provide7.png
)
<br
/>
![
image8.png
](
/yifu-study-front-share/images/Provide/Provide8.png
)
<br
/>
从源码里面我们能看到 instance 中有两个属性,一个是parent,一个provides。在初始化的时候如果有父组件那就把父组件的provides赋值给当前实例,如果没有父组件,那就创建一个新的appContext(从父组件获取 appContext,如果没有,从 vnode.appContext 获取,如果没有,使用空的 appContext),并且把provides属性设置为新对象原型对象上的属性。
<a
name=
"CTtTq"
></a>
### inject
```
export function inject(key, defaultValue, treatDefaultAsFactory = false) {
// fallback to `currentRenderingInstance` so that this can be called in
// a functional component
const instance = currentInstance || currentRenderingInstance; // 获取当前实例
if (instance) {
// #2400
// to support `app.use` plugins,
// fallback to appContext's `provides` if the instance is at root
const provides = instance.parent == null // 如果当前实例没有父实例
? instance.vnode.appContext && instance.vnode.appContext.provides // 获取当前实例的appContext的provides
: instance.parent.provides; // 获取当前实例的父实例的provides
if (provides && key in provides) { // 如果provides存在并且key在provides中
// TS doesn't allow symbol as index type
return provides[key]; // 返回provides中key对应的值
}
else if (arguments.length > 1) { // 如果参数大于1
return treatDefaultAsFactory && isFunction(defaultValue) // treatDefaultAsFactory为true 并且defaultValue为函数
? defaultValue.call(instance.proxy) // 调用defaultValue函数
: defaultValue; // 否则返回defaultValue
}
else if (__DEV__) {
warn(`injection "${String(key)}" not found.`); // 如果没有找到key对应的值 报错
}
}
else if (__DEV__) {
warn(`inject() can only be used inside setup() or functional components.`); // 如果当前实例不存在 报错
}
}
```
从上面可以看出
1.
获取当前组件实例,判断是否有实例
2.
获取当前实例或父的provides对象,进行遍历
3.
如果provides存在并且key在provides中,返回provides中key对应的值
4.
如果没有则判断是否存在默认内容,默认内容如果是个函数,就执行并且通过call方法把组件实例的代理对象绑定到该函数,否则就直接返回默认内容。
以上总结:
<br
/>
整体来说就是利用原型和原型链进行数据的继承和获取。在provide 函数中,设置父级的provides为当前provides对象原型对象上的属性,在inject函数中,先找provides对象自身的属性,如果自身查找不到,则沿着原型链向上一个对象中去查找
<a
name=
"VoVPb"
></a>
# 额外知识点 Object.create()
**Object.create()**
方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。
<br
/>
Object.create(proto,
[
propertiesObject
]
)
-
proto:必须。新创建对象的原型对象,即通过 Object.create()生成的对象的原型 指向 proto(可以是 null、对象、函数的 prototype 属性)。(注:
**创建空原型的对象时需传 null , 否则会抛出 TypeError 异常**
)。
-
propertiesObjec:可选。添加到新创建对象的可枚举属性(即其自身的属性,而不是原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应 Object.defineProperties()的第二个参数。
-
返回值:一个新对象,带着指定的原型对象和属性。
因此可以看出 Object.create()的两个参数其实就是,proto 设置原型对象的属性和方法,propertiesObjec 设置新创建对象的属性和方法。而 propertiesObjec 创建属性方法的时候加入了属性可配置的形式,相当于简化并增强对象对象的写法,让其拥有对象字面量和 Object.defineProperties()的特性
```
const obj = {
name:'我是obj'
}
const obj2 = Object.create(obj)
console.log(obj2)
```
![
image9.png
](
/yifu-study-front-share/images/Provide/Provide9.png
)
<br
/>
第二个参数
```
const obj3 = Object.create(obj,{
age:{
value:18
}
})
console.log(obj3)
```
![
image10.png
](
/yifu-study-front-share/images/Provide/Provide10.png
)
source/images/Provide/Provide.png
0 → 100644
View file @
4573ed34
22.8 KB
source/images/Provide/Provide10.png
0 → 100644
View file @
4573ed34
27.1 KB
source/images/Provide/Provide2.png
0 → 100644
View file @
4573ed34
30.2 KB
source/images/Provide/Provide3.png
0 → 100644
View file @
4573ed34
25.4 KB
source/images/Provide/Provide4.png
0 → 100644
View file @
4573ed34
22.2 KB
source/images/Provide/Provide5.png
0 → 100644
View file @
4573ed34
5.04 KB
source/images/Provide/Provide6.png
0 → 100644
View file @
4573ed34
33.9 KB
source/images/Provide/Provide7.png
0 → 100644
View file @
4573ed34
25.3 KB
source/images/Provide/Provide8.png
0 → 100644
View file @
4573ed34
25.3 KB
source/images/Provide/Provide9.png
0 → 100644
View file @
4573ed34
25.5 KB
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment