Преглед изворни кода

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.
Evan You пре 9 година
родитељ
комит
9fb1057672
2 измењених фајлова са 24 додато и 48 уклоњено
  1. 22 42
      src/core/util/env.js
  2. 2 6
      src/platforms/web/util/index.js

+ 22 - 42
src/core/util/env.js

@@ -1,6 +1,5 @@
 /* @flow */
 
-/* global MutationObserver */
 // can we use __proto__?
 export const hasProto = '__proto__' in {}
 
@@ -9,40 +8,24 @@ export const inBrowser =
   typeof window !== 'undefined' &&
   Object.prototype.toString.call(window) !== '[object Object]'
 
-// detect devtools
-export const devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__
-
-// UA sniffing for working around browser-specific quirks
 export const UA = inBrowser && window.navigator.userAgent.toLowerCase()
-const isIos = UA && /(iphone|ipad|ipod|ios)/i.test(UA)
-const iosVersionMatch = UA && isIos && UA.match(/os ([\d_]+)/)
-const iosVersion = iosVersionMatch && iosVersionMatch[1].split('_').map(Number)
+export const isIE = UA && /msie|trident/.test(UA)
+export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
+export const isAndroid = UA && UA.indexOf('android') > 0
 
-// MutationObserver is unreliable in iOS 9.3 UIWebView
-// detecting it by checking presence of IndexedDB
-// ref #3027
-const hasMutationObserverBug =
-  iosVersion &&
-  !window.indexedDB && (
-    iosVersion[0] > 9 || (
-      iosVersion[0] === 9 &&
-      iosVersion[1] >= 3
-    )
-  )
+// detect devtools
+export const devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__
 
 /**
  * Defer a task to execute it asynchronously. Ideally this
- * should be executed as a microtask, so we leverage
- * MutationObserver if it's available, and fallback to
- * setTimeout(0).
- *
- * @param {Function} cb
- * @param {Object} ctx
+ * should be executed as a microtask, but MutationObserver is unreliable
+ * in iOS UIWebView so we use a setImmediate shim and fallback to setTimeout.
  */
 export const nextTick = (function () {
   let callbacks = []
   let pending = false
   let timerFunc
+
   function nextTickHandler () {
     pending = false
     const copies = callbacks.slice(0)
@@ -53,27 +36,24 @@ export const nextTick = (function () {
   }
 
   /* istanbul ignore else */
-  if (typeof MutationObserver !== 'undefined' && !hasMutationObserverBug) {
-    var counter = 1
-    var observer = new MutationObserver(nextTickHandler)
-    var textNode = document.createTextNode(String(counter))
-    observer.observe(textNode, {
-      characterData: true
+  if (inBrowser && window.postMessage &&
+    !window.importScripts && // not in WebWorker
+    !(isAndroid && !window.requestAnimationFrame) // not in Android <= 4.3
+  ) {
+    const NEXT_TICK_TOKEN = '__vue__nextTick__'
+    window.addEventListener('message', e => {
+      if (e.source === window && e.data === NEXT_TICK_TOKEN) {
+        nextTickHandler()
+      }
     })
-    timerFunc = function () {
-      counter = (counter + 1) % 2
-      textNode.data = String(counter)
+    timerFunc = () => {
+      window.postMessage(NEXT_TICK_TOKEN, '*')
     }
   } else {
-    // webpack attempts to inject a shim for setImmediate
-    // if it is used as a global, so we have to work around that to
-    // avoid bundling unnecessary code.
-    var context = inBrowser
-      ? window
-      : typeof global !== 'undefined' ? global : {}
-    timerFunc = context.setImmediate || setTimeout
+    timerFunc = (typeof global !== 'undefined' && global.setImmediate) || setTimeout
   }
-  return function (cb: Function, ctx?: Object) {
+
+  return function queueNextTick (cb: Function, ctx?: Object) {
     const func = ctx
       ? function () { cb.call(ctx) }
       : cb

+ 2 - 6
src/platforms/web/util/index.js

@@ -1,16 +1,12 @@
 /* @flow */
 
-import { warn, inBrowser } from 'core/util/index'
+import { warn } from 'core/util/index'
+export { inBrowser, isIE, isIE9, isAndroid } from 'core/util/env'
 
 export * from './attrs'
 export * from './class'
 export * from './element'
 
-const UA = inBrowser && window.navigator.userAgent.toLowerCase()
-export const isIE = UA && /msie|trident/.test(UA)
-export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
-export const isAndroid = UA && UA.indexOf('android') > 0
-
 /**
  * Query an element selector if it's not an element already.
  */