1
1
/* @flow */
2
+ /* globals MutationObserver */
3
+
4
+ import { noop } from 'shared/util'
2
5
3
6
// can we use __proto__?
4
7
export const hasProto = '__proto__' in { }
@@ -13,61 +16,84 @@ export const isIE = UA && /msie|trident/.test(UA)
13
16
export const isIE9 = UA && UA . indexOf ( 'msie 9.0' ) > 0
14
17
export const isEdge = UA && UA . indexOf ( 'edge/' ) > 0
15
18
export const isAndroid = UA && UA . indexOf ( 'android' ) > 0
19
+ export const isIOS = UA && / i p h o n e | i p a d | i p o d | i o s / . test ( UA )
16
20
17
21
// detect devtools
18
22
export const devtools = inBrowser && window . __VUE_DEVTOOLS_GLOBAL_HOOK__
19
23
24
+ function isNative ( Ctor : Function ) : boolean {
25
+ return / n a t i v e c o d e / . test ( Ctor . toString ( ) )
26
+ }
27
+
20
28
/**
21
29
* Defer a task to execute it asynchronously. Ideally this
22
30
* should be executed as a microtask, but MutationObserver is unreliable
23
31
* in iOS UIWebView so we use a setImmediate shim and fallback to setTimeout.
24
32
*/
25
33
export const nextTick = ( function ( ) {
26
- let callbacks = [ ]
34
+ const callbacks = [ ]
27
35
let pending = false
28
36
let timerFunc
29
37
30
38
function nextTickHandler ( ) {
31
39
pending = false
32
40
const copies = callbacks . slice ( 0 )
33
- callbacks = [ ]
41
+ callbacks . length = 0
34
42
for ( let i = 0 ; i < copies . length ; i ++ ) {
35
43
copies [ i ] ( )
36
44
}
37
45
}
38
46
39
- /* istanbul ignore else */
40
- if ( inBrowser && window . postMessage &&
41
- ! window . importScripts && // not in WebWorker
42
- ! ( isAndroid && ! window . requestAnimationFrame ) // not in Android <= 4.3
43
- ) {
44
- const NEXT_TICK_TOKEN = '__vue__nextTick__'
45
- window . addEventListener ( 'message' , e => {
46
- if ( e . source === window && e . data === NEXT_TICK_TOKEN ) {
47
- nextTickHandler ( )
48
- }
47
+ // the nextTick behavior leverages the microtask queue, which can be accessed
48
+ // via either native Promise.then or MutationObserver.
49
+ // MutationObserver has wider support, however it is seriously bugged in
50
+ // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
51
+ // completely stops working after triggering a few times... so, if native
52
+ // Promise is available, we will use it:
53
+ if ( typeof Promise !== 'undefined' && isNative ( Promise ) ) {
54
+ var p = Promise . resolve ( )
55
+ timerFunc = ( ) => {
56
+ p . then ( nextTickHandler )
57
+ // in problematic UIWebViews, Promise.then doesn't completely break, but
58
+ // it can get stuck in a weird state where callbacks are pushed into the
59
+ // microtask queue but the queue isn't being flushed, until the browser
60
+ // needs to do some other work, e.g. handle a timer. Therefore we can
61
+ // "force" the microtask queue to be flushed by adding an empty timer.
62
+ if ( isIOS ) setTimeout ( noop )
63
+ }
64
+ } else if ( typeof MutationObserver !== 'undefined' ) {
65
+ // use MutationObserver where native Promise is not available,
66
+ // e.g. IE11, iOS7, Android 4.4
67
+ var counter = 1
68
+ var observer = new MutationObserver ( nextTickHandler )
69
+ var textNode = document . createTextNode ( String ( counter ) )
70
+ observer . observe ( textNode , {
71
+ characterData : true
49
72
} )
50
73
timerFunc = ( ) => {
51
- window . postMessage ( NEXT_TICK_TOKEN , '*' )
74
+ counter = ( counter + 1 ) % 2
75
+ textNode . data = String ( counter )
52
76
}
53
77
} else {
54
- timerFunc = ( typeof global !== 'undefined' && global . setImmediate ) || setTimeout
78
+ // fallback to setTimeout
79
+ timerFunc = setTimeout
55
80
}
56
81
57
82
return function queueNextTick ( cb : Function , ctx ?: Object ) {
58
83
const func = ctx
59
84
? function ( ) { cb . call ( ctx ) }
60
85
: cb
61
86
callbacks . push ( func )
62
- if ( pending ) return
63
- pending = true
64
- timerFunc ( nextTickHandler , 0 )
87
+ if ( ! pending ) {
88
+ pending = true
89
+ timerFunc ( nextTickHandler , 0 )
90
+ }
65
91
}
66
92
} ) ( )
67
93
68
94
let _Set
69
95
/* istanbul ignore if */
70
- if ( typeof Set !== 'undefined' && / n a t i v e c o d e / . test ( Set . toString ( ) ) ) {
96
+ if ( typeof Set !== 'undefined' && isNative ( Set ) ) {
71
97
// use native Set when available.
72
98
_Set = Set
73
99
} else {
0 commit comments