provide
Required オプションとinject
一緒に使用すると、祖先コンポーネントがそのすべての子孫に依存関係を注入できるようになり、コンポーネント階層がどれほど深くても、上流と下流の関係が確立されると常に有効になります。
1. Provide/inject の使用法を簡単に確認してみましょう
次のように:
var Provider = {
provide: {
foo: "bar"
}
}
var Child = {
inject: ["foo"],
created() {
console.log(this.foo); // "bar"
}
}
ES5 Symbol
キーとして使用する場合は、次のように使用されます。
const s = Symbol();
var Provider = {
provide() {
return {
[s]: "bar"
}
}
}
var Child = {
inject: {
s },
created() {
console.log(this.foo); // "bar"
}
}
data/props
注入された値には次の場所でアクセスできます。
var Provider = {
provide: {
foo: "bar",
foo2: "bar2"
}
}
var Child = {
inject: ["foo"],
props: {
bar: {
default() {
return this.foo;
}
}
},
data() {
return {
bar2: this.foo2
}
}
}
設定できるinject
デフォルト値:
var Provider = {
provide: {
foo: "bar"
}
}
var Child = {
inject: {
foo: {
default: "foo"}
}
}
別の名前のプロパティから注入する必要がある場合は、from
ソース プロパティを示すために使用します。
var Provider = {
provide: {
foo: "bar"
}
}
var Child = {
inject: {
foo: {
from: "var",
default: "foo"
}
}
}
2. インジェクションの内部原理
inject
の前に初期化されdata/props
、provide
の後に初期化されましたdata/props
。この目的は、挿入されたコンテンツをのユーザーがdata/props
利用できるようにすることです。inject
つまり、data/props
depend するためにはinject
、初期化をinject
初期化の前に置く必要がありますdata/props
。
まず、初期化inject
関数を定義します。
export function initInjections (vm: Component) {
// 通过用户配置的inject,自底向上搜索可用的注入内容,并将搜索结果返回
const result = resolveInject(vm.$options.inject, vm)
if (result) {
toggleObserving(false) // 通知defineReactive不要将内容转换成响应式
Object.keys(result).forEach(key => {
defineReactive(vm, key, result[key])
})
toggleObserving(true)
}
}
resolveInject
関数の役割はユーザーによって構成されinject
、利用可能な注入コンテンツを下から上に検索し、検索結果を返します。そしてtoggleObserving(false)
その役割は、コンテンツをレスポンシブに変換しないようにdefineReactiveに通知することです。
それで、resolveInject
どうやって?この関数を実装する主なアイデアは、現在のコンポーネントでユーザーがinject
設定した値を読み取りkey
、現在のコンポーネントから親コンポーネントまでkey
各値をループしkey
て値があるかどうかを確認し、ループを停止することです。見つかった場合は、最後にkey
対応するすべての値を変換して、一緒に返すことができます。
export function resolveInject (inject: any, vm: Component): ?Object {
if (inject) {
const result = Object.create(null)
/**
* hasSymbol:是否支持Symbol
*/
const keys = hasSymbol
? Reflect.ownKeys(inject)
: Object.keys(inject)
return result
}
}
最初のステップは、結果を保存するオブジェクトを定義しresult
、結果をinject
読み出すことですkey
。Symbol
サポートされている場合は読み取りを使用し、Reflect.ownKeys(inject)
サポートされていない場合は読み取りを使用しますObject.keys(inject)
。
ただし、inject
次のような配列形式が使用されている場合、配列形式がサポートされることがわかっています。
var Provider = {
provide: {
foo: "bar"
}
}
var Child = {
inject: ["foo"],
created() {
console.log(this.foo); // "bar"
}
}
inject
中身をすべて正しく読み取ることはできないのでしょうかkey
?
実際、Vue.js
インスタンス化の最初のステップは、ユーザーによって渡されたデータを正規化することです。inject
渡されたコンテンツが配列の場合、データはオブジェクトに正規化され、from
プロパティに格納されます。
ユーザーがinject
次のように設定されている場合:
{
inject: ["foo"]
}
正規化後は次のようになります。
{
inject: {
foo: {
from: "foo"
}
}
}
次に、ループする必要がありますkey
。key
現在のコンポーネントから開始して、親コンポーネントからの値があるかどうかを確認し続けます。見つかった場合はループを停止し、最後に対応する値をすべてまとめて返しますkey
。
export function resolveInject (inject: any, vm: Component): ?Object {
if (inject) {
const result = Object.create(null)
/**
* hasSymbol:是否支持Symbol
*/
const keys = hasSymbol
? Reflect.ownKeys(inject)
: Object.keys(inject)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
/*
* 通过from属性得到provide源属性
* 当Vue.js被实例化时,会在上下文(this)中添加$options属性,这会把inject中提供的数据规格化,包括inject
* 用户设置: { inject: [foo] }
* 规格化:{ inject: { foo: { from: "foo" }}}
*/
const provideKey = inject[key].from
let source = vm // 一开始为当前实例
// 自底向上寻找provide源属性
while (source) {
if (source._provided && hasOwn(source._provided, provideKey)) {
result[key] = source._provided[provideKey]
break
}
source = source.$parent // 向上寻找
}
}
return result
}
}
上記のコードでは、最も外側の層は for ループ キーを使用してループ本体でキーを順番に取得し、inject[key].from
provide source 属性を取得しますprovideKey
。次に、ソース プロパティを介して while ループを使用してコンテンツを検索します。最初はsource
、現在のコンポーネント インスタンスです。対応する値が元のプロパティで見つかった場合は、それを に設定しsource
、break を使用してループを終了します。それ以外の場合は、次のサイクルでソースを親コンポーネント インスタンスに設定します。_provided
result
インジェクションが提供されない場合provide
、inject のデフォルト設定が使用されます。コードは次のとおりです。
export function resolveInject (inject: any, vm: Component): ?Object {
if (inject) {
const result = Object.create(null)
/**
* hasSymbol:是否支持Symbol
*/
const keys = hasSymbol
? Reflect.ownKeys(inject)
: Object.keys(inject)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
/*
* 通过from属性得到provide源属性
* 当Vue.js被实例化时,会在上下文(this)中添加$options属性,这会把inject中提供的数据规格化,包括inject
* 用户设置: { inject: [foo] }
* 规格化:{ inject: { foo: { from: "foo" }}}
*/
const provideKey = inject[key].from
let source = vm // 一开始为当前实例
// 自底向上寻找provide源属性
while (source) {
if (source._provided && hasOwn(source._provided, provideKey)) {
result[key] = source._provided[provideKey]
break
}
source = source.$parent // 向上寻找
}
// 没有source,设置默认值
if (!source) {
if ('default' in inject[key]) {
const provideDefault = inject[key].default
// 支持函数和普通字符串
result[key] = typeof provideDefault === 'function'
? provideDefault.call(vm)
: provideDefault
} else if (process.env.NODE_ENV !== 'production') {
warn(`Injection "${
key}" not found`, vm)
}
}
}
return result
}
}
ループ終了後にsource
空の場合は、provide
対応する値の注入が行われていないと判断できるため、この際、inject
で設定したデフォルト値を読み込む必要があります。そうであれば'default' in inject[key]
、デフォルトが設定されていることを証明し、そうでない場合は、本番環境で警告が出力されます。inject[key].default
readとなっていますprovideDefault
が、デフォルト値は関数と通常の文字列をサポートしています このときprovideDefault
関数かどうかを判断する必要があります 関数であれば実行して結果を result に格納します そうでない場合は直接 result に格納しprovideDefault
ます。
3. 完全なコードは次のとおりです。
/**
* 通过用户配置的inject,自底向上搜索可用的注入内容,并将搜索结果返回
* 当使用provide注入内容时,其实是将内容注入到当前组件实例的_provide中,所以inject可以从父组件实例的_provide中获取注入的内容
*/
export function resolveInject (inject: any, vm: Component): ?Object {
if (inject) {
// inject is :any because flow is not smart enough to figure out cached
const result = Object.create(null)
/**
* hasSymbol:是否支持Symbol
*/
const keys = hasSymbol
? Reflect.ownKeys(inject)
: Object.keys(inject)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
// #6574 in case the inject object is observed...
if (key === '__ob__') continue
/*
* 通过from属性得到provide源属性
* 当Vue.js被实例化时,会在上下文(this)中添加$options属性,这会把inject中提供的数据规格化,包括inject
* 用户设置: { inject: [foo] }
* 规格化:{ inject: { foo: { from: "foo" }}}
*/
const provideKey = inject[key].from
let source = vm // 一开始为当前实例
// 自底向上寻找provide源属性
while (source) {
if (source._provided && hasOwn(source._provided, provideKey)) {
result[key] = source._provided[provideKey]
break
}
source = source.$parent // 向上寻找
}
// 没有source,设置默认值
if (!source) {
if ('default' in inject[key]) {
const provideDefault = inject[key].default
// 支持函数和普通字符串
result[key] = typeof provideDefault === 'function'
? provideDefault.call(vm)
: provideDefault
} else if (process.env.NODE_ENV !== 'production') {
warn(`Injection "${
key}" not found`, vm)
}
}
}
return result
}
}