您现在的位置是:首页 >技术交流 >手写vue-watch网站首页技术交流

手写vue-watch

cwj&xyp 2024-06-22 18:01:03
简介手写vue-watch

一、watch的使用

watch有多重形式,可能是字符串、函数、数组

// 函数形式 数组形式  vm.$watch
            // 最终调用的都是vm.$watch
            watch:{
                firstname(newValue,oldValue){
                    // 观察者 观察变量改变 执行回调函数
                    console.log(newValue,oldValue)
                }
            }
 vm.$watch(()=>vm.firstname,(newValue,oldValue)=>{
            console.log(newValue,oldValue,11)
        })

二、实现原理

本质是一个Wathcer,观测一个响应式数据,当数据发生变化时通知并执行相应的回调函数。
在监听对象变化的时候,加上 deep 这个属性即可深度监听对象数据;如果你想在页面进来时就执行 watch 方法,加上 immediate 即可。值得注意的是,设置了 immediate 属性的 watch 的执行顺序是在 created 生命周期之前的

三、实现watch

3.1初始化

export function initState(vm) {
  const opts = vm.$options;
  ...
  if (opts.watch) {
    // 初始化watch
    initWatch(vm);
  }
}

createWatcher 中会判断 handler 是否是对象,如果是对象将 handler 挂载到 options 这个属性,再将对象的 handler 属性提取出来;如果 handler 是一个字符串的话,会从 Vue 实例找到这个方法赋值给 handler。从这里我们也能看出来,watch 还可以支持字符串的写法。执行 Vue 实例上的 $watch 方法。

function initWatch(vm){
  let watch = vm.$options.watch
  for(let key in watch){ //字符串 数组 函数
    const handler = watch[key]
    if(Array.isArray(handler)){
      // 循环创建watch
      for(let i=0;i<handler.length;i++){
        createWatcher(vm,key,handler[i])
      }
    }else{
      createWatcher(vm,key,handler)
    }

  }
}

// 字符串 函数
function createWatcher(vm,key,handler){
  if(typeof handler === 'string'){
    handler = vm[handler]
  }
  return vm.$watch(key,handler)
}
// 监控的某个值,回调
Vue.prototype.$watch = function (expreOrFn, cb) {
  // firstname
 // ()=>vm.firstname
 // 表示用户自己写的watcher


//  firstname的值变化直接执行cb函数
  new Watcher(this,expreOrFn,{user:true},cb)
}
class Watcher {
  constructor(vm, exprOrFn, options, cb) {
    this.id = id++;
    this.renderWatcher = options; //标识是一个渲染watcher

    if (typeof exprOrFn === 'string') {
      this.getter = function () {
        return vm[exprOrFn]
      }
    } else {
      this.getter = exprOrFn; //getter意味着调用这个函数可以发生取值操作
    }


    this.deps = []; // watcher记住dep,比如计算属性和组件卸载,希望watcher清理掉所有响应式数据
    this.depsId = new Set(); //去掉重复的dep

    this.vm = vm
    this.user = options.user //标识是否是用户自己的watcher
    this.cb = cb
    this.lazy = options.lazy;
    this.dirty = options.lazy;
    this.value = this.lazy ? undefined : this.get(); //此处先调用get是因为,首次渲染组件需要去取到数据放到页面上
  }

 run() {
    let newValue = this.get(); // 渲染的时候用的是最新的vm来渲染的
    if(this.user){
      this.cb.call(this.vm,newValue,this.value)
    }
  }
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。