前言
面试题:vue中created、watch(immediate: true)和computed的执行顺序是啥?
先看个简单的例子:
当前例子的执行顺序为:watch --> created --> computed。
为什么?
在new Vue的实例化过程中,会执行初始化方法this._init,其中有代码:
猛一看代码,是不是发现先执行的initComputed(vm, opts.computed),然后执行initWatch(vm, opts.watch),再执行callHook(vm, 'created'),那为啥不是computed --> watch --> created呢?
不要着急,听我娓娓道来。
1、关于initComputed
在通过initComputed初始化计算属性的时候,通过遍历的方式去处理当前组件中的computed。首先,在进行计算属性实例化的时候,将{ lazy: true }作为参数传入,并且实例化的Watcher中的getter就是当前例子中的computedCount函数;其次,通过defineComputed(vm, key, userDef)的方式在当前组件实例vm上为key进行userDef的处理。具体为:
从以上可以看出,这里通过Object.defineProperty(target, key, sharedPropertyDefinition)的方式,将函数computedGetter作为get函数,只有当对key进行访问的时候,才会触发其内部的逻辑。内部逻辑watcher.evaluate()为:
get中有主要逻辑:
这里的this.getter就是当前例子中的:
也就是说,只有当获取computedCount的时候才会触发computed的计算,也就是在进行vm.$mount(vm.$options.el)阶段才会执行到console.log('computed')。
2、关于initWatch
在通过initWatch初始化侦听器的时候,如果watch为数组,则遍历执行createWatcher,否则直接执行createWatcher。如果handler是对象或者字符串时,将其进行处理,最终作为参数传入vm.$watch中去,具体为:
这里获取到的options中会有immediate: true的键值,同时通过options.user = true设置user为true,再将其作为参数传入去进行Watcher的实例化。
当前例子中options.immediate为true,所以会执行cb.call(vm, watcher.value),也就是以vm为主体,立刻执行cb。当前例子中cb就是handler:
这里就解释了当前例子中console.log('watch')是最先执行的。
然后,执行完initComputed和initWatch以后,就会通过callHook(vm, 'created')执行到生命周期中的console.log('created')了。
最后通过vm.$mount(vm.$options.el)进行页面渲染的时候,会先去创建vNode,这时就需要获取到computedCount的值,进而触发其get函数的后续逻辑,最终执行到console.log('computed')。
附:为什么vue中的watch在mounted之后执行
首先,在调用mounted的时候,会进入到defineReactive函数,然后调用函数里面的set方法,将mounted中赋的新的值给传递过去,并通过调用dep.notify( )把消息发送给订阅者,继而更新订阅者的列表
后面才开始渲染watch进入Watcher的class
总结
关于vue中created和watch的执行顺序相对比较简单,而其中computed是通过Object.defineProperty为当前vm进行定义,再到后续创建vNode阶段才去触发执行其get函数,最终执行到计算属性computed对应的逻辑。
到此这篇关于vue中created、watch和computed执行顺序详解的文章就介绍到这了,更多相关vue created、watch和computed执行顺序内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
您可能感兴趣的文章:详解vue中属性执行顺序vue-router中的钩子函数和执行顺序说明vue2与vue3中生命周期执行顺序的区别说明vue中各选项及钩子函数执行顺序详解tdesign vue初始化组件源码解析Vue3源码分析组件挂载初始化props与slotsVue 先初始化父组件再初始化子组件的方法(自定义父子组件mounted执行顺序)