Vue.nextTick如何使用

一、nextTick小测试

你真的了解nextTick吗?来,直接上题~

<template>  <div id="app">    <p ref="name">{{ name }}</p>    <button @click="handleClick">修改name</button>  </div></template><script>  export default {  name: 'App',  data () {    return {      name: '井柏然'    }  },  mounted() {    console.log('mounted', this.$refs.name.innerText)  },  methods: {    handleClick () {      this.$nextTick(() => console.log('nextTick1', this.$refs.name.innerText))      this.name = 'jngboran'      console.log('sync log', this.$refs.name.innerText)      this.$nextTick(() => console.log('nextTick2', this.$refs.name.innerText))    }  }}</script>

请问上述代码中,当点击按钮“修改name”时,'nextTick1''sync log''nextTick2'对应的this.$refs.name.innerText分别会输出什么?注意,这里打印的是DOM的innerText~(文章结尾处会贴出答案)

如果此时的你有非常坚定的答案,那你可以不用继续往下看了~但如果你对自己的答案有所顾虑,那不如跟着我,接着往下看。相信你看完,不需要看到答案都能有个肯定的答案了~!

二、nextTick源码实现

源码位于core/util/next-tick中。可以将其分为4个部分来看,直接上代码

1. 全局变量

callbacks队列、pending状态

const callbacks = [] // 存放cb的队列let pending = false // 是否马上遍历队列,执行cb的标志

2. flushCallbacks

遍历callbacks执行每个cb

