Skip to content

Commit 9fb1057

Browse files
committed
Adjust nextTick implementation
MutationObserver is unreliable in UIWebView on iOS >= 9.3.3, and this hasn't been fixed in iOS10. Moreover it is tedious to identify UIWebView vs. WKWebView and native Safari now that iOS10 UIWebView supports IndexedDB as well. We are switching to a setImmediate shim using window.postMessage. This uses macrotask instead of microtask, but it doesn't actually affect Vue's logic.
1 parent 7eb6c8c commit 9fb1057

File tree

2 files changed

+24
-48
lines changed

2 files changed

+24
-48
lines changed

src/core/util/env.js

Lines changed: 22 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/* @flow */
22

3-
/* global MutationObserver */
43
// can we use __proto__?
54
export const hasProto = '__proto__' in {}
65

@@ -9,40 +8,24 @@ export const inBrowser =
98
typeof window !== 'undefined' &&
109
Object.prototype.toString.call(window) !== '[object Object]'
1110

12-
// detect devtools
13-
export const devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__
14-
15-
// UA sniffing for working around browser-specific quirks
1611
export const UA = inBrowser && window.navigator.userAgent.toLowerCase()
17-
const isIos = UA && /(iphone|ipad|ipod|ios)/i.test(UA)
18-
const iosVersionMatch = UA && isIos && UA.match(/os ([\d_]+)/)
19-
const iosVersion = iosVersionMatch && iosVersionMatch[1].split('_').map(Number)
12+
export const isIE = UA && /msie|trident/.test(UA)
13+
export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
14+
export const isAndroid = UA && UA.indexOf('android') > 0
2015

21-
// MutationObserver is unreliable in iOS 9.3 UIWebView
22-
// detecting it by checking presence of IndexedDB
23-
// ref #3027
24-
const hasMutationObserverBug =
25-
iosVersion &&
26-
!window.indexedDB && (
27-
iosVersion[0] > 9 || (
28-
iosVersion[0] === 9 &&
29-
iosVersion[1] >= 3
30-
)
31-
)
16+
// detect devtools
17+
export const devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__
3218

3319
/**
3420
* Defer a task to execute it asynchronously. Ideally this
35-
* should be executed as a microtask, so we leverage
36-
* MutationObserver if it's available, and fallback to
37-
* setTimeout(0).
38-
*
39-
* @param {Function} cb
40-
* @param {Object} ctx
21+
* should be executed as a microtask, but MutationObserver is unreliable
22+
* in iOS UIWebView so we use a setImmediate shim and fallback to setTimeout.
4123
*/
4224
export const nextTick = (function () {
4325
let callbacks = []
4426
let pending = false
4527
let timerFunc
28+
4629
function nextTickHandler () {
4730
pending = false
4831
const copies = callbacks.slice(0)
@@ -53,27 +36,24 @@ export const nextTick = (function () {
5336
}
5437

5538
/* istanbul ignore else */
56-
if (typeof MutationObserver !== 'undefined' && !hasMutationObserverBug) {
57-
var counter = 1
58-
var observer = new MutationObserver(nextTickHandler)
59-
var textNode = document.createTextNode(String(counter))
60-
observer.observe(textNode, {
61-
characterData: true
39+
if (inBrowser && window.postMessage &&
40+
!window.importScripts && // not in WebWorker
41+
!(isAndroid && !window.requestAnimationFrame) // not in Android <= 4.3
42+
) {
43+
const NEXT_TICK_TOKEN = '__vue__nextTick__'
44+
window.addEventListener('message', e => {
45+
if (e.source === window && e.data === NEXT_TICK_TOKEN) {
46+
nextTickHandler()
47+
}
6248
})
63-
timerFunc = function () {
64-
counter = (counter + 1) % 2
65-
textNode.data = String(counter)
49+
timerFunc = () => {
50+
window.postMessage(NEXT_TICK_TOKEN, '*')
6651
}
6752
} else {
68-
// webpack attempts to inject a shim for setImmediate
69-
// if it is used as a global, so we have to work around that to
70-
// avoid bundling unnecessary code.
71-
var context = inBrowser
72-
? window
73-
: typeof global !== 'undefined' ? global : {}
74-
timerFunc = context.setImmediate || setTimeout
53+
timerFunc = (typeof global !== 'undefined' && global.setImmediate) || setTimeout
7554
}
76-
return function (cb: Function, ctx?: Object) {
55+
56+
return function queueNextTick (cb: Function, ctx?: Object) {
7757
const func = ctx
7858
? function () { cb.call(ctx) }
7959
: cb

src/platforms/web/util/index.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
/* @flow */
22

3-
import { warn, inBrowser } from 'core/util/index'
3+
import { warn } from 'core/util/index'
4+
export { inBrowser, isIE, isIE9, isAndroid } from 'core/util/env'
45

56
export * from './attrs'
67
export * from './class'
78
export * from './element'
89

9-
const UA = inBrowser && window.navigator.userAgent.toLowerCase()
10-
export const isIE = UA && /msie|trident/.test(UA)
11-
export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
12-
export const isAndroid = UA && UA.indexOf('android') > 0
13-
1410
/**
1511
* Query an element selector if it's not an element already.
1612
*/

0 commit comments

Comments
 (0)