1 はじめに
この記事では、ライフサイクルの初期化フェーズで呼び出される 5 番目の初期化関数を紹介しますinitState
。関数名からするとインスタンスの状態を初期化する関数のようですが、インスタンスの状態とは何でしょうか?前回の記事で少し触れましたが、日常の開発では、、、、、Vue
などのオプションがコンポーネントに記述されますが、これらのオプションをインスタンスのステータス オプションと呼びます。言い換えれば、この関数はこれらの状態を初期化するために使用されるため、次に、関数がこれらの状態オプションをどのように初期化するかを分析します。props
data
methods
computed
watch
initState
2. initState関数の解析
initState
まず、関数を分析しましょう。関数の定義はsrc/core/instance/state.js
、次のようにソース コードにあります。
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {
}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
ご覧のとおり、この関数のコードはそれほど多くなく、ロジックは非常に明確です。
_watchers
まず、現在のインスタンス内のすべてのインスタンスを保存するために、新しい属性がインスタンスに追加されますwatcher
。登録されたインスタンスであってもvm.$watch
、オプションを使用して登録されたwatcher
インスタンスであっても、この属性に保存されます。watch
watcher
ここでもう少し詳しく説明します。変更検出の章では、Vue
属性インターセプトを使用してデータ変更の検出が実装されていることを紹介しました。ただし、属性インターセプトはVue
すべてのデータの変更を検出するために使用されるわけではありません。これは、データが多ければ多いほど、これが原因です。つまり、より多くの依存関係がデータにバインドされることになり、依存関係の追跡に大きなメモリ オーバーヘッドが発生するためVue 2.0
、このバージョンからはVue
すべてのデータが検出されなくなりますが、検出の粒度が変更されます。コンポーネント レベルと各コンポーネントが検出されるため、このコンポーネントで使用されるすべての状態の依存関係を保存するために、新しい属性が各コンポーネントに追加されますvm._watchers
。状態の 1 つが変更されると、コンポーネントに通知され、内部で仮想DOM
データ比較が使用してメモリのオーバーヘッドを削減し、パフォーマンスを向上させます。
ソース コードに戻り続けます。次のステップでは、インスタンス内にどのオプションがあるかを判断し、次のように、対応するオプション初期化サブ関数を呼び出して初期化します。
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {
}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
まず、インスタンスにオプションがあるかどうかを確認しprops
、オプションがある場合は、props
オプション初期化関数を呼び出してオプションinitProps
を初期化します。props
次に、インスタンスにオプションがあるかどうかを確認しmethods
、ある場合は、methods
オプション初期化関数を呼び出してオプションinitMethods
を初期化します。methods
data
次に、インスタンスにオプションがあるかどうかを確認し、オプションがある場合は、data
オプション初期化関数を呼び出してオプションをinitData
初期化し、そうでない場合は空のオブジェクトとして扱い、応答性の高いオブジェクトに変換します。data
data
computed
次に、インスタンスにオプションがあるかどうかを確認し、ある場合は、computed
オプション初期化関数を呼び出してオプションinitComputed
を初期化します。computed
最後に、インスタンスにオプションがあるかどうかを確認しwatch
、オプションがある場合は、watch
オプション初期化関数を呼び出してオプションinitWatch
を初期化します。watch
つまり、1 つの文は次のとおりです。オプションがある場合は、対応するオプション初期化サブ関数を呼び出してオプションを初期化します。
上記はinitState
関数のロジックのすべてですが、実際、関数内で 5 つのオプションが初期化されると、それらの順序は無計画ではなく意図的に配置されていることがわかります。開発中に inと観察とin をdata
使用できることに気づいた場合は、これができる理由は、初期化中に、最初に初期化、次に初期化、そして最後に初期化という順序に従っているためです。props
watch
data
props
props
data
watch
次に、これら 5 つのステータス オプションに対応する 5 つの初期化サブ関数を 1 つずつ分析し、内部でどのように初期化されるかを確認します。
3. 小道具を初期化する
props
オプションは通常、現在のコンポーネントの親コンポーネントによって渡されます。親コンポーネントが子コンポーネントを呼び出すと、通常、props
次のように、属性値がラベル属性として子コンポーネントのラベルに追加されます。
<Child prop1="xxx" prop2="yyy"></Child>
前回の記事で初期化イベントinitEvents
関数を紹介したとき、テンプレートがコンパイルされるとき、コンポーネント タグが解析されるときに、すべてのタグ属性が解析され、サブコンポーネントが次の場合にサブコンポーネントに渡されると述べました。もちろん、ここにはprops
データが含まれます。
子コンポーネント内ではprops
親コンポーネントからのデータをオプションで受け取りますが、受け取る際は次のように記述できます。
// 写法一
props: ['name']
// 写法二
props: {
name: String, // [String, Number]
}
// 写法三
props: {
name:{
type: String
}
}
Vue
ユーザーに提供されるprops
オプションは非常に自由な記述であることがわかりますVue
。規約によれば、記述方法はたくさんありますが、最終処理では 1 つの記述方法のみが処理されます。このとき、必ず正規化することを考えることになります。処理前のデータを処理すると、すべての書き込みメソッドが処理され、すべて 1 つの書き込みメソッドに変換されます。正規化されたイベントと同様に、属性を結合するときにprops
データも正規化されます。
3.1 正規化されたデータ
props
データ正規化関数の定義は、次のようにソース コードにありますsrc/core/util/options.js
。
function normalizeProps (options, vm) {
const props = options.props
if (!props) return
const res = {
}
let i, val, name
if (Array.isArray(props)) {
i = props.length
while (i--) {
val = props[i]
if (typeof val === 'string') {
name = camelize(val)
res[name] = {
type: null }
} else if (process.env.NODE_ENV !== 'production') {
warn('props must be strings when using array syntax.')
}
}
} else if (isPlainObject(props)) {
for (const key in props) {
val = props[key]
name = camelize(key)
res[name] = isPlainObject(val)
? val
: {
type: val }
}
} else if (process.env.NODE_ENV !== 'production') {
warn(
`Invalid value for option "props": expected an Array or an Object, ` +
`but got ${
toRawType(props)}.`,
vm
)
}
options.props = res
}
上記コードではまずインスタンス内のオプションを取得しますがprops
、存在しない場合は直接返します。
const props = options.props
if (!props) return
res
存在する場合は、最終結果を格納する空のオブジェクトを定義します。次に、props
オプションが配列であるかどうかを確認し (書き込み方法 1)、配列内の各要素を調べます。要素が文字列の場合は、まず要素をキャメル ケース名に変換し、次に要素を , Stored in; if としてkey
使用します{type: null}
。要素が文字列ではない場合、例外がスローされます。次のように:value
res
if (Array.isArray(props)) {
i = props.length
while (i--) {
val = props[i]
if (typeof val === 'string') {
name = camelize(val)
res[name] = {
type: null }
} else if (process.env.NODE_ENV !== 'production') {
warn('props must be strings when using array syntax.')
}
}
}
オプションが配列でない場合はprops
、引き続きオブジェクトかどうかを判断します。オブジェクトの場合は、オブジェクト内のキー値の各ペアを走査します。キー値の各ペアを取得した後、最初にキー名を次のように変換します。キャメル ケースの名前付け。次に、値がオブジェクトであるかどうかを判断します。値がオブジェクトの場合 (書き方 3)、キーと値のペアは に格納されます。値がオブジェクトでない場合 (書き方 2) res
、キー名はとして保存されkey
ます。次のように:{type: null}
value
res
if (isPlainObject(props)) {
for (const key in props) {
val = props[key]
name = camelize(key)
res[name] = isPlainObject(val)
? val
: {
type: val }
}
}
オプションが配列でもオブジェクトでもない場合props
、非運用環境では例外がスローされ、オプションはres
正規化された結果としてインスタンスに再割り当てされますprops
。次のように:
if (process.env.NODE_ENV !== 'production') {
warn(
`Invalid value for option "props": expected an Array or an Object, ` +
`but got ${
toRawType(props)}.`,
vm
)
}
options.props = res
以上がprops
データの正規化ですが、3つの書き込み方法のどれを使っても最終的には以下のような書き込み方法に変換されることが分かります。
props: {
name:{
type: xxx
}
}
3.2 initProps 関数の分析
オプションを標準化した後props
、実際にprops
オプションを初期化できます。initProps
関数の定義はsrc/core/instance/state.js
、次のようにソース コードにあります。
function initProps (vm: Component, propsOptions: Object) {
const propsData = vm.$options.propsData || {
}
const props = vm._props = {
}
// cache prop keys so that future props updates can iterate using Array
// instead of dynamic object key enumeration.
const keys = vm.$options._propKeys = []
const isRoot = !vm.$parent
// root instance props should be converted
if (!isRoot) {
toggleObserving(false)
}
for (const key in propsOptions) {
keys.push(key)
const value = validateProp(key, propsOptions, propsData, vm)
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
const hyphenatedKey = hyphenate(key)
if (isReservedAttribute(hyphenatedKey) ||
config.isReservedAttr(hyphenatedKey)) {
warn(
`"${
hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
vm
)
}
defineReactive(props, key, value, () => {
if (vm.$parent && !isUpdatingChildComponent) {
warn(
`Avoid mutating a prop directly since the value will be ` +
`overwritten whenever the parent component re-renders. ` +
`Instead, use a data or computed property based on the prop's ` +
`value. Prop being mutated: "${
key}"`,
vm
)
}
})
} else {
defineReactive(props, key, value)
}
// static props are already proxied on the component's prototype
// during Vue.extend(). We only need to proxy props defined at
// instantiation here.
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
}
toggleObserving(true)
}
ご覧のとおり、この関数は、現在のVue
インスタンスと現在のインスタンスの正規化されたオプションという2 つのパラメータを受け取りますprops
。
まず、関数内で次の 4 つの変数が定義されます。
const propsData = vm.$options.propsData || {
}
const props = vm._props = {
}
const keys = vm.$options._propKeys = []
const isRoot = !vm.$parent
props
propsData:親コンポーネントによって渡される実際のデータ。- props:変数に設定されたすべてのプロパティが保存される
vm._props
ポインター。props
vm._props
vm.$options._propKeys
キー:キャッシュ オブジェクトprops
内で指されるポインタ。key
将来更新するときは、配列をprops
走査するだけですべてのキーを取得できます。vm.$options._propKeys
props
key
- isRoot: 現在のコンポーネントがルート コンポーネントであるかどうか。
次に、現在のコンポーネントがルート コンポーネントであるかどうかを確認します。そうでない場合は、データをレスポンシブなコンポーネントprops
に変換するかどうかを制御するために使用される配列をレスポンシブなコンポーネントに変換する必要はありません。toggleObserving(false)
次のように:
if (!isRoot) {
toggleObserving(false)
}
次に、props
オプションを調べてキー値の各ペアを取得し、最初にキー名を に追加しkeys
、次にvalidateProp
関数 (この関数は以下で紹介します) を呼び出して、親コンポーネントによって渡されたデータ型が一致するかどうかを確認しprops
、受信したデータを取得します。 value . 次に、次のように関数を使用しvalue
てキーと値を(つまり)に追加します。defineReactive
props
vm._props
for (const key in propsOptions) {
keys.push(key)
const value = validateProp(key, propsOptions, propsData, vm)
if (process.env.NODE_ENV !== 'production') {
const hyphenatedKey = hyphenate(key)
if (isReservedAttribute(hyphenatedKey) ||
config.isReservedAttr(hyphenatedKey)) {
warn(
`"${
hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
vm
)
}
defineReactive(props, key, value, () => {
if (vm.$parent && !isUpdatingChildComponent) {
warn(
`Avoid mutating a prop directly since the value will be ` +
`overwritten whenever the parent component re-renders. ` +
`Instead, use a data or computed property based on the prop's ` +
`value. Prop being mutated: "${
key}"`,
vm
)
}
})
} else {
defineReactive(props, key, value)
}
}
追加後、key
現在のインスタンスに存在するかどうかを判断しvm
、存在しない場合はproxy
関数を呼び出して属性とみなしたvm
コードを設定し、データにアクセスする際に実際にアクセスしているのは です。次のように:key
vm[key]
vm._props[key]
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
以上がinitProps
関数のロジックですが、次に親コンポーネントで渡されたデータ型が一致するかどうかを確認し、渡された値を取得するvalidateProp
関数の使い方を見てみましょう。props
3.3 validateProp 関数の分析
validateProp
関数の定義はsrc/core/util/props.js
、次のようにソース コードにあります。
export function validateProp (key,propOptions,propsData,vm) {
const prop = propOptions[key]
const absent = !hasOwn(propsData, key)
let value = propsData[key]
// boolean casting
const booleanIndex = getTypeIndex(Boolean, prop.type)
if (booleanIndex > -1) {
if (absent && !hasOwn(prop, 'default')) {
value = false
} else if (value === '' || value === hyphenate(key)) {
// only cast empty string / same name to boolean if
// boolean has higher priority
const stringIndex = getTypeIndex(String, prop.type)
if (stringIndex < 0 || booleanIndex < stringIndex) {
value = true
}
}
}
// check default value
if (value === undefined) {
value = getPropDefaultValue(vm, prop, key)
// since the default value is a fresh copy,
// make sure to observe it.
const prevShouldObserve = shouldObserve
toggleObserving(true)
observe(value)
toggleObserving(prevShouldObserve)
}
if (process.env.NODE_ENV !== 'production') {
assertProp(prop, key, value, vm, absent)
}
return value
}
ご覧のとおり、この関数は次の 4 つのパラメーターを受け取ります。
- key:
propOptions
走査中に取得された各属性名。 - propOptions: 現在のインスタンスの正規化されたオプション
props
。 props
propsData:親コンポーネントによって渡される実際のデータ。- vm: 現在のインスタンス。
まず、関数内で次の 3 つの変数が定義されます。
const prop = propOptions[key]
const absent = !hasOwn(propsData, key)
let value = propsData[key]
- prop:現在
key
にあるpropOptions
対応する値。 - missing: に現在存在する
key
かどうかpropsData
、つまり、親コンポーネントがこのプロパティに渡したかどうか。 - value: の現在対応する値、つまり、この属性の親コンポーネントによって渡される実際の値
key
。propsData
次に、属性がブール型 (Boolean) であるprop
かどうかを判断します。この関数は、属性に特定の型が存在するかどうかを判断するために使用されます。存在する場合は、属性内の型のインデックスを返します (属性は配列である可能性があるため) ). 存在しない場合は、-1 を返します。type
getTypeIndex
prop
type
type
type
ブール型の場合、個別に処理する必要がある 2 つのエッジ ケースがあります。
-
absent
の場合、true
つまり、親コンポーネントがこの属性を渡さずprop
、属性にデフォルト値がない場合はfalse
、次のように属性値を に設定します。if (absent && !hasOwn(prop, 'default')) { value = false }
-
親コンポーネントがこの
prop
プロパティを渡す場合は、次の点を満たす必要があります。- 属性値が空の文字列であるか、属性値が属性名と等しい場合。
prop
タイプが属性type
に存在しませんString
。- 属性に型がある
prop
場合、属性の型のインデックスは型のインデックスより小さくなければなりません。つまり、型の優先順位が高くなります。type
String
Boolean
type
String
Boolean
次に、次のように属性値を に設定します
true
。if (value === '' || value === hyphenate(key)) { const stringIndex = getTypeIndex(String, prop.type) if (stringIndex < 0 || booleanIndex < stringIndex) { value = true } }
また、属性値と属性名が等しいと判断した場合は、まず属性名をキャメルケースから
-
連結文字列に変換し、以下の記述方法ではサブコンポーネントprop
を としますtrue
。<Child name></Child> <Child name="name"></Child> <Child userName="user-name"></Child>
ブール型ではなく、それ以外の型の場合は親コンポーネントに属性が渡されているかどうかだけを判断すればよく、渡されていない場合は属性値がこの時に関数を呼び出します(この関数は後述します
undefined
)getPropDefaultValue
。次のように、属性のデフォルト値を取得し、それを応答性の高い値に変換します。if (value === undefined) { value = getPropDefaultValue(vm, prop, key) // since the default value is a fresh copy, // make sure to observe it. const prevShouldObserve = shouldObserve toggleObserving(true) observe(value) toggleObserving(prevShouldObserve) }
親コンポーネントがこの属性を渡し、対応する実際の値を持っている場合、非運用環境では、
assertProp
属性値が必要な型と一致するかどうかを確認する関数が呼び出されます (この関数については以下で紹介します)。次のように:if (process.env.NODE_ENV !== 'production' ) { assertProp(prop, key, value, vm, absent) }
最後に、親コンポーネントによって渡されたプロパティの実際の値が返されます。
3.4 getPropDefaultValue 関数の分析
getPropDefaultValue
関数の定義はsrc/core/util/props.js
、次のようにソース コードにあります。
function getPropDefaultValue (vm, prop, key){
// no default, return undefined
if (!hasOwn(prop, 'default')) {
return undefined
}
const def = prop.default
// warn against non-factory defaults for Object & Array
if (process.env.NODE_ENV !== 'production' && isObject(def)) {
warn(
'Invalid default value for prop "' + key + '": ' +
'Props with type Object/Array must use a factory function ' +
'to return the default value.',
vm
)
}
// the raw prop value was also undefined from previous render,
// return previous default value to avoid unnecessary watcher trigger
if (vm && vm.$options.propsData &&
vm.$options.propsData[key] === undefined &&
vm._props[key] !== undefined
) {
return vm._props[key]
}
// call factory function for non-Function types
// a value is Function if its prototype is function even across different execution context
return typeof def === 'function' && getType(prop.type) !== 'Function'
? def.call(vm)
: def
}
この関数は、次の 3 つのパラメータを受け取ります。
- vm: 現在のインスタンス。
- prop:サブコンポーネントオプション
props
内のkey
対応する各値。 props
キー:各サブコンポーネントのオプションkey
。
props
その機能は、サブコンポーネントのオプションに基づいて、key
対応するデフォルト値を取得することです。
最初に属性があるprop
かどうかを確認し、属性がない場合はデフォルト値がないことを意味し、それが直接返されます。default
次のように:
if (!hasOwn(prop, 'default')) {
return undefined
}
存在する場合は、属性を取り出してdefault
変数に割り当てますdef
。次に、それが非本番環境のdef
オブジェクトであるかどうかが判断され、そうであれば、オブジェクトまたは配列のデフォルト値はファクトリ関数から取得する必要があるという警告がスローされます。次のように:
const def = prop.default
// warn against non-factory defaults for Object & Array
if (process.env.NODE_ENV !== 'production' && isObject(def)) {
warn(
'Invalid default value for prop "' + key + '": ' +
'Props with type Object/Array must use a factory function ' +
'to return the default value.',
vm
)
}
次に、親コンポーネントが属性を渡さないがprops
、vm._props
の属性値を持つ場合、vm._props
次のように、 の属性値がデフォルト値であると判断されます。
if (vm && vm.$options.propsData &&
vm.$options.propsData[key] === undefined &&
vm._props[key] !== undefined
) {
return vm._props[key]
}
最後に、def
それが関数であるかどうかをprop.type
判断しますFunction
。関数である場合は、def
オブジェクトまたは配列を返すファクトリ関数であることを示し、関数の戻り値はデフォルト値として返されます。関数でない場合は、関数の戻り値がデフォルト値として返されますdef
。関数を使用すると、それがdef
デフォルト値として返されます。次のように:
return typeof def === 'function' && getType(prop.type) !== 'Function'
? def.call(vm)
: def
3.5assertProp関数の解析
assertProp
関数の定義はsrc/core/util/props.js
、次のようにソース コードにあります。
function assertProp (prop,name,value,vm,absent) {
if (prop.required && absent) {
warn(
'Missing required prop: "' + name + '"',
vm
)
return
}
if (value == null && !prop.required) {
return
}
let type = prop.type
let valid = !type || type === true
const expectedTypes = []
if (type) {
if (!Array.isArray(type)) {
type = [type]
}
for (let i = 0; i < type.length && !valid; i++) {
const assertedType = assertType(value, type[i])
expectedTypes.push(assertedType.expectedType || '')
valid = assertedType.valid
}
}
if (!valid) {
warn(
`Invalid prop: type check failed for prop "${
name}".` +
` Expected ${
expectedTypes.map(capitalize).join(', ')}` +
`, got ${
toRawType(value)}.`,
vm
)
return
}
const validator = prop.validator
if (validator) {
if (!validator(value)) {
warn(
'Invalid prop: custom validator check failed for prop "' + name + '".',
vm
)
}
}
}
この関数は、次の 5 つのパラメータを受け取ります。
- 小道具:
prop
オプション; - 名前:
props
中央のprop
オプションkey
; - value:親コンポーネントによって
propsData
渡されるkey
、対応する実際のデータ。 - vm: 現在のインスタンス。
- missing: に現在存在する
key
かどうかpropsData
、つまり、親コンポーネントがこのプロパティに渡したかどうか。
その機能は、親コンポーネントによって渡された実際の値が型と一致するかどうかを検証することでありprop
、type
一致しない場合、非実稼働環境では警告がスローされます。
関数内でprop
、必須の項目が設定されており (prop.required
つまりtrue
)、親コンポーネントが属性を渡さない場合、その項目が必須であることを示す警告がスローされます。次のように:
if (prop.required && absent) {
warn(
'Missing required prop: "' + name + '"',
vm
)
return
}
そして、その品物が不要であり、品物の価値がvalue
存在しない場合には、現時点では合法であると判断され、直接返品されます。次のように:
if (value == null && !prop.required) {
return
}
次に、次の 3 つの変数が定義されます。
let type = prop.type
let valid = !type || type === true
const expectedTypes = []
- タイプ:
prop
を入力します。type
- valid: 検証が成功したかどうか。
- ExpectedTypes: 期待されるタイプの配列を保存します。検証が失敗して警告がスローされると、ユーザーは属性がどのようなタイプであると予想されるかを尋ねられます。
通常、type
ネイティブ コンストラクター、または複数の型を含む配列にすることができます。それ以外の場合、このプロパティは設定できません。ユーザーがネイティブ コンストラクターまたは配列を設定する場合、vaild
デフォルトはfalse
( !type
) です。ユーザーがこの属性を設定しない場合、つまり検証が必要ない場合、vaild
デフォルトは ( ) 、true
つまり検証は成功します。
また、type
に等しい場合はtrue
、 と書かれており、検証が成功したことをprops:{name:true}
意味します。prop
したがって、この構文が表示されるとき、現時点ではtype === true
、vaild
デフォルトは ですtrue
。
次に、型を確認します。ユーザーがtype
属性を設定した場合は、その属性が配列であるかどうかが判断され、そうでない場合は、次のように後続の処理を容易にするために配列に変換されます。
if (type) {
if (!Array.isArray(type)) {
type = [type]
}
}
次に、type
配列を反復処理し、assertType
関数 verifyを呼び出しますvalue
。assertType
関数の検証後、次のようにオブジェクトが返されます。
{
vaild:true, // 表示是否校验成功
expectedType:'Boolean' // 表示被校验的类型
}
次に、次のように、検証されたタイプを に追加しexpectedTypes
、vaild
変数を に設定します。assertedType.valid
for (let i = 0; i < type.length && !valid; i++) {
const assertedType = assertType(value, type[i])
expectedTypes.push(assertedType.expectedType || '')
valid = assertedType.valid
}
ここで注意してください: 上記のループの条件ステートメントには次のような条件があります:!vaild
つまり、type
配列内に成功する検証がもう 1 つあり、ループはただちに終了し、検証が合格したことを示します。
次に、ループが完了した後にvaild
検証false
が失敗したことを意味し、警告がスローされます。次のように:
if (!valid) {
warn(
`Invalid prop: type check failed for prop "${
name}".` +
` Expected ${
expectedTypes.map(capitalize).join(', ')}` +
`, got ${
toRawType(value)}.`,
vm
)
return
}
さらに、prop
このオプションは次のようなカスタム検証関数もサポートしています。
props:{
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
したがって、ユーザーが渡したカスタム検証関数を使用してデータを検証することも必要です。まず、ユーザーによって渡された検証関数が取得され、その関数が呼び出され、検証対象のデータが渡されます。検証が失敗した場合は、警告がスローされます。次のように:
const validator = prop.validator
if (validator) {
if (!validator(value)) {
warn(
'Invalid prop: custom validator check failed for prop "' + name + '".',
vm
)
}
}
4. メソッドの初期化
初期化は比較的単純で、その初期化関数の定義は次のようにmethods
ソース コードにあります。src/core/instance/state.js
function initMethods (vm, methods) {
const props = vm.$options.props
for (const key in methods) {
if (process.env.NODE_ENV !== 'production') {
if (methods[key] == null) {
warn(
`Method "${
key}" has an undefined value in the component definition. ` +
`Did you reference the function correctly?`,
vm
)
}
if (props && hasOwn(props, key)) {
warn(
`Method "${
key}" has already been defined as a prop.`,
vm
)
}
if ((key in vm) && isReserved(key)) {
warn(
`Method "${
key}" conflicts with an existing Vue instance method. ` +
`Avoid defining component methods that start with _ or $.`
)
}
}
vm[key] = methods[key] == null ? noop : bind(methods[key], vm)
}
}
コードからわかるように、初期化では次のmethods
3 つのことだけが行われます。名前の文字が命名規則に従っていませんか? 存在し、仕様を満たしている場合は、インスタンスにマウントします。次に、ソース コードを 1 行ずつ分析し、これら 3 つのことを確認していきます。method
method
method
vm
まず、methods
オプション内の各オブジェクトを走査し、非運用環境でmethods
メソッドが 1 つだけkey
存在value
するかどうかを判断します。つまり、メソッド名のみが存在し、メソッド本体が存在しない場合は、例外がスローされます。メソッドが未定義であることをユーザーに伝えます。次のように:
if (methods[key] == null) {
warn(
`Method "${
key}" has an undefined value in the component definition. ` +
`Did you reference the function correctly?`,
vm
)
}
methods
そして、あるメソッド名があるprops
属性名と同じであると判断し、メソッド名が重複していることをユーザに通知する例外をスローします。次のように:
if (props && hasOwn(props, key)) {
warn(
`Method "${
key}" has already been defined as a prop.`,
vm
)
}
methods
そして、あるメソッド名がvm
インスタンス内にすでに存在し、そのメソッド名が_
またはで始まる場合には$
例外がスローされ、メソッド名が標準化されていないことをユーザーに通知します。次のように:
if ((key in vm) && isReserved(key)) {
warn(
`Method "${
key}" conflicts with an existing Vue instance method. ` +
`Avoid defining component methods that start with _ or $.`
)
}
このうち、文字列がまたはisReserved
で始まるかどうかを判定するために使用される関数です。_
$
最後に、上記の判断がすべて正しければ、次のように、method
それを インスタンス にバインドして、 options のメソッドにアクセスできるvm
ようにします。this.xxx
methods
xxx
vm[key] = methods[key] == null ? noop : bind(methods[key], vm)
5. データの初期化
初期化も比較的簡単で、その初期化関数の定義は次のようにdata
ソース コードにあります。src/core/instance/state.js
function initData (vm) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {
}
if (!isPlainObject(data)) {
data = {
}
process.env.NODE_ENV !== 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html##data-Must-Be-a-Function',
vm
)
}
// proxy data on instance
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while (i--) {
const key = keys[i]
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {
warn(
`Method "${
key}" has already been defined as a data property.`,
vm
)
}
}
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' && warn(
`The data property "${
key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) {
proxy(vm, `_data`, key)
}
}
// observe data
observe(data, true /* asRootData */)
}
ご覧のとおり、initData
関数のロジックは複雑ではなく、initMethods
関数のロジックにある程度似ています。data
ユーザーから渡されたオプションが正当であるかどうかを一連の条件によって判断し、最終的data
に応答に変換してインスタンスにバインドしますvm
。コードのロジックを詳しく見てみましょう。
まず、ユーザーから渡されたオプションを取得しdata
、変数に代入しdata
、その変数をdata
ポインタとして指しvm._data
、関数かどうかを判定しdata
、関数であればgetData
その関数を呼び出して戻り値を取得し、に保存しますvm._data
。そうでない場合は、それ自体を に保存しますvm._data
。次のように:
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {
}
渡されたオプションが関数であるかどうかに関係なくdata
、その最終値はオブジェクトである必要があることはわかっていますが、オブジェクトでない場合は、data
オブジェクトである必要があることをユーザーに求める警告がスローされます。次のように:
if (!isPlainObject(data)) {
data = {
}
process.env.NODE_ENV !== 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html##data-Must-Be-a-Function',
vm
)
}
data
次に、オブジェクト内の各項目が調べられ、非運用環境では、data
オブジェクト内の項目の属性名が重複しているかどうかが判断されますkey
。重複がある場合は、警告がスローされ、ユーザーに次のことを促します。属性名が重複しています。methods
次のように:
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {
warn(
`Method "${
key}" has already been defined as a data property.`,
vm
)
}
}
key
次に、特定の項目prop
に属性名が重複しているかどうかを判断し、重複がある場合は、属性名が重複していることをユーザーに通知する警告をスローします。次のように:
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' && warn(
`The data property "${
key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
}
重複がない場合は、関数が呼び出されて、 またはインスタンスで始まらないオブジェクト内のプロパティをproxy
プロキシするため、options 内のデータにアクセスできるようになります。次のように:data
key
_
$
vm
this.xxx
data
xxx
if (!isReserved(key)) {
proxy(vm, `_data`, key)
}
最後に、次のようにobserve
関数を呼び出してdata
データを応答に変換します。
observe(data, true /* asRootData */)
6. 計算結果の初期化
計算プロパティcomputed
には誰もが精通していると思いますし、日々の開発で頻繁に使用されることは間違いありません。また、計算プロパティには優れた機能があることもわかっています。計算プロパティの結果はキャッシュされ、依存関係がない限り更新されません。応答プロパティの変更、計算。次に、計算プロパティがこれらの関数をどのように実装するかを見てみましょう。
6.1 使用方法を確認する
まず、公式ドキュメントの使用例に従って、次のように計算プロパティの使用法を確認しましょう。
var vm = new Vue({
data: {
a: 1 },
computed: {
// 仅读取
aDouble: function () {
return this.a * 2
},
// 读取和设置
aPlus: {
get: function () {
return this.a + 1
},
set: function (v) {
this.a = v - 1
}
}
}
})
vm.aPlus // => 2
vm.aPlus = 3
vm.a // => 2
vm.aDouble // => 4
ご覧のとおり、computed
オプションの属性値は関数にすることができ、getter
その関数のデフォルトはデータの読み取りのみに使用される値ゲッターになります。また、値ゲッターgetter
と値ストアを持つオブジェクトにすることもできますsetter
。データの取得と設定の読み取りに使用されます。
6.2 initComputed関数の解析
計算プロパティの使用法を理解した後、initComputed
計算プロパティの初期化関数の内部原理を分析しましょう。initComputed
関数の定義はsrc/core/instance/state.js
、次のようにソース コードにあります。
function initComputed (vm: Component, computed: Object) {
const watchers = vm._computedWatchers = Object.create(null)
const isSSR = isServerRendering()
for (const key in computed) {
const userDef = computed[key]
const getter = typeof userDef === 'function' ? userDef : userDef.get
if (process.env.NODE_ENV !== 'production' && getter == null) {
warn(
`Getter is missing for computed property "${
key}".`,
vm
)
}
if (!isSSR) {
// create internal watcher for the computed property.
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)
}
if (!(key in vm)) {
defineComputed(vm, key, userDef)
} else if (process.env.NODE_ENV !== 'production') {
if (key in vm.$data) {
warn(`The computed property "${
key}" is already defined in data.`, vm)
} else if (vm.$options.props && key in vm.$options.props) {
warn(`The computed property "${
key}" is already defined as a prop.`, vm)
}
}
}
}
ご覧のとおり、関数内では、次のように変数が最初に定義されてwatchers
空のオブジェクトが割り当てられ、ポインターとしてポイントされますvm._computedWatchers
。
const watchers = vm._computedWatchers = Object.create(null)
次に、computed
オプション内の各属性を走査し、最初に各項目の属性値を取得し、それを として記録してから、それが関数であるかどうかをuserDef
判断します。関数である場合、関数はデフォルトで値取得者になり、それを変数に割り当てます。; それが関数ではない場合は、それがオブジェクトであることを意味し、オブジェクト内の属性が値受け取り子として使用され、変数に割り当てられます。次のように:userDef
getter
getter
get
getter
const userDef = computed[key]
const getter = typeof userDef === 'function' ? userDef : userDef.get
次に、非運用環境では、上記の 2 つの状況で取得された値ゲッターが存在しないと判断され、計算された属性には値ゲッターが必要であることをユーザーに求める警告がスローされます。次のように:
if (process.env.NODE_ENV !== 'production' && getter == null) {
warn(
`Getter is missing for computed property "${
key}".`,
vm
)
}
次に、サーバー側のレンダリング環境にないかどうかを確認し、watcher
インスタンスを作成し、現在ループされているプロパティ名をキーとして使用し、作成したインスタンスをオブジェクトのwatcher
値として保存します。watchers
次のように:
if (!isSSR) {
// create internal watcher for the computed property.
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)
}
最後に、現在ループされている属性名が現在のインスタンスに存在するかどうかが判断されますvm
。存在する場合は、非運用環境で警告がスローされます。存在しない場合は、計算された属性を設定する関数が呼び出されます。defineComputed
インスタンス。vm
上記はinitComputed
関数の内部ロジックですが、次に、defineComputed
関数がvm
インスタンスに対して計算されたプロパティをどのように設定するかを見てみましょう。
6.3 計算関数解析の定義
defineComputed
関数の定義はsrc/core/instance/state.js
、次のようにソース コードにあります。
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
}
export function defineComputed (target,key,userDef) {
const shouldCache = !isServerRendering()
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = shouldCache
? createComputedGetter(key)
: userDef
sharedPropertyDefinition.set = noop
} else {
sharedPropertyDefinition.get = userDef.get
? shouldCache && userDef.cache !== false
? createComputedGetter(key)
: userDef.get
: noop
sharedPropertyDefinition.set = userDef.set
? userDef.set
: noop
}
if (process.env.NODE_ENV !== 'production' &&
sharedPropertyDefinition.set === noop) {
sharedPropertyDefinition.set = function () {
warn(
`Computed property "${
key}" was assigned to but it has no setter.`,
this
)
}
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
この関数は 3 つのパラメータ、つまりtarget
とkey
を受け入れますuserDef
。target
その機能は、上記の属性を定義することでありkey
、属性key
のgetter
合計は値setter
に従ってuserDef
設定されます。この関数の具体的なロジックを見てみましょう。
変数は最初に定義されますsharedPropertyDefinition
。これはデフォルトのプロパティ記述子です。
shouldCache
次に、計算されたプロパティをキャッシュするかどうかを識別する変数が関数内で定義されます。この変数の値は、現在の環境が非サーバー レンダリング環境であるかどうかを示します。非サーバー レンダリング環境の場合、この変数は ですtrue
。つまり、非サーバー レンダリング環境では、計算されたプロパティのみをキャッシュする必要があります。次のように:
const shouldCache = !isServerRendering()
userDef
次に、関数であればデフォルトの valuer であると判断しますがgetter
、ここでは非サーバーレンダリング環境では直接使用せず、userDef
関数getter
を呼び出してcreateComputedGetter
(この関数は後述します)作成します。これgetter
はuserDef
単なる通常のものでありgetter
、キャッシュ機能を持っていないため、計算されたプロパティが実行されるため、getter
サーバー側のレンダリング環境で直接userDef
使用できるキャッシュ機能を備えた追加のものを作成する必要があります。getter
サーバー側のレンダリング環境にキャッシュする必要はありません。ユーザーがsetter
機能を設定していないため、sharedPropertyDefinition.set
に設定されますnoop
。次のように:
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = shouldCache
? createComputedGetter(key)
: userDef
sharedPropertyDefinition.set = noop
}
関数ではない場合はuserDef
オブジェクトとして扱います。を設定する場合はsharedPropertyDefinition.get
、まずuserDef.get
存在するかどうかを確認します。存在しない場合は に設定しますnoop
。存在する場合は上記と同じです。非サーバー レンダリング環境で、ユーザーが明示的に に設定userDef.cache
しない場合は、関数を呼び出しますをクリックして割り当てを作成します。次に、それを関数として設定します。次のように:false
createComputedGetter
getter
sharedPropertyDefinition.get
sharedPropertyDefinition.set
userDef.set
sharedPropertyDefinition.get = userDef.get
? shouldCache && userDef.cache !== false
? createComputedGetter(key)
: userDef.get
: noop
sharedPropertyDefinition.set = userDef.set
? userDef.set
: noop
次に、ユーザーが非運用環境で設定していない場合はsetter
、setter
デフォルトの関数が提供されます。これは、ユーザーがsetter
計算された属性を設定せずに変更することを防ぐためであり、次のように警告がスローされます。
if (process.env.NODE_ENV !== 'production' &&
sharedPropertyDefinition.set === noop) {
sharedPropertyDefinition.set = function () {
warn(
`Computed property "${
key}" was assigned to but it has no setter.`,
this
)
}
}
最後に、Object.defineProperty
プロパティをkey
バインドするメソッドを呼び出しますtarget
。プロパティ記述子は上記で設定したものですsharedPropertyDefinition
。このようにして、計算されたプロパティはインスタンスにバインドされますvm
。
上記はdefineComputed
関数のロジックのすべてです。さらに、計算された属性がキャッシュされるかどうか、およびその応答性は主に、それが関数の戻り結果getter
に設定されているかどうかに依存することがわかりました。それでは次に、この関数を詳しく見てcreateComputedGetter
みましょう。createComputedGetter
6.4 createComputedGetter関数の解析
createComputedGetter
関数の定義はsrc/core/instance/state.js
、次のようにソース コードにあります。
function createComputedGetter (key) {
return function computedGetter () {
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
watcher.depend()
return watcher.evaluate()
}
}
}
ご覧のとおり、この関数は内部で関数を返す高階関数であるcomputedGetter
ため、computedGetter
実際には に関数が代入されますsharedPropertyDefinition.get
。計算されたプロパティの値が取得されると、プロパティの関数が実行されgetter
、プロパティの関数が最終的に実行される関数getter
になります。sharedPropertyDefinition.get
computedGetter
関数内では、computedGetter
まず現在のインスタンス上の_computedWatchers
属性key
に対応するインスタンスを格納しwatcher
、存在する場合にはそのインスタンス上のメソッドやメソッドwatcher
を呼び出し、そのメソッドの戻り値を計算された属性の計算結果として返します。では、インスタンス上のメソッドとメソッドとは何でしょうか?watcher
depend
evaluate
evaluate
watcher
depend
evaluate
6.5 依存と評価
上記のインスタンスを作成したときのことを振り返ると、次のようになりますwatcher
。
const computedWatcherOptions = {
computed: true }
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)
渡される 2 番目のパラメーターはgetter
関数、4 番目のパラメーターはオブジェクトですcomputedWatcherOptions
。
次のように、クラスの定義をもう一度確認してみましょうWatcher
。
export default class Watcher {
constructor (vm,expOrFn,cb,options,isRenderWatcher) {
if (options) {
// ...
this.computed = !!options.computed
// ...
} else {
// ...
}
this.dirty = this.computed // for computed watchers
if (typeof expOrFn === 'function') {
this.getter = expOrFn
}
if (this.computed) {
this.value = undefined
this.dep = new Dep()
}
}
evaluate () {
if (this.dirty) {
this.value = this.get()
this.dirty = false
}
return this.value
}
/**
* Depend on this watcher. Only for computed property watchers.
*/
depend () {
if (this.dep && Dep.target) {
this.dep.depend()
}
}
update () {
if (this.computed) {
if (this.dep.subs.length === 0) {
this.dirty = true
} else {
this.getAndInvoke(() => {
this.dep.notify()
})
}
}
}
getAndInvoke (cb: Function) {
const value = this.get()
if (
value !== this.value ||
// Deep watchers and watchers on Object/Arrays should fire even
// when the value is the same, because the value may
// have mutated.
isObject(value) ||
this.deep
) {
// set new value
const oldValue = this.value
this.value = value
this.dirty = false
if (this.user) {
try {
cb.call(this.vm, value, oldValue)
} catch (e) {
handleError(e, this.vm, `callback for watcher "${
this.expression}"`)
}
} else {
cb.call(this.vm, value, oldValue)
}
}
}
}
Watcher
クラスをインスタンス化するときに、オブジェクトが 4 番目のパラメーターとして渡されることがわかりますcomputedWatcherOptions = { computed: true }
。オブジェクト内の属性は、computed
このwatcher
インスタンスwatcher
が計算された属性、つまりWatcher
クラス内の属性のインスタンスであることを示しますthis.computed
。 , このクラスは、this.dirty
計算された属性の戻り値が変更されたかどうかをマークするための属性も定義します。計算された属性のキャッシュは、この属性によって判断されます。計算された属性が依存するデータが変更されるたびに、属性は次のように設定されます。そのため、次回計算された属性が読み取られたときに、再計算結果が返されます。それ以外の場合は、前の計算結果が直接返されますthis.dirty
。true
メソッドが呼び出されるとwatcher.depend()
、計算された属性を読み取るメソッドが計算された属性のインスタンスの依存関係リストwatcher
に追加されますwatcher
。計算された属性で使用されるデータが変更されると、watcher
計算された属性のインスタンスがwatcher.update()
メソッドを実行します。update
メソッドは、現在の属性はwatcher
計算された属性かどうかを判断しますwatcher
。そうである場合、getAndInvoke
計算された属性の戻り値が変更されたかどうかを比較するために呼び出されます。変更された場合は、コールバックが実行され、計算された属性を読み取る人に再設定を通知しますwatcher
。 - レンダリング ロジックを実行します。
メソッドを呼び出すと、まずであるかどうかがwatcher.evaluate()
判定され、 である場合は、計算された属性が依存するデータが変更されたことを意味し、呼び出しを呼び出して計算結果を再取得して最終的に戻ります。の場合は、前の計算結果が直接返されます。this.dirty
true
true
this.get()
false
その内部原理を図に示します。
7.時計を初期化する
次は最後の初期化関数、初期化watch
オプションです。このオプションは日常の開発でもwatch
よく使用され、既存のデータをリッスンし、データが変更されたときに対応するコールバック関数を実行するために使用できます。それでは、いくつかのオプションがどのように初期化されるかを見てみましょうwatch
。
7.1 使用方法を確認する
watch
まず、次のように、公式ドキュメントの使用例に従ってオプションの使用法を確認してみましょう。
var vm = new Vue({
data: {
a: 1,
b: 2,
c: 3,
d: 4,
e: {
f: {
g: 5
}
}
},
watch: {
a: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
},
// methods选项中的方法名
b: 'someMethod',
// 深度侦听,该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
c: {
handler: function (val, oldVal) {
/* ... */ },
deep: true
},
// 该回调将会在侦听开始之后被立即调用
d: {
handler: 'someMethod',
immediate: true
},
// 调用多个回调
e: [
'handle1',
function handle2 (val, oldVal) {
/* ... */ },
{
handler: function handle3 (val, oldVal) {
/* ... */ },
}
],
// 侦听表达式
'e.f': function (val, oldVal) {
/* ... */ }
}
})
vm.a = 2 // => new: 2, old: 1
ご覧のとおり、watch
オプションの使用は非常に柔軟です。まず、watch
オプションはオブジェクト、キーは監視される式、値は対応するコールバック関数です。値はメソッド名、またはオプションを含むオブジェクトにすることもできます。ユーザーに提供する用途は柔軟であるため、さまざまな用途に応じてコードを条件に応じて判断し、処理する必要があります。
7.2 initWatch関数の解析
watch
オプションの使用法を理解した後、watch
オプション初期化関数initWatch
の内部原理を分析しましょう。initWatch
関数の定義はsrc/core/instance/state.js
、次のようにソース コードにあります。
function initWatch (vm, watch) {
for (const key in watch) {
const handler = watch[key]
if (Array.isArray(handler)) {
for (let i = 0; i < handler.length; i++) {
createWatcher(vm, key, handler[i])
}
} else {
createWatcher(vm, key, handler)
}
}
}
ご覧のとおり、オプションは関数内で処理され、各項目の対応する値watch
が取得されます。配列である場合は、配列をループし、関数を呼び出して配列内の各項目を順番に作成します。配列でない場合は、関数を直接呼び出して作成します。では、この関数はどのように作成されるのでしょうか?key
handler
handler
createWatcher
watcher
createWatcher
watcher
createWatcher
watcher
7.3 createWatcher関数の解析
createWatcher
関数の定義はsrc/core/instance/state.js
、次のようにソース コードにあります。
function createWatcher (
vm: Component,
expOrFn: string | Function,
handler: any,
options?: Object
) {
if (isPlainObject(handler)) {
options = handler
handler = handler.handler
}
if (typeof handler === 'string') {
handler = vm[handler]
}
return vm.$watch(expOrFn, handler, options)
}
ご覧のとおり、この関数は次の 4 つのパラメーターを受け取ります。
- vm: 現在のインスタンス。
- expOrFn: リッスンされているプロパティ式
- ハンドラ:
watch
オプションの各項目の値 - options:渡すための
vm.$watch
オプション オブジェクト
関数内ではまずhandler
渡されたものがオブジェクトかどうかが判定され、オブジェクトであればユーザーが次のような書き方をしたものとみなされます。
watch: {
c: {
handler: function (val, oldVal) {
/* ... */ },
deep: true
}
}
つまり、リッスン オプションを使用すると、次のように、handler
オブジェクト全体が全体として記録されoptions
、handler
オブジェクト内のプロパティがhandler
実際のコールバック関数として記録されます。handler
if (isPlainObject(handler)) {
options = handler
handler = handler.handler
}
次に、受信したhandler
文字列が文字列であるかどうかを判断し、文字列である場合は、ユーザーが次の書き込み方法を使用したと見なされます。
watch: {
// methods选项中的方法名
b: 'someMethod',
}
つまり、コールバック関数はmethods
オプション内のメソッド名です。オプションを初期化するときにmethods
、オプション内の各メソッドが現在のインスタンスにバインドされることがわかっているため、現時点では現在のインスタンスからメソッドを取得するだけで済みます。実際のコールバック関数はhandler
次のように記録されます。
if (typeof handler === 'string') {
handler = vm[handler]
}
オブジェクトでも文字列でもない場合は、関数であるとみなされ、処理は行われません。
さまざまな種類の値を処理した後、expOrFn
リッスンする属性式、handler
変数はコールバック関数、options
変数はリッスン オプションとなり、最後にvm.$watcher
メソッドが呼び出されます (このメソッドについては、グローバル メソッドの導入時に詳しく説明します)。インスタンスメソッド) を渡して上記の 3 つのパラメータを入力して初期化を完了しますwatch
。
8. まとめ
この記事では、ライフサイクルの初期化フェーズで呼び出される 5 番目の初期化関数を紹介しますinitState
。props
この初期化関数内で、 、methods
、data
、computed
の合計 5 つのオプションが初期化されますwatch
。
これら 5 つのオプションの初期化順序は任意ではなく、慎重に配置されます。data
この順序で初期化することによってのみ、それを開発で使用しprops
、およびwatch
で観察することができます。data
props
これら 5 つのオプションのすべてのプロパティは最終的にインスタンスにバインドされるため、これを使用して任意this.xxx
のプロパティにアクセスできます。同時に、まさにこのため、これら 5 つのオプションのすべての属性名を繰り返すべきではありません。繰り返すと、属性が互いに上書きされます。
最後に、これら 5 つのオプションを 1 つずつ初期化する方法の内部原理を分析しました。