当前位置: 首页 > Web前端 > vue.js

VuenextTick的源码理解、使用场景

时间:2023-03-31 17:45:58 vue.js

Vue.nextTick([callback,context])参数:{Function}[callback]{Object}[context]用法:在下一个DOM更新周期结束后执行一个延迟回调。修改数据后立即使用此方法获取更新后的DOM。当你想在创建的场景中操作dom时,由于创建时dom还没有被渲染,不能直接操作dom,需要在nextTick处获取。mounted()中的任何DOM操作都不会出现问题,因为所有DOM挂载和渲染都在mounted()执行时完成value

***created(){//直接访问会报错,因为获取不到dom:"TypeError:Cannotsetproperty'innerText'ofundefined"console.log(this.$refs.myRef)//undefinedthis.$refs.myRef.innerText="设置值"//报错}因此,如果不使用$nextTick(),则created钩子中的DOM操作会报错://正确使用created(){this.$nextTick(()=>{this.$refs.myRef.innerText="setvalue"})}使用第三方插件时,需要在dom动态变化后应用这些插件。这时候需要使用$nextTick()重新应用插件方法,比如使用swiperthis.nextTick(function(){varswiper=newSwiper('.swiper-container',{pagination:'.swiper-pagination',nextButton:'.swiper-button-next',prevButton:'.swiper-button-prev',autoplay:2500})})源码解析Vue.nextTick中的回调函数会在页面打开后执行呈现,因此将需要异步调用。这里的异步主要是用到macrotasks和microtasks(看我的文章)。以下代码判断当前运行环境支持的异步方法。优先级为Promise、MutationObserver、setImmediate和setTimeout。有兴趣的可以去了解一下,简单理解就是timerFunc是一个异步执行函数=()=>{p.then(flushCallbacks)if(isIOS)setTimeout(noop)}isUsingMicroTask=true}elseif(!isIE&&typeofMutationObserver!=='undefined'&&(isNative(MutationObserver)||//PhantomJS和iOS7.xMutationObserver.toString()==='[objectMutationObserverConstructor]')){//在原生Promise不可用的情况下使用MutationObserver,//例如PhantomJS,iOS7,Android4.4//(#6466MutationObserver在IE11中不可靠)letcounter=1constobserver=newMutationObserver(flushCallbacks)consttextNode=document.createTextNode(String(counter))observer.observe(textNode,{characterData:真})timerFunc=()=>{counter=(counter+1)%2textNode.data=String(counter)}isUsingMicroTask=true}elseif(typeofsetImmediate!=='undefined'&&isNative(setImmediate)){//回退到setImmediate。//从技术上讲,它利用了(宏)任务队列,//但它仍然是比setTimeout更好的选择。timerFunc=()=>{setImmediate(flushCallbacks)}}else{//回退到setTimeout。timerFunc=()=>{setTimeout(flushCallbacks,0)}}在上面的代码中,我们可以看到异步函数timerFunc执行了flushCallbacks。该函数的作用是依次执行callbacks数组中的方法。callbacks数组包含调用Vue.nextTick传递的所有回调constcallbacks=[]functionflushCallbacks(){pending=falseconstcopies=callbacks。切片(0)回调。length=0for(leti=0;i{if(cb){try{cb.call(ctx)}catch(e){handleError(e,ctx,'nextTick')}}elseif(_resolve){_resolve(ctx)}})if(!pending){pending=truetimerFunc()}//$flow-disable-lineif(!cb&&typeofPromise!=='undefined'){returnnewPromise(resolve=>{_resolve=resolve})}}根据当前执行环境使用async方法PromiseMutationObserversetImmediatesetTimeout总结下一次DOM更新周期后执行延迟回调完成源码Vue.nextTick源码路径/node_modules/vue/src/core/util/next-tick.js/*@flow*//*globalsMutationObserver*/import{noop}from'shared/util'import{handleError}from'./error'import{isIE,isIOS,isNative}from'./env'exportletisUsingMicroTask=falseconstcallbacks=[]letpending=falsefunctionflushCallbacks(){pending=false常量副本=callbacks.slice(0)callbacks.length=0for(leti=0;i{p.then(flushCallbacks)if(isIOS)setTimeout(noop)}isUsingMicroTask=true}elseif(!isIE&&typeofMutationObserver!=='undefined'&&(isNative(MutationObserver)||//PhantomJS和iOS7.xMutationObserver.toString()==='[objectMutationObserverConstructor]')){//在原生Promise不可用的情况下使用MutationObserver,//例如PhantomJS,iOS7,Android4.4//(#6466MutationObserver在IE11中不可靠)letcounter=1constobserver=newMutationObserver(flushCallbacks)consttextNode=document.createTextNode(String(counter))observer.observe(textNode,{characterData:true})timerFunc=()=>{counter=(counter+1)%2textNode.data=String(counter)}isUsingMicroTask=true}elseif(typeofsetImmediate!=='undefined'&&isNative(setImmediate)){//回退到setImmediate。//从技术上讲,它利用了(宏)任务队列,//但它仍然是比setTimeout更好的选择。timerFunc=()=>{setImmediate(flushCallbacks)}}else{//回退到setTimeout。timerFunc=()=>{setTimeout(flushCallbacks,0)}}导出函数nextTick(cb?:Function,ctx?:Object){let_resolvecallbacks.push(()=>{if(cb){try{cb.call(ctx)}catch(e){handleError(e,ctx,'nextTick')}}elseif(_resolve){_resolve(ctx)}})if(!pending){pending=truetimerFunc()}//$flow-disable-lineif(!cb&&typeofPromise!=='undefined'){returnnewPromise(resolve=>{_resolve=resolve})}}