Sección 4: El principio de implementación de vue3-WatchAPI

El núcleo del reloj es observar datos receptivos, notificar y ejecutar una devolución de llamada cuando los datos cambien (es decir, es un efecto en sí mismo)

La esencia del reloj es que el efecto se basará en los datos completados por el usuario para recopilar

Cuando cambia el objeto de supervisión del reloj, las propiedades del objeto no se activarán porque la dirección de referencia del objeto no ha cambiado.

reloj es equivalente al efecto, que guardará el valor anterior y el valor nuevo y llamará al método

Escenas a utilizar

1. El objeto de monitoreo puede monitorear los cambios de datos y volver a ejecutar cuando los datos cambian.

El objeto de monitoreo no puede distinguir entre el valor nuevo y el valor anterior antes y después

const state= reactive({flag: true, name: 'lyp',address:{num: 6}, age: 30})
// 监测一个响应式值的变化
watch(state,(oldValue,newValue)=>{ 
    console.log(oldValue,newValue)
})
setTimeout(() => {
    state.name ='jdlyp'
    // 也可以触发watch
    // state.address.num='10000'
}, 1000);
复制代码

2. Puede monitorear una función.El valor de retorno de la función es obtener el nuevo valor después de que se actualice el valor anterior.

No se puede escribir directamente como state.address.num

const state= reactive({flag: true, name: 'lyp',address:{num: 6}, age: 30})
watch(()=>state.address.num,(oldValue,newValue)=>{ // 监测一个响应式值的变化
    console.log(oldValue,newValue)
})
setTimeout(() => {
    state.address.num=10000
}, 1000);
复制代码

3. Cuando el reloj se activa continuamente, la operación de reloj anterior debe limpiarse para lograr el propósito del último resultado devuelto.

Cuando el usuario ingresa en el cuadro de entrada, devolvemos el resultado de acuerdo con el contenido de entrada (async como ajax)

Pasos de implementación

  • 1) Pase una devolución de llamada cancelada cuando llame al reloj por primera vez
  • 2) Cuando se llama al reloj por segunda vez, se ejecuta la última devolución de llamada pasada

onCleanup es un gancho proporcionado por el código fuente de vue al usuario

La función pasada por el usuario a onCleanup será llamada automáticamente por el código fuente de vue

const state= reactive({flag: true, name: 'lyp',address:{num: 6}, age: 30})
let i = 2000;
// 模拟ajax  实现 第一次比第二次返回的晚
function getData(timer){ 
    return new Promise((resolve,reject)=>{
        setTimeout(() => {
            resolve(timer)
        }, timer);
    })
}

// 每次数据变化 都会执行watch的回调函数 
// 每次都会形成一个私有作用域 传入的onCleanup函数 执行改变的是上一个私有作用域的clear值
// onCleanup 是 vue源码提供给用户的钩子
watch(()=>state.age,async (newValue,oldValue,onCleanup)=>{
    let clear = false;
    
    // 将 终止的调函数 给到vue源码中的cleanup(也就是传递给下一层)  终止函数的调用会有vue源码自动执行
    onCleanup(()=>{  
        clear = true; 
    })
    i-=1000;
    let r =  await getData(i); // 第一次执行1s后渲染1000, 第二次执行0s后渲染0, 最终应该是0
    if(!clear){document.body.innerHTML = r;} 
},{flush:'sync'}); // {flush:'sync'} 表示同步执行
state.age = 31;
state.age = 32;
复制代码

Código

  • 1. Si es un objeto receptivo, recorra las propiedades
  • 2. Si es una función, deje que la función se use como fn
  • 3. Cree un efecto para monitorear los cambios de los datos de la función construidos por usted mismo, vuelva a ejecutar el trabajo y luego obtenga el nuevo valor
  • 4. Cuando se ejecute para guardar el valor anterior, deje que el captador se ejecute, es decir, la ejecución de la fuente o la fuente del bucle.
  • 5. Si necesita ser ejecutado inmediatamente, ejecute la tarea inmediatamente
  • 6. La función onCleanup se pasa en la devolución de llamada para exponer el enlace al usuario
  • 7. Guarde los parámetros pasados ​​por el usuario como limpieza
export const enum ReactiveFlags {
    IS_REACTIVE = '__v_isReactive' 
}
export function isReactive(value){
    return !!(value && value[ReactiveFlags.IS_REACTIVE])
}
// 遍历的是对象的话  考虑对象中有循环引用的问题
function traverse(value,seen = new Set()){ 
    if(!isObject(value)){ // 不是对象就不再递归了
        return value
    }
    // 如果循环过就不再考虑 直接返回上次的对象就行 解决 object= {a:obj} 的问题
    if(seen.has(value)){ 
        return value;
    }
    seen.add(value);
    for(const k in value){ // 递归访问属性用于依赖收集
        traverse(value[k],seen)
    }
    return value
}


