您好,欢迎访问一九零五行业门户网

带你深入了解下this.$nextTick!

都快2023年,你还不会this.$nexttick吗,vue2都出快10年了,vue3也已经出了两年多了,没错!说出来就丢脸,我现在才会this.nexttick(实话实说),好的咱先百度一下,哒哒哒....网速飞快地跳到了vue.js官网文档,突然发现文档中有一言?:
nexttick:在下一次dom更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的dom。它有两个参数:第一个参数是回调函数,不传时提供promise调用;第二个参数是执行环境上下文,不传默认是自动绑定到调用它的实例上。【相关推荐:vuejs视频教程、web前端开发】
我们先看看nexttick究竟是个啥?
console.log(this.$nexttick);// 控制台打印if(fn){ return nexttick(fn, this);}
我们可以看出nexttick就是一个方法,方法有两个参数:fn和this,fn就是需要传的回调函数,this就是所说的执行环境上下文。那么问题来了,在vue中是如何实现在下一次dom更新结束之后才会执行延迟回调的? 我们先看看下面的例子:
<div ref="test1">created:{{message}}</div>// vue实例data: { message: "hello world!",},created(){ this.message = '你好,世界!'; console.log(this.$refs.test1.innertext);// 报错 // typeerror: cannot read properties of undefined (reading 'innertext') this.$nexttick(()=>{ console.log('test1 nexttick:',this.$refs.test1.innertext);// 你好,世界! });},
从上面例子中,在created生命周期中操作了dom,但是我们都知道created生命周期只是初始化了数据,这期间是还没有渲染dom的,如果我们直接操作dom是找不到dom元素的,那么问题来了:为什么放在nexttick中就可以获取到了dom元素呢? 这不是很明显吗,等到dom渲染完才调用不就获取到了吗,从而知道了nexttick作用就是用来等下次dom渲染完才去调用nexttick内的dom操作代码。那么问题又来了,nexttick究竟做了什么?下面我们一起从源码层面来分别分析vue2和vue3版本的nexttick原理是啥。
vue2版本的nexttick由于vue暴露给开发者的是nexttick这个方法,在这个方法中主要做了三件事,回调函数的添加、延迟执行回调函数、判断当前的nexttick是否传入回调函数。不传的话,是一个promise,this.$nexttick.then(()=>{}),按promise处理。
回调函数添加入callbacks数组,因为可能有多个nexttick函数在当前作用域中。
判断当前nexttick是否已经标记为pending=true,也就是正在执行,如果不是就执行timerfunc(异步执行函数 用于异步延迟调用 flushcallbacks 函数)。timerfunc的执行,判断当前环境是否支持promise、mutationobserver、setimmediate、settimeout,优先级高低从前到后,分四种情况:优先使用promise,如果当前环境支持promise,nexttick默认优先使用promise去执行延迟回调函数,timerfunc执行的是promise,promise是es6下的语法,如果当前环境只支持es6以语法下的,只能考虑后面支持情况了。
支持mutationobserver,html5的api,中文意思是:修改的监听,mutationobserver用来监听dom的变动,比如节点的增减、属性的变动、文本内容的修改等都会触发mutationobserver事件。注意地,与事件不同,事件是同步触发,dom的变动会立即触发事件,而mutationobserver事件是异步触发,dom不会立即触发,需要等当前所有dom操作完毕才会触发。mutationobserver有7个属性:childlist(true,监听子节点的变动)、attributes(true,监听属性的变动)、characterdata(true,监听节点内容或节点文本的变动)、subtree(是否应用于该节点的所有后代节点)、attributeoldvalue(观察attributes变动时,是否需要记录变动前的属性值)、characterdataoldvalue(观察characterdata变动时,是否需要记录变动前的值)、attributefilter(数组,表示需要观察的特定属性(比如[‘class’,‘src’])。
为什么需要创建一个文本节点?因为在这里操作dom保证浏览器页面是最新dom渲染的,虽然看来好像是没什么作用,但这是保证拿到的dom是最新的。
支持setimmediate、settimeout,setimmediate即时计时器立即执行工作,它是在事件轮询之后执行,为了防止轮询阻塞,每次只会调用一个。settimeout按照一定时间后执行回调函数。
好了好了,到了现在,我们都知道nexttick做了什么吧,但是我们有没有想过这样的一个问题:既然都是异步回调执行等待dom更新后才去调用操作dom的代码,那么这个机制又是什么原理?这就是js的执行机制有关了,涉及宏任务与微任务的知识,我们先来看看这样的一道题:
console.log('同步代码1');settimeout(function () { console.log("settimeout");}, 0);new promise((resolve) => { console.log('同步代码2') resolve()}).then(() => { console.log('promise.then')})console.log('同步代码3');
我们可能会问上面的输出是个啥,首先js是单线程,所以在js程序运行中只有一个执行栈,实现不了多线程,所以就需要任务均衡分配,通俗的讲,按任务急优先处理原则,js中分为同步任务和异步任务,异步任务又分为宏任务和微任务,同步任务先入栈,程序会先把执行栈中的所有同步任务执行完,再去判断是否有异步任务,而异步任务中微任务的优先级高于宏任务。如果当前执行栈为空,而微任务队列不为空,就先执行微任务,等把所有微任务执行完,最后才会考虑宏任务。而上面代码中promise是属于微任务,而settimeout是宏任务,所以上面的输出为:
// 同步代码1// 同步代码2// 同步代码3// promise.then // settimeout
使用vue2的nexttick传入回调函数参数使用:
this.$nexttick(()=>{ // ...操作dom的代码})
不传入回调函数参数使用:
// 方式一this.$nexttick().then(()=>{ // ...操作dom的代码})// 方式二await this.$nexttick();// 后写操作dom的代码
vue3版本的nexttickvue3版本就没有vue2版本的那么多环境支持,nexttick封装成了一个promise异步回调函数执行。
// vue3.2.45// core-main\core-main\packages\runtime-core\srcexport function nexttick<t = void>( this: t, fn?: (this: t) => void): promise<void> { const p = currentflushpromise || resolvedpromise return fn ? p.then(this ? fn.bind(this) : fn) : p}
使用vue3的nexttick传入回调函数使用
import { nexttick } from 'vue' // 引入setup () { nexttick(()=>{ // ...操作dom的代码})
不传入回调函数的使用
import { nexttick } from 'vue' // 引入setup () { // 方式一 nexttick().then(()=>{ // ...操作dom的代码 }) // 方式二 await nexttick(); // 后写操作dom的代码}
总结nexttick可以通俗的当作一个promise,所以nexttick属于微任务。nexttick在页面更新数据后,dom更新,可以通俗理解为,nexttick就是用来支持操作dom的代码及时更新渲染页面。也就是在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的dom结构的时候,这个操作都应该放进vue.nexttick()的回调函数中。在vue生命周期的created()钩子函数进行的dom操作一定要放在vue.nexttick()的回调函数中。(学习视频分享:vuejs入门教程、编程基础视频)
以上就是带你深入了解下this.$nexttick!的详细内容。
其它类似信息

推荐信息