function flushCallbacks () {  pending = false // 注意这里,一旦执行,pending马上被重置为false  const copies = callbacks.slice(0)  callbacks.length = 0  for (let i = 0; i < copies.length; i++) {    copies[i]() // 执行每个cb  }}

3. nextTick的异步实现

根据执行环境的支持程度采用不同的异步实现策略

let timerFunc // nextTick异步实现fnif (typeof Promise !== 'undefined' && isNative(Promise)) {  // Promise方案  const p = Promise.resolve()  timerFunc = () => {    p.then(flushCallbacks) // 将flushCallbacks包装进Promise.then中  }  isUsingMicroTask = true} else if (!isIE && typeof MutationObserver !== 'undefined' && (  isNative(MutationObserver) ||  MutationObserver.toString() === '[object MutationObserverConstructor]')) {  // MutationObserver方案  let counter = 1  const observer = new MutationObserver(flushCallbacks) // 将flushCallbacks作为观测变化的cb  const textNode = document.createTextNode(String(counter)) // 创建文本节点  // 观测文本节点变化  observer.observe(textNode, {    characterData: true  })  // timerFunc改变文本节点的data,以触发观测的回调flushCallbacks  timerFunc = () => {     counter = (counter + 1) % 2    textNode.data = String(counter)  }  isUsingMicroTask = true} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {  // setImmediate方案  timerFunc = () => {    setImmediate(flushCallbacks)  }} else {  // 最终降级方案setTimeout  timerFunc = () => {    setTimeout(flushCallbacks, 0)  }}

这里用个真实案例加深对MutationObserver的理解。毕竟比起其他三种异步方案,这个应该是大家最陌生的

const observer = new MutationObserver(() => console.log('观测到文本节点变化'))const textNode = document.createTextNode(String(1))observer.observe(textNode, {    characterData: true})console.log('script start')setTimeout(() => console.log('timeout1'))textNode.data = String(2) // 这里对文本节点进行值的修改console.log('script end')

知道对应的输出会是怎么样的吗?

script startscript end会在名列前茅轮宏任务中执行,这点没问题

setTimeout会被放入下一轮宏任务执行

MutationObserver是微任务,所以会在本轮宏任务后执行,所以先于setTimeout

结果如下图:

Vue.nextTick如何使用

4. nextTick方法实现

cbPromise方式

export function nextTick (cb?: Function, ctx?: Object) {  let _resolve  // 往全局的callbacks队列中添加cb  callbacks.push(() => {    if (cb) {      try {        cb.call(ctx)      } catch (e) {        handleError(e, ctx, 'nextTick')      }    } else if (_resolve) {      // 这里是支持Promise的写法      _resolve(ctx)    }  })  if (!pending) {    pending = true    // 执行timerFunc,在下一个Tick中执行callbacks中的所有cb    timerFunc()  }  // 对Promise的实现,这也是我们使用时可以写成nextTick.then的原因  if (!cb && typeof Promise !== 'undefined') {    return new Promise(resolve => {      _resolve = resolve    })  }}
  • 深入细节,理解pending有什么用,如何运作?

  • 案例1,同一轮Tick中执行2次$nextTicktimerFunc只会被执行一次

this.$nextTick(() => console.log('nextTick1'))this.$nextTick(() => console.log('nextTick2'))
  • 用图看看更直观?

Vue.nextTick如何使用

三、Vue组件的异步更新

这里如果有对Vue组件化、派发更新不是十分了解的朋友,可以先戳这里,看图解Vue响应式原理了解下Vue组件化和派发更新的相关内容再回来看噢~

Vue的异步更新DOM其实也是使用nextTick来实现的,跟我们平时使用的$nextTick其实是同一个~

这里我们回顾一下,当我们改变一个属性值的时候会发生什么?

Vue.nextTick如何使用

根据上图派发更新过程,我们从watcher.update开时讲起,以渲染Watcher为例,进入到queueWatcher

1. queueWatcher做了什么?

// 用来存放Wathcer的队列。注意,不要跟nextTick的callbacks搞混了,都是队列,但用处不同~const queue: Array<Watcher> = []function queueWatcher (watcher: Watcher) {  const id = watcher.id // 拿到Wathcer的id,这个id每个watcher都有且全局少数  if (has[id] == null) {    // 避免添加重复wathcer,这也是异步渲染的优化做法    has[id] = true    if (!flushing) {      queue.push(watcher)    }    if (!waiting) {      waiting = true      // 这里把flushSchedulerQueue推进nextTick的callbacks队列中      nextTick(flushSchedulerQueue)    }  }}

2. flushSchedulerQueue做了什么?

function flushSchedulerQueue () {  currentFlushTimestamp = getNow()  flushing = true  let watcher, id  // 排序保证先父后子执行更新,保证userWatcher在渲染Watcher前  queue.sort((a, b) => a.id - b.id)  // 遍历所有的需要派发更新的Watcher执行更新  for (index = 0; index < queue.length; index++) {    watcher = queue[index]    id = watcher.id    has[id] = null    // 真正执行派发更新,render -> update -> patch    watcher.run()  }}
  • 最后,一张图搞懂组件的异步更新过程

Vue.nextTick如何使用

四、回归题目本身

如图所示,mounted时候的innerText是“井柏然”的中文

Vue.nextTick如何使用

接下来是点击按钮后,打印结果如图所示

Vue.nextTick如何使用

没错,输出结果如下(意不意外?惊不惊喜?)

sync log 井柏然

nextTick1 井柏然

nextTick2 jngboran

下面简单分析一下每个输出:

this.$nextTick(() => console.log('nextTick1', this.$refs.name.innerText))this.name = 'jngboran'console.log('sync log', this.$refs.name.innerText)this.$nextTick(() => console.log('nextTick2', this.$refs.name.innerText))

sync log:这个同步打印没什么好说了,相信大部分童鞋的疑问点都不在这里。如果不清楚的童鞋可以先回顾一下EventLoop,这里不多赘述了~

nextTick1:注意其虽然是放在$nextTick的回调中,在下一个tick执行,但是他的位置是在this.name = 'jngboran&#39;的前。也就是说,他的cb会比App组件的派发更新(flushSchedulerQueue)更先进入队列,当nextTick1打印时,App组件还未派发更新,所以拿到的还是旧的DOM值。

nextTick2就不展开了,大家可以自行分析一下。相信大家对它应该是最肯定的,我们平时不就是这样拿到更新后的DOM吗?

最后来一张图加深理解

Vue.nextTick如何使用

“Vue.nextTick如何使用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

文章标题:Vue.nextTick如何使用,发布者:亿速云,转载请注明出处:https://worktile.com/kb/p/23011

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
亿速云的头像亿速云认证作者
上一篇 2022年9月5日 下午11:59
下一篇 2022年9月6日 上午12:01

相关推荐

  • windows中360桌面助手如何整理桌面

    360桌面助手整理桌面方法: 1、首先打开自己的桌面选择空白处右击。 2、然后在弹出的菜单中点击“桌面助手”。 3、之后选择右侧菜单中的“一键整理桌面”。 4、最后就可以将桌面全部的整理完成了。 感谢各位的阅读,以上就是“windows中360桌面助手如何整理桌面”的内容了,经过本文的学习后,相信大…

    2022年8月31日
    55200
  • windows xbox登录账号没反应如何解决

    xbox登录账号没反应解决方法: 方法一: 1、我们可以尝试先在电脑中登录微软账号,这样打开xbox就不需要登录了。 2、首先我们进入系统设置。 3、然后打开“账户” 4、在其中选择“改用Microsoft账户登录” 方法二: 1、首先我们以管理员身份运行“windows powershell” 2…

    2022年9月20日
    70200
  • vue-router中children怎么使用

    关于children的使用 children的使用场景 比如页面左侧显示菜单,右侧显示不同菜单下的内容,类似如下element网站,那么右侧部分的内容就是当前页面的children 存在如下场景,点击导航一跳转至页面1,导航二跳转页面2,且页面1中存在子页面 路由js如下: const routes…

    2022年8月30日
    72800
  • windows KB4525245安装失败怎么解决

    根据情况可能不同,小编准备了五种解决方案 名列前茅种方案: 1.查看【windows update】、【app readiness】、【Cryptographic Services】、【Background Intelligent Transfer Service】、【Windows Install…

    2022年9月1日
    56900
  • 用于解析mac地址的协议是哪个

    用于解析mac地址的协议是“RARP”。RARP(反向地址转换协议)可以将MAC地址解析为IP地址,允许局域网的物理机器从网关服务器的ARP表或者缓存上请求其IP地址。RARP发出要反向解析的物理地址并希望返回其对应的IP地址,应答包括由能够提供所需信息的RARP服务器发出的IP地址。 本教程操作环…

    2022年9月18日
    63100
  • sql文件怎么导入数据库Navicat

    sql文件导入数据库Navicat: 1、首先需要创建数据库,然后在连接名字上右击建立“数据库”。 2、然后输入一个数据库的名称,这里举例为“utf-8”。 3、双击你新建的数据库,然后让其呈现绿色的状态,在数据右击选择“运行sql”。 4、之后点击右侧的三个点,来选择你要运行的“sql文件”。 5…

    2022年8月29日
    1.3K00
  • 如何实现C++程序释放后使用导致的漏洞分析

    1、释放后使用 当动态分配的内存释放时,该内存的内容是不确定的,有可能保持完整并可以被访问,因为什么时候重新分配或回收释放的内存块是内存管理程序决定的,但是,也可能该内存的内容已经被改变,导致意外的程序行为。因此,当内存释放之后,保证不再对它进行写入或读取。 2、 释放后使用的危害 由内存管理不当导…

    2022年9月20日
    45300
  • windows驱动精灵intel显卡dch驱动异常如何解决

    解决方法: 1、右击“此电脑”,点击“管理”。 2、选择左侧任务栏的“设备管理器”。 3、点击右侧任务栏“显示适配器”的下拉,选择“更新驱动程序”。 4、点击下方的“浏览我的计算机以查找驱动程序软件”。 5、接着点击“让我从计算机上的可用驱动程序列表中选取”。 6、选择“Microsoft 基本显示…

    2022年9月13日
    53500
  • windows是否只查看安全传送的网页内容如何关闭

    是否只查看安全传送的网页内容关闭方法: 1、首先打开浏览器,然后点击任务栏中的“工具”。 2、之后点击下拉菜单中的“Internet选项”。 3、再点击选项窗口中的“安全”。 4、接着去点击下面的“自定义级别”。 5、下拉找到“显示混合内容”。 6、勾选下面的“启动”。 7、最后点击确定即可。 以上…

    2022年8月31日
    60400
  • WordPress插件WooCommerce任意文件删除漏洞怎么解决

    技术细节 WordPress的权限处理机制主要是通过给不同角色提供不同的功能来实现的,当商铺管理员角色被定义之后,它便会给这个角色分配edit_users功能,这样他们就可以直接管理商铺的客户账号了。整个权限分配过程发生在插件插件饿的安装过程当中。woocommerce/includes/class…

    2022年8月30日
    56300
站长微信
站长微信
电话联系

400-800-1024

工作日9:30-21:00在线

分享本页
返回顶部