• 聊聊vue的nextTick方法


    前言

    nextTick是面试常考的vue中的一个重要知识点,但是很多小伙伴常常无法真正的理解nextTick的执行机制,并且背后包含的许多vue的重要知识。本文会把nextTick聊的非常细,让大家彻底搞懂它。

    正文

    那么在nextTick正式登场之前呢,我们先看一个例子:

    页面上有20个li元素,当点击更新列表按钮时,页面会增加10个里li元素。假如现在为了提供用户更好的体验,将页面滚动到新添加元素中最后一个元素的位置,scrollIntoView 原生JS自带的方法,滚动到指定的dom结构上去,behavior: 'smooth' 指定什么类型的滚动行为。 那么该如何去书写?下面的写法可以吗?

    1. <template>
    2. <div>
    3. <button @click="updateList">更新列表button>
    4. <ul>
    5. <li v-for="n in list">{{ n }}li>
    6. ul>
    7. div>
    8. template>
    9. <script setup>
    10. import { ref } from 'vue';
    11. const list = ref(new Array(20).fill(0))
    12. const updateList = () => {
    13. //解构是因为我们要一个一个元素push进去,而不是整个数组
    14. list.value.push(...(new Array(10).fill(1)))
    15. const liItem = document.querySelector('li:last-child')
    16. liItem.scrollIntoView({ behavior: 'smooth' })
    17. }
    18. script>
    19. <style lang="css" scoped>
    20. li{
    21. height: 100px;
    22. background-color: #21e07a;
    23. margin: 10px;
    24. }
    25. style>

    很明显,页面滚动到一半就停止了,并没有滚到最后一个元素处,这是为什么呢? 这里我们可以复习一下vue中的生命周期的相关知识。

    vue组件渲染的整个过程过程中,setup语法糖也就是组合式api中的逻辑最先执行,其次才是编译模板、挂载组件、渲染DOM节点。那么在这个例子中,当点击按钮后,执行点击事件的回调函数中逻辑是最先执行的,然后添加li元素,再去获取页面更新后的最后一个DOM节点。但是页面更新DOM节点是需要时间的,执行document.querySelector('li:last-child')时能获取到哪取决于电脑的性能,所以在添加多个DOM节点后很可能无法滚动到最后一个节点就会停止。

    那么怎么解决呢?

    没错,用nextTick来解决。

    nextTick是什么

    是vue内置的一个api,在页面渲染完成后执行回调函数中的逻辑。接受一个回调函数作为参数。nextTick 是异步操作,且属于微任务。 所以会比 类似于setTimeout这样的延迟函数 更快执行。

    那么很显然,这个场景非常适合用nextTick(仅展示JS部分):

    1. <script setup>
    2. import { nextTick, ref } from 'vue';
    3. import { myNextTick } from './next-tick';
    4. const list = ref(new Array(20).fill(0))
    5. const updateList = () => {
    6. //解构是因为我们要一个一个元素push进去,而不是整个数组
    7. list.value.push(...(new Array(10).fill(1)))
    8. nextTick(() => { //保证浏览器更新dom完毕后
    9. const liItem = document.querySelector('li:last-child')
    10. // scrollIntoView 原生JS自带的方法,滚动到指定的dom结构上去
    11. // behavior: 'smooth' 指定什么类型的滚动行为
    12. liItem.scrollIntoView({ behavior: 'smooth' })
    13. })
    14. }
    15. script>
    怎么样,是不是顺畅多了!

    手写nextTick

    nextTick的手写也相对比较简单,那么下面是实现代码:

    1. function myNextTick(fn) {
    2. let app = document.getElementById('app')
    3. // 使用MutationObserver方法监听dom
    4. var observerOptions = {
    5. childList: true, // 观察目标子节点的变化,是否有添加或者删除
    6. attributes: true, // 观察属性变动
    7. subtree: true, // 观察后代节点,默认为 false
    8. };
    9. // 要保证fn在dom更新完成后再调用
    10. // 创建一个DOM监听器
    11. let observer = new MutationObserver((el,obs) => {
    12. //当被监听的DOM节点更新完成时,该回调会触发
    13. fn()
    14. })
    15. //将目标节点和callback绑定,并定义observerOptions来配置需要监听dom的哪些变化
    16. observer.observe(app,observerOptions)
    17. }

    传入的fn是需要执行的回调函数。那么如何保证fn在页面渲染完成后才调用呢?

    这里使用了JS原生的MutationObserver方法监视DOM 树。MutationObserver方法接受回调函数(callback)作为参数。

    这里关键使用了MutationObserver构造函数身上的observe方法,去配置 MutationObserver 在 DOM 更改匹配给定选项时,通过其回调函数开始接收通知,这里的给定选项就是observerOptions对象中的属性。当 observer 发现匹配观察请求中指定的配置项的更改时,callback() 方法便会被调用。所以只要将fn放入callback中调用即可。

    最后

    欢迎在评论区留言!

  • 相关阅读:
    第1章 数据结构的概念
    强化服务器安全!CentOS 7如何使用fail2ban防范SSH暴力破解攻击?
    Vue项目中实现拖拽排序效果-demo
    唯物辩证法-条件论
    SpringBoot-启动扩展点
    文件服务之FTP
    【JavaEE】_Servlet程序的编写方法
    Linux基础命令
    JVM内存和垃圾回收-03.运行时数据区概述及线程
    【文档】开发者常用术语
  • 原文地址:https://blog.csdn.net/qq_36877763/article/details/136707965