23-获取更新后的dom-1-vue源码分析

67 阅读4分钟

提升系列

提升系列,我主要是想写一些平时工作上面,自己会用到的一些好的技巧或者方法。当然,这些技巧和方法是针对我自己的。

除此之外,还可能会写一些解决难点的方法。比如,遇到了某个难点,要通过什么方法来解决它?

正如它的名字一样--提升,通过学习一些好的技巧、方法,或者解决一些难题、难点,来提升我们的能力。

出这个提升系列,一方面,是提升自己的开发能力;另一方面,也希望这个提升系列,可以帮助到部分人,提升他们的开发能力。

在这个系列里,我能想到的或者实现方法,不一定是最好的。欢迎大家参与讨论。

前言

前面有段时间,在工作上面,我遇到了一个问题:获取dom的高度有问题。为了解决这个问题,花费了我挺多的时间。现在趁着放假了,自己有空了,打算就这个问题,写一些文章。一方面,是作为记录自己怎么解决这个问题;另一方面,也希望把自己的解决方法,分享给大家,让大家可以从中学习到一些知识。

问题

我在项目里遇到的问题是,在页面里,经过一系列操作之后,会改变了dom的高度。但是在获取dom高度的时候,我获取到的,还是上一次dom的高度,即更新之前的高度。那要怎么获取更新之后dom的高度呢?

思路

怎么获取更新之后dom的高度?在解决这个问题之前,我想先介绍一下vue的源码。因为我解决问题的思路,其实也是参考了一下vue的源码。

在vue的源码里面,在nexttick部分,有介绍了一些方法,来进行延迟操作。

下面是我从vue官方仓库里拿到的vue源码,获取了部分关于nexttick的代码

let timerFunc
​
// The nextTick behavior leverages the microtask queue, which can be accessed
// via either native Promise.then or MutationObserver.
// MutationObserver has wider support, however it is seriously bugged in
// UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
// completely stops working after triggering a few times... so, if native
// Promise is available, we will use it:
/* istanbul ignore next, $flow-disable-line */
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  timerFunc = () => {
    p.then(flushCallbacks)
    // In problematic UIWebViews, Promise.then doesn't completely break, but
    // it can get stuck in a weird state where callbacks are pushed into the
    // microtask queue but the queue isn't being flushed, until the browser
    // needs to do some other work, e.g. handle a timer. Therefore we can
    // "force" the microtask queue to be flushed by adding an empty timer.
    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true
} else if (
  !isIE &&
  typeof MutationObserver !== 'undefined' &&
  (isNative(MutationObserver) ||
    // PhantomJS and iOS 7.x
    MutationObserver.toString() === '[object MutationObserverConstructor]')
) {
  // Use MutationObserver where native Promise is not available,
  // e.g. PhantomJS, iOS7, Android 4.4
  // (#6466 MutationObserver is unreliable in IE11)
  let counter = 1
  const observer = new MutationObserver(flushCallbacks)
  const textNode = document.createTextNode(String(counter))
  observer.observe(textNode, {
    characterData: true
  })
  timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  // Fallback to setImmediate.
  // Technically it leverages the (macro) task queue,
  // but it is still a better choice than setTimeout.
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
  // Fallback to setTimeout.
  timerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}

我们先分析一下,这段代码是怎么执行的:

  • 先是判断在运行环境里Promise是否存在,如果Promise存在,则说明支持Promise。那么就创建一个Promise对象,然后使用Promsie.then来延迟回调函数的执行
  • 如果Promise不支持,接着判断MutationObserver是否存在。如果存在,则利用MutationObserver来创建一个MutationObserver实例对象,监听dom的变化。
  • 如果MutationObserver不支持,则判断setImmediate是否存在。setImmediate存在,则利用setImmediate来延迟函数执行
  • 如果setImmediate不支持,最后就使用steTimeout,来延迟函数的执行。

通过上面的分析,我们可以看到,在vue的nexttick里,主要是在运行环境里面,依次对Promise、MutationObserver、setImmediate做判断,看是否支持它们。如果支持,则利用它们来延迟回调函数的执行。如果它们都不支持,最后会做兼容处理,利用setTimeout来延迟回调函数执行。

小结

本章节不是用来介绍vue的nexttick的源码的,主要是从它的源码里面,学习一下它的处理方法。看下它是怎么来等dom更新之后,延迟回调函数的执行。

我解决问题的思路,也是参考了vue nexttick的源码。我们将在后面几个章节里,进行详细介绍的。本章就先介绍一下vue的nexttick源码。

最后,放上自己比较喜欢的一句诗句:

千淘万漉虽辛苦,吹尽狂沙始到金 - 唐 刘禹锡《浪淘沙》