好家伙,
代码出了点bug,暂时只能实现这两种形式
完整代码已开源https://github.com/Fattiger4399/analytic-vue.git
Vue:watch的多种使用方法
watch有非常多种使用方式,我们要对其进行分类讨论处理
1.初始化:
//initState.js
if (opts.watch) { initWatch(vm); }
initWatch()方法
function initWatch(vm) { //1 获取watch let watch = vm.$options.watch console.log(watch) //2 遍历 { a,b,c} for (let key in watch) { //2.1获取 他的属性对应的值 (判断) let handler = watch[key] //数组 ,对象 ,字符,函数 if (Array.isArray(handler)) {//数组 [] handler.forEach(item=>{ createrWatcher(vm,key,item) }) } else {//对象 ,字符,函数 //3创建一个方法来处理 createrWatcher(vm,key,handler) } } }
createrWatcher()
//格式化处理 //vm 实例 //exprOrfn key //hendler key对应的值 //options 自定义配置项 vue自己的为空,用户定义的才有 function createrWatcher(vm,exprOrfn,handler,options){ //3.1 处理handler if(typeof handler ==='object'){ options = handler; //用户的配置项目 handler = handler.handler;//这个是一个函数 } if(typeof handler ==='string'){// 'aa' handler = vm[handler] //将实例行的方法作为 handler 方法代理和data 一样 } //其他是 函数 //watch 最终处理 $watch 这个方法 // console.log(vm,"||vm") // console.log(exprOrfn,"||exprOrfn") // console.log(handler,"||handler") // console.log(options,"||options") return vm.$watch(vm,exprOrfn,handler,options) }
原型上挂$watch方法
export function stateMixin(vm) { console.log(vm,6666) //列队 :1就是vue自己的nextTick 2用户自己的 vm.prototype.$nextTick = function (cb) { //nextTick: 数据更新之后获取到最新的DOM // console.log(cb) nextTick(cb) }, vm.prototype.$watch =function(Vue,exprOrfn,handler,options={}){ //上面格式化处理 // console.log(exprOrfn,handler,options) //实现watch 方法 就是new watcher //渲染走 渲染watcher $watch 走 watcher user false // watch 核心 watcher let watcher = new Watcher(Vue,exprOrfn,handler,{...options,user:true}) if(options.immediate){ handler.call(Vue) //如果有这个immediate 立即执行 } } }
2.watcher.js
watcher类
class Watcher { //vm 实例 //exprOrfn vm._updata(vm._render()) constructor(vm, exprOrfn, cb, options) { // 1.创建类第一步将选项放在实例上 this.vm = vm; this.exprOrfn = exprOrfn; this.cb = cb; this.options = options; // 2. 每一组件只有一个watcher 他是为标识 this.id = id++ this.user = !!options.user // 3.判断表达式是不是一个函数 this.deps = [] //watcher 记录有多少dep 依赖 this.depsId = new Set() if (typeof exprOrfn === 'function') { this.getter = exprOrfn }else{ //{a,b,c} 字符串 变成函数 this.getter =function(){ //属性 c.c.c let path = exprOrfn.split('.') let obj = vm for(let i = 0;i<path.length;i++){ obj = obj[path[i]] } return obj // } } // 4.执行渲染页面 this.value = this.get() //保存watch 初始值 } addDep(dep) { //去重 判断一下 如果dep 相同我们是不用去处理的 let id = dep.id // console.log(dep.id) if (!this.depsId.has(id)) { this.deps.push(dep) this.depsId.add(id) //同时将watcher 放到 dep中 // console.log(666) dep.addSub(this) } // 现在只需要记住 一个watcher 有多个dep,一个dep 有多个watcher //为后面的 component } run() { //old new let value = this.get() //new let oldValue = this.value //old this.value = value //执行 hendler (cb) 这个用户wathcer if(this.user){ this.cb.call(this.vm,value,oldValue) } } get() { // Dep.target = watcher pushTarget(this) //当前的实例添加 const value = this.getter()// 渲染页面 render() with(wm){_v(msg,_s(name))} ,取值(执行get这个方法) 走劫持方法 popTarget(); //删除当前的实例 这两个方法放在 dep 中 return value } //问题:要把属性和watcher 绑定在一起 去html页面 // (1)是不是页面中调用的属性要和watcher 关联起来 //方法 //(1)创建一个dep 模块 updata() { //三次 //注意:不要数据更新后每次都调用 get 方法 ,get 方法回重新渲染 //缓存 // this.get() //重新渲染 queueWatcher(this) } }
3.看看效果
<body> <div id="app">{{a}}</div> <script src='./dist/vue.js'></script> <script> let vm = new Vue({ el: "#app", data: { a: 1, b:[1,2], c:{c:{c:100}} }, methods:{ aa(){ console.log(2000) } }, //watch 基本使用方式 //1 属性 :方法(函数) //2 属性 :数组 //3 属性 :对象 //4 属性 :字符串 watch: { 'c.c.c'(newValue,oldValue){ console.log(newValue,oldValue,'||this isnewValue,oldValue from c.c.c') }, a:{ handler(){ // console.log('a数据更新') }, immediate:true }, // a:'aa', } }) vm.a = 100 // vm.b[1] = 2 console.log(vm.c.c.c=2000) </script> </body>
