vue3 watch 和watchEffect()
// const isFunction = (val) =>typeof val === 'function'//options 为可选参数function watch(source, cb, options) {if (!isFunction(cb)) {//vue2中的 watch(fn,options?)已移动到单独的API。vue3中要使用watchEffect(fn,options
·
// const isFunction = (val) =>typeof val === 'function'
//options 为可选参数
function watch(source, cb, options) {
if (!isFunction(cb)) {
//vue2中的 watch(fn,options?)已移动到单独的API。vue3中要使用watchEffect(fn,options),现在watch()只支持watch(fn,cb,option?) options
//为可选参数
warn$1(`\`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);
}
//watch与watchEffect 区别在于watch需要传入cb回调函数,而watchEffect则不需要
//这点从传入doWatch()函数中的形参就可以看出来
const effectStack = [];
const trackStack = [];
let effectTrackDepth = 0;
let trackOpBit = 1;
const maxMarkerBits = 30;
function enableTracking() {
trackStack.push(shouldTrack);
shouldTrack = true;
}
let activeEffectScope;
//scope=undefined
function recordEffectScope(effect, scope) {
// console.log(scope,'x----scope') // undefined
scope = scope || activeEffectScope;
if (scope && scope.active) {
scope.effects.push(effect);
}
}
const wasTracked = (dep) => (dep.w & trackOpBit) > 0;
const newTracked = (dep) => (dep.n & trackOpBit) > 0;
const finalizeDepMarkers = (effect) => {
const { deps } = effect;
console.log(deps,'deps')
if (deps.length) {
let ptr = 0;
for (let i = 0; i < deps.length; i++) {
const dep = deps[i];
if (wasTracked(dep) && !newTracked(dep)) {
dep.delete(effect);
}
else {
deps[ptr++] = dep;
}
// clear bits
dep.w &= ~trackOpBit;
dep.n &= ~trackOpBit;
}
deps.length = ptr;
}
};
const initDepMarkers = ({ deps }) => {
if (deps.length) {
for (let i = 0; i < deps.length; i++) {
deps[i].w |= trackOpBit; // set was tracked
}
}
};
function resetTracking() {
const last = trackStack.pop();
shouldTrack = last === undefined ? true : last;
}
//用来实例化 效应
//getter 获取值的箭头函数
// scheduler = () => {
// if (!instance || instance.isMounted) {
// queuePreFlushCb(job);
// }
// else {
// // with 'pre' option, the first call must happen before
// // the component is mounted so it is called synchronously.
// //使用“pre”选项,第一次呼叫必须在
// //组件已安装,因此可以同步调用它。
// console.log('调用pre选项')
// job();
// }
// };
class ReactiveEffect {
constructor(fn, scheduler = null, scope) {
this.fn = fn;
this.scheduler = scheduler;
this.active = true;
this.deps = [];
recordEffectScope(this, scope);
}
run() {
if (!this.active) {
return this.fn();
}
if (!effectStack.includes(this)) {
try {
effectStack.push((activeEffect = this));
console.log(effectStack,'effectSatck')
//判断是否能够继续追踪
enableTracking();
trackOpBit = 1 << ++effectTrackDepth;
// console.log(trackOpBit,'trackOpBit')
// trackOpBit = 2
//effectTrackDepth = 0
//maxMarkerBits=30
if (effectTrackDepth <= maxMarkerBits) {
console.log(this,'传入的this对象')
initDepMarkers(this);
}
else {
cleanupEffect(this);
}
console.log(this,'被标记追踪this对象')
return this.fn();
}
finally {
console.log('执行了finally')
if (effectTrackDepth <= maxMarkerBits) {
finalizeDepMarkers(this);
}
trackOpBit = 1 << --effectTrackDepth;
resetTracking();
effectStack.pop();
const n = effectStack.length;
// console.log(n,'n') // 0
activeEffect = n > 0 ? effectStack[n - 1] : undefined;
}
}
}
stop() {
if (this.active) {
cleanupEffect(this);
if (this.onStop) {
this.onStop();
}
this.active = false;
}
}
}
let currentInstance = null;
const EMPTY_OBJ = Object.freeze({})
const NOOP = () => { };
const INITIAL_WATCHER_VALUE = {};
let isFlushing = false;
let isFlushPending = false;
const queue = [];
let flushIndex = 0;
let activePostFlushCbs = null;
let postFlushIndex = 0;
const resolvedPromise = Promise.resolve();
let currentFlushPromise = null;
let currentPreFlushParentJob = null;
const RECURSION_LIMIT = 100;
function warn$1(msg, ...args) {
// avoid props formatting or warn handler tracking deps that might be mutated
// during patch, leading to infinite recursion.
pauseTracking();
const instance = stack.length ? stack[stack.length - 1].component : null;
const appWarnHandler = instance && instance.appContext.config.warnHandler;
const trace = getComponentTrace();
if (appWarnHandler) {
callWithErrorHandling(appWarnHandler, instance, 11 /* APP_WARN_HANDLER */, [
msg + args.join(''),
instance && instance.proxy,
trace
.map(({ vnode }) => `at <${formatComponentName(instance, vnode.type)}>`)
.join('\n'),
trace
]);
}
else {
const warnArgs = [`[Vue warn]: ${msg}`, ...args];
/* istanbul ignore if */
if (trace.length &&
// avoid spamming console during tests
!false) {
warnArgs.push(`\n`, ...formatTrace(trace));
}
console.warn(...warnArgs);
}
resetTracking();
}
function checkRecursiveUpdates(seen, fn) {
if (!seen.has(fn)) {
seen.set(fn, 1);
}
else {
const count = seen.get(fn);
if (count > RECURSION_LIMIT) {
const instance = fn.ownerInstance;
const componentName = instance && getComponentName(instance.type);
warn$1(`Maximum recursive updates exceeded${componentName ? ` in component <${componentName}>` : ``}. ` +
`This means you have a reactive effect that is mutating its own ` +
`dependencies and thus recursively triggering itself. Possible sources ` +
`include component template, render function, updated hook or ` +
`watcher source function.`);
return true;
}
else {
seen.set(fn, count + 1);
}
}
}
function flushPreFlushCbs(seen, parentJob = null) {
if (pendingPreFlushCbs.length) {
currentPreFlushParentJob = parentJob;
activePreFlushCbs = [...new Set(pendingPreFlushCbs)];
pendingPreFlushCbs.length = 0;
{
seen = seen || new Map();
}
for (preFlushIndex = 0; preFlushIndex < activePreFlushCbs.length; preFlushIndex++) {
if (checkRecursiveUpdates(seen, activePreFlushCbs[preFlushIndex])) {
continue;
}
activePreFlushCbs[preFlushIndex]();
}
activePreFlushCbs = null;
preFlushIndex = 0;
currentPreFlushParentJob = null;
// recursively flush until it drains
flushPreFlushCbs(seen, parentJob);
}
}
function flushJobs(seen) {
isFlushPending = false;
isFlushing = true;
{
seen = seen || new Map();
}
flushPreFlushCbs(seen);
//
// Sort queue before flush.
// This ensures that:
// 1. Components are updated from parent to child. (because parent is always
// created before the child so its render effect will have smaller
// priority number)
// 2. If a component is unmounted during a parent component's update,
// its update can be skipped.
//刷新前对队列进行排序。
//这可确保:
// 1. 组件从父级更新到子级。(因为父母总是
//在子对象之前创建,因此其渲染效果将更小
//优先权号码)
// 2. 如果在父组件更新期间卸载了组件,
//可以跳过它的更新。
queue.sort((a, b) => getId(a) - getId(b));
try {
for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {
const job = queue[flushIndex];
if (job && job.active !== false) {
if (true && checkRecursiveUpdates(seen, job)) {
continue;
}
// console.log(`running:`, job.id)
callWithErrorHandling(job, null, 14 /* SCHEDULER */);
}
}
}
finally {
flushIndex = 0;
queue.length = 0;
flushPostFlushCbs(seen);
isFlushing = false;
currentFlushPromise = null;
// some postFlushCb queued jobs!
// keep flushing until it drains.
if (queue.length ||
pendingPreFlushCbs.length ||
pendingPostFlushCbs.length) {
flushJobs(seen);
}
}
}
function queueFlush() {
if (!isFlushing && !isFlushPending) {
isFlushPending = true;
currentFlushPromise = resolvedPromise.then(flushJobs);
}
}
function queueCb(cb, activeQueue, pendingQueue, index) {
if (!isArray(cb)) {
if (!activeQueue ||
!activeQueue.includes(cb, cb.allowRecurse ? index + 1 : index)) {
pendingQueue.push(cb);
}
}
else {
// if cb is an array, it is a component lifecycle hook which can only be
// triggered by a job, which is already deduped in the main queue, so
// we can skip duplicate check here to improve perf
// 如果cb是一个数组,那么它是一个组件生命周期挂钩,只能
//由已在主队列中消除重复的作业触发,因此
//我们可以在这里跳过重复检查以提高性能
pendingQueue.push(...cb);
}
queueFlush();
}
let activePreFlushCbs = null;
const pendingPreFlushCbs = [];
let preFlushIndex = 0;
function queuePreFlushCb(cb) {
queueCb(cb, activePreFlushCbs, pendingPreFlushCbs, preFlushIndex);
}
function logError(err, type, contextVNode, throwInDev = true) {
{
const info = ErrorTypeStrings[type];
if (contextVNode) {
pushWarningContext(contextVNode);
}
warn$1(`Unhandled error${info ? ` during execution of ${info}` : ``}`);
if (contextVNode) {
popWarningContext();
}
// crash in dev by default so it's more noticeable
if (throwInDev) {
throw err;
}
else {
console.error(err);
}
}
}
function handleError(err, instance, type, throwInDev = true) {
const contextVNode = instance ? instance.vnode : null;
if (instance) {
let cur = instance.parent;
// the exposed instance is the render proxy to keep it consistent with 2.x
const exposedInstance = instance.proxy;
// in production the hook receives only the error code
const errorInfo = ErrorTypeStrings[type] ;
while (cur) {
const errorCapturedHooks = cur.ec;
if (errorCapturedHooks) {
for (let i = 0; i < errorCapturedHooks.length; i++) {
if (errorCapturedHooks[i](err, exposedInstance, errorInfo) === false) {
return;
}
}
}
cur = cur.parent;
}
// app-level handling
const appErrorHandler = instance.appContext.config.errorHandler;
if (appErrorHandler) {
callWithErrorHandling(appErrorHandler, null, 10 /* APP_ERROR_HANDLER */, [err, exposedInstance, errorInfo]);
return;
}
}
logError(err, type, contextVNode, throwInDev);
}
function callWithErrorHandling(fn, instance, type, args) {
let res;
try {
res = args ? fn(...args) : fn();
}
catch (err) {
handleError(err, instance, type);
}
return res;
}
function callWithAsyncErrorHandling(fn, instance, type, args) {
if (isFunction(fn)) {
const res = callWithErrorHandling(fn, instance, type, args);
if (res && isPromise(res)) {
res.catch(err => {
handleError(err, instance, type);
});
}
return res;
}
const values = [];
for (let i = 0; i < fn.length; i++) {
values.push(callWithAsyncErrorHandling(fn[i], instance, type, args));
}
return values;
}
function doWatch(source, cb, { immediate, deep, flush, onTrack, onTrigger } = EMPTY_OBJ) {
console.log(immediate,'immediate') //立即的 值为undefined
console.log(deep,'deep') //深的 值为undefined
console.log(flush,'flush') //值为undefined
console.log(onTrack,'onTrack') //值为undefined
console.log(onTrigger,'onTrigger') //值为undefined
//监听回调函数不存在时会报错
if (!cb) {
if (immediate !== undefined) {
warn$1(`watch() "immediate" option is only respected when using the ` +
`watch(source, callback, options?) signature.`);
}
if (deep !== undefined) {
warn$1(`watch() "deep" option is only respected when using the ` +
`watch(source, callback, options?) signature.`);
}
}
const warnInvalidSource = (s) => {
warn$1(`Invalid watch source: `, s, `A watch source can only be a getter/effect function, a ref, ` +
`a reactive object, or an array of these types.`);
};
const instance = currentInstance;
let getter;
let forceTrigger = false;
let isMultiSource = false;
//根据是ref 还是reactive来进行区别,分别进行处理
if (isRef(source)) {
console.log('source为ref对象')
getter = () => source.value;
forceTrigger = !!source._shallow;
}
else if (isReactive(source)) {
getter = () => source;
deep = true;
}
else if (isArray(source)) {
isMultiSource = true;
forceTrigger = source.some(isReactive);
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)) {
if (cb) {
// getter with cb
getter = () => callWithErrorHandling(source, instance, 2 /* WATCH_GETTER */);
}
else {
// no cb -> simple effect
getter = () => {
if (instance && instance.isUnmounted) {
return;
}
if (cleanup) {
cleanup();
}
return callWithAsyncErrorHandling(source, instance, 3 /* WATCH_CALLBACK */, [onInvalidate]);
};
}
}
else {
getter = NOOP;
warnInvalidSource(source);
}
if (cb && deep) {
const baseGetter = getter;
getter = () => traverse(baseGetter());
}
let cleanup;
let onInvalidate = (fn) => {
cleanup = effect.onStop = () => {
callWithErrorHandling(fn, instance, 4 /* WATCH_CLEANUP */);
};
};
//isMultiSource=false
let oldValue = isMultiSource ? [] : INITIAL_WATCHER_VALUE;
console.log(oldValue,'oldValue')
const job = () => {
if (!effect.active) {
return;
}
if (cb) {
// watch(source, cb)
const newValue = effect.run();
if (deep ||
forceTrigger ||
(isMultiSource
? newValue.some((v, i) => hasChanged(v, oldValue[i]))
: hasChanged(newValue, oldValue)) ||
(false )) {
// cleanup before running cb again
if (cleanup) {
cleanup();
}
callWithAsyncErrorHandling(cb, instance, 3 /* WATCH_CALLBACK */, [
newValue,
// pass undefined as the old value when it's changed for the first time
oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue,
onInvalidate
]);
oldValue = newValue;
}
}
else {
// watchEffect
effect.run();
}
};
// important: mark the job as a watcher callback so that scheduler knows
// it is allowed to self-trigger (#1727)
job.allowRecurse = !!cb;
let scheduler;
if (flush === 'sync') {
scheduler = job; // the scheduler function gets called directly
}
else if (flush === 'post') {
scheduler = () => queuePostRenderEffect(job, instance && instance.suspense);
}
else {
// default: 'pre'
scheduler = () => {
if (!instance || instance.isMounted) {
queuePreFlushCb(job);
}
else {
// with 'pre' option, the first call must happen before
// the component is mounted so it is called synchronously.
//使用“pre”选项,第一次呼叫必须在
//组件已安装,因此可以同步调用它。
console.log('调用pre选项')
job();
}
};
}
//getter 获取值的箭头函数
console.log(scheduler,'scheduler')
// scheduler = () => {
// if (!instance || instance.isMounted) {
// queuePreFlushCb(job);
// }
// else {
// // with 'pre' option, the first call must happen before
// // the component is mounted so it is called synchronously.
// //使用“pre”选项,第一次呼叫必须在
// //组件已安装,因此可以同步调用它。
// console.log('调用pre选项')
// job();
// }
// };
const effect = new ReactiveEffect(getter, scheduler);
console.log(effect,'实例化effect对象')
{
effect.onTrack = onTrack;
effect.onTrigger = onTrigger;
}
// initial run
// immediate=undefined
if (cb) {
if (immediate) {
job();
}
else {
oldValue = effect.run();
console.log(oldValue,'oldValue')
}
}
else if (flush === 'post') {
queuePostRenderEffect(effect.run.bind(effect), instance && instance.suspense);
}
else {
effect.run();
}
return () => {
effect.stop();
if (instance && instance.scope) {
remove(instance.scope.effects, effect);
}
};
}
function watchEffect(effect, options) {
return doWatch(effect, null, options);
}
console.log(isRef(watchValue),'watchValue的值')
watchEffect(()=>{
console.log(watchValue)
const result = h('h1',{class:['my-class','my-title1']},watchValue.value)
render(result,p1)
})
// console.log(isRef(watchValue),'watchValue的值')
// watch(watchValue,()=>{
// console.log('值发生了改变')
// const result = h('h1',{class:['my-class','my-title1']},watchValue.value)
// render(result,p1)
// })
更多推荐
已为社区贡献5条内容
所有评论(0)