提升系列
提升系列,我主要是想写一些平时工作上面,自己会用到的一些好的技巧或者方法。当然,这些技巧和方法是针对我自己的。
除此之外,还可能会写一些解决难点的方法。比如,遇到了某个难点,要通过什么方法来解决它?
正如它的名字一样--提升,通过学习一些好的技巧、方法,或者解决一些难题、难点,来提升我们的能力。
出这个提升系列,一方面,是提升自己的开发能力;另一方面,也希望这个提升系列,可以帮助到部分人,提升他们的开发能力。
在这个系列里,我能想到的或者实现方法,不一定是最好的。欢迎大家参与讨论。
前言
前面有段时间,在工作上面,我遇到了一个问题:获取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源码。
最后,放上自己比较喜欢的一句诗句:
千淘万漉虽辛苦,吹尽狂沙始到金 - 唐 刘禹锡《浪淘沙》