// source 是用户传入的对象   cb就是对应的用户回调  immediate是否立即执行一次回调
export function watch(source,cb,{immediate} = {} as any){
    let getter;
    // 1、如果是响应式对象 循环一遍属性
    if(isReactive(source)){
        // 对用户传入的数据循环一遍来收集effect 只需要循环一遍就好
        //(递归循环,只要循环就会访问对象的每一个属性,在effect中 访问属性的时候 会进行依赖收集)
        // 包装成effect对应的fn, 函数内部进行遍历达到依赖收集的目的
        getter = () => traverse(source)
        console.log(getter)
    }else if(isFunction(source)){
        getter = source // 2、如果是函数则让函数作为fn即可
    }
    let oldValue;
    let cleanup;
    let onCleanup = (fn) =>{  
        cleanup = fn;// 7、保存用户传入的参数为cleanup
    }
    const job = () =>{
        // 值变化时再次运行effect函数,获取新值
        const newValue = effect.run(); 
        // 第一次没有 下次watch执行前调用上次注册的回调
        if(cleanup) cleanup(); 
        // 6、回调时传入onCleanup函数 将钩子暴露给用户
        cb(newValue,oldValue,onCleanup); 
        oldValue = newValue
    }
    // 3、创建effect 监控自己构造的函数 数据变化后重新执行job 然后获取新值
    const effect = new ReactiveEffect(getter,job) 
    if(immediate){ // 5、需要立即执行,则立刻执行任务
        job();
    }
    // 4、运行保存老值 run 的时候 让getter执行 也就是source执行 或者循环source
    oldValue = effect.run(); 
}
复制代码

paso a paso

Supervisión de objetos reactivos

function traverse(value,seen = new Set()){
    if(!isObject(value)){
        return value
    }
    if(seen.has(value)){
        return value;
    }
    seen.add(value);
    for(const k in value){ // 递归访问属性用于依赖收集
        traverse(value[k],seen)
    }
    return value
}
export function isReactive(value){
    return !!(value && value[ReactiveFlags.IS_REACTIVE])
}
export function watch(source,cb){
    let getter;
    if(isReactive(source)){ // 如果是响应式对象
        getter = () => traverse(source)// 包装成effect对应的fn, 函数内部进行遍历达到依赖收集的目的
    }
    let oldValue;
    const job = () =>{
        const newValue = effect.run(); // 值变化时再次运行effect函数,获取新值
        cb(newValue,oldValue);
        oldValue = newValue
    }
    const effect = new ReactiveEffect(getter,job) // 创建effect
    oldValue = effect.run(); // 运行保存老值
}
复制代码

Función de vigilancia

export function watch(source,cb){
    let getter;
    if(isReactive(source)){ // 如果是响应式对象
        getter = () => traverse(source)
    }else if(isFunction(source)){
        getter = source // 如果是函数则让函数作为fn即可
    }
    // ...
}
复制代码

Tiempo de ejecución de devolución de llamada en el reloj

export function watch(source,cb,{immediate} = {} as any){
    const effect = new ReactiveEffect(getter,job) // 创建effect
    if(immediate){ // 需要立即执行,则立刻执行任务
        job();
    }
    oldValue = effect.run(); 
}
复制代码

implementación de limpieza en el reloj

Cuando el reloj se activa continuamente, la operación de reloj anterior debe limpiarse

onCleanup es un gancho proporcionado por el código fuente de vue al usuario

La función pasada por el usuario a onCleanup será llamada automáticamente por el código fuente de vue

// 使用
const state = reactive({ flag: true, name: 'lyp', age: 30 })
let i = 2000;
function getData(timer){
    return new Promise((resolve,reject)=>{
        setTimeout(() => {
            resolve(timer)
        }, timer);
    })
}
watch(()=>state.age,async (newValue,oldValue,onCleanup)=>{
    let clear = false;
    onCleanup(()=>{ // 利用钩子函数将 取消的回调传给下一层
        clear = true;
    })
    i-=1000;
    let r =  await getData(i); // 第一次执行1s后渲染1000, 第二次执行0s后渲染0, 最终应该是0
    if(!clear){document.body.innerHTML = r;}
},{flush:'sync'});
state.age = 31;
state.age = 32;
复制代码
// 源码实现
let cleanup;
let onCleanup = (fn) =>{
    cleanup = fn; // 保存用户的终止函数
}
const job = () =>{
    const newValue = effect.run(); 
    if(cleanup) cleanup(); // 第一次没有 下次watch执行前调用上次注册的回调
    cb(newValue,oldValue,onCleanup); // 调用用户回调传入onCleanup函数 暴露钩子
    oldValue = newValue
}
复制代码

Supongo que te gusta

Origin juejin.im/post/7079322953174745125
Recomendado
Clasificación