ref とリアクティブ
レスポンシブ データの定義には、ref 関数とリアクティブ関数の両方が使用されます。
しかし、reactive は参照型の定義に適していて、ref は基本的なデータ型 (基本的なデータ型とオブジェクトを受け取ることができる) の定義に適しています。
反応性
1. 詳細な応答性、本質は受信データを Proxy オブジェクトにパックすること
2. パラメータはオブジェクトまたは配列でなければならない オブジェクトの要素に応答性を実装させたい場合は、toRefs を使用する必要があります。したがって、それぞれに値によるアクセスが必要です
参照
1. 関数パラメータは、基本的なデータ型またはオブジェクト型にすることができます.
2. パラメータがオブジェクト型の場合、基本的な本質は依然としてリアクティブです.
3. ref 応答性の原則は、get() と Object.defineProperty() のセットに依存します. ()の
時計
独自のコンポーネントでリアクティブ オブジェクトをリッスンする
let a = reactive({
test: 123, bg: 456, hh: {
hhh: 78}})
// 定时器 1
setTimeout(() => {
a.test = 456
}, 2000)
// 定时器 2
setTimeout(() => {
a.hh.hhh = 56
}, 4000)
// 定时器 3
setTimeout(() => {
a = {
}
}, 6000)
//
watch(a, () => {
// 定时器1, 2 可以触发监听, 不需要deep也可以
// 定时器3 不能触发监听, 因为对象的地址已经发生改变了
console.log("change")
})
// 函数返回方式 采用deep可以监听, 不加deep不能监听
watch(
() => a,
() => {
console.log(" func change")
},
{
deep: true
}
)
ソースコードを見てください。
function watch(source, cb, options) {
if (!isFunction(cb)) {
warn2(
`\`watch(fn, options?)\` signature has been moved to a separate API. Use \`watchEffect(fn, options?)\` instead. \`watch\` now only supports \`watch(source, cb, options?) signature.`
);
}
return doWatch(source, cb, options);
}
function doWatch(source, cb, {
immediate, deep, flush, onTrack, onTrigger } = EMPTY_OBJ) {
// 代码不完整,截取部分
...
if (isRef(source)) {
getter = () => source.value;
forceTrigger = isShallow(source);
} else if (isReactive(source)) {
// 这里 ,如果 source 是 reactive
// 则 deep = true
// 而 deep 为true 后面会 执行traverse
getter = () => source;
deep = true;
} else if (isArray(source)) {
isMultiSource = true;
forceTrigger = source.some((s) => isReactive(s) || isShallow(s));
getter = () => source.map((s) => {
if (isRef(s)) {
return s.value;
} else if (isReactive(s)) {
return traverse(s);
} else if (isFunction(s)) {
return callWithErrorHandling(s, instance, 2 /* WATCH_GETTER */);
} else {
warnInvalidSource(s);
}
});
} else if (isFunction(source)) {
// 如果是函数,
//最终 getter () => fn()
// deep为false,因此不走 traverse()
if (cb) {
getter = () => callWithErrorHandling(source, instance, 2 /* WATCH_GETTER */);
} else {
getter = () => {
if (instance && instance.isUnmounted) {
return;
}
if (cleanup) {
cleanup();
}
return callWithAsyncErrorHandling(
source,
instance,
3 /* WATCH_CALLBACK */,
[onCleanup]
);
};
}
} else {
getter = NOOP;
warnInvalidSource(source);
}
....
if (cb && deep) {
const baseGetter = getter;
getter = () => traverse(baseGetter());
}
}
トラバースは、オブジェクト全体を深くトラバースし、すべての応答変数に深くアクセスし、依存関係を収集します
独自のコンポーネントで ref オブジェクトを聞く
let a = ref({
test: 123, bg: 456, hh: {
hhh: 78}})
setTimeout(() => {
a.value.test = 456
}, 2000)
setTimeout(() => {
a.value.hh.hhh = 56
}, 4000)
setTimeout(() => {
a.value = {
}
}, 6000)
// 注意下面两种写法 一个是 a , 一个是 a.value
// 从源码可知, 如果是 a, 那么走isRef(source)分支, 如果是 a.value 那么走 isReactive(分支)
// 这里不给出结果,动手试试
watch(a.value, (val) => {
console.log(val, "change")
})
watch(a, (val) => {
console.log(val, "change")
})
// 如果是 函数返回的方式呢? 其实也分两种,类推即可,同时也需要注意是否需要加 deep 属性
watch(() => a.value, (val) => {
console.log(val, "change")
})
watch(() => a, (val) => {
console.log(val, "change")
})
子コンポーネントが親コンポーネントのデータを監視する必要があり、親コンポーネントが v-model を介して双方向にバインドできる場合は、細心の注意を払う必要があります。そうしないと、バグが発生する可能性があります
監視監視が無効な場合は、データ構造に基づいて誤った書き込みが原因であるかどうかを分析します。