New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix the bug of UIwebview.MutationObserver not working exception in iO… #3027

Merged
merged 4 commits into from Jun 4, 2016

Conversation

Projects
None yet
3 participants
@miccycn
Contributor

miccycn commented Jun 3, 2016

Vue.js the bug of UIwebview.MutationObserver not working exception in iOS 9.3.*

Vue.js version

1.0.24

Reproduction Link

https://github.com/miccycn/vue-bug-demo

Steps to reproduce

IOS 9.3.x系统的以下浏览器中打开demo页面:

  • QQ浏览器
  • QQ Webview
  • 支付宝webview
  • 微博webview
  • UC浏览器
  • Opera
  • 百度浏览器:

执行以下动作:

  • 1.在灰色区域内随意高频大幅度滑动(让滚动条有滚动);
  • 2.点击左上角的按钮

What is Expected? What is actually happening?

点击按钮后,弹出“Right”的是Vue正常工作,弹出“Error”是Vue出现了异常。

我用v-text方法在按钮上输出arr.length。同时我在这里的click事件中用原生方法做了一个强制判断:

this.arr.length == document.getElementById("button").textContent

而在IOS 9.3.x的这些出现异常的浏览器中这时候arr.length的真实数值和按钮上显示的不一样,所以会弹“Error”。

目前已知在IOS 9.3.x以下浏览器中运行是正常的:

  • 360浏览器
  • firefox
  • safari
  • chrome
  • 微信webview

在IOS9.2及以下的系统版本中所有浏览器都是正常的。

通过审查Vue的源码,发现问题出在util/env.js中全局MutationObserver的监听。

  /* istanbul ignore if */
  if (typeof MutationObserver !== 'undefined' && !(isWechat && isIos)) {
    var counter = 1
    var observer = new MutationObserver(nextTickHandler)
    var textNode = document.createTextNode(counter)
    observer.observe(textNode, {
      characterData: true
    })
    timerFunc = function () {
      counter = (counter + 1) % 2
      textNode.data = counter
    }
  } 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.
    const context = inBrowser
      ? window
      : typeof global !== 'undefined' ? global : {}
    timerFunc = context.setImmediate || setTimeout
  }

v1.0.24里这段不计入代码覆盖率测试的代码中,还特意对IOS的微信webview做了强制降级处理(函数的异步执行不使用MutationObserver而采用setTimeout(0)),所以在微信webview下没有这个异常。尝试取消对IOS的微信的特殊处理后,微信webview也出现了这个问题。

经过实验测试基本可以认为是浏览器底层对MutationObserver的异步执行出现了问题,在浏览器屏幕滚动或回弹的过程中如果同时让touch的一些默认事件触发,会产生对MutationObserver的阻塞,以至于MutationObserver的回调函数不去执行。造成后后面一系列的异常。

IOS 9.3对系统底层的touch、click等很多事件进行了重写,取消了300ms延迟可能会连带着在一些莫名其妙的地方带来阻塞,特别容易对一些涉及异步的原生方法产生影响。

有异常的国产浏览器的共同点是他们都是UIWebView,表现正常的浏览器(safari除外)均采用了WKWebView。

(补充:在IOS中,WKWebView是IOS8以后才有的,比UIWebView更新,性能也更好,现在还没推广开来,国内厂商大都还在用UIWebView。)

我最后提交了一个PR,改成对IOS 9.3的所有UIWebView在MutationObserver这个方法上做降级处理。(UIWebView不支持IndexedDB)

把源码中对ios微信的hack删掉了,毕竟实在太dirty……不过PR里面我对IOS9.3的判断这里我处理得也很dirty,这个与系统的UIwebview相关的bug估计以后不一定会修复,用系统版本来做判断毕竟不是长久之计。这个issue的异常就需要尤大大进一步考虑了。

如果不改Vue的源码,在这里对touch事件全部preventDefault掉,所有默认的触屏事件都用其他库来实现也是解决方法之一。但是我们的业务不允许我们preventDefault掉所有touchmove,所以只有在Vue源码中找原因了。

@yyx990803

This comment has been minimized.

Show comment
Hide comment
@yyx990803

yyx990803 Jun 3, 2016

Member

多谢详细的报告!这个问题可能还有其他的解决方式,比如用 Promise.then 模拟 nextTick,待我测试一下。 Promise 在这些浏览器里同样不靠谱,还是用 setTimeout 吧...

测试例 live link http://yyx990803.github.io/vue-bug-demo/test.html

Member

yyx990803 commented Jun 3, 2016

多谢详细的报告!这个问题可能还有其他的解决方式,比如用 Promise.then 模拟 nextTick,待我测试一下。 Promise 在这些浏览器里同样不靠谱,还是用 setTimeout 吧...

测试例 live link http://yyx990803.github.io/vue-bug-demo/test.html

Show outdated Hide outdated src/util/env.js Outdated
Show outdated Hide outdated src/util/env.js Outdated
@yyx990803

This comment has been minimized.

Show comment
Hide comment
@yyx990803

yyx990803 Jun 3, 2016

Member

调整了一下:

const iosVersionMatch = isIos && UA.match(/os ([\d_]+)/)
const iosVersion = iosVersionMatch && iosVersionMatch[1].split('_')
const hasMutationObserverBug =
  iosVersion &&
  Number(iosVersion[0]) >= 9 &&
  Number(iosVersion[1]) >= 3 &&
  !window.indexedDB
Member

yyx990803 commented Jun 3, 2016

调整了一下:

const iosVersionMatch = isIos && UA.match(/os ([\d_]+)/)
const iosVersion = iosVersionMatch && iosVersionMatch[1].split('_')
const hasMutationObserverBug =
  iosVersion &&
  Number(iosVersion[0]) >= 9 &&
  Number(iosVersion[1]) >= 3 &&
  !window.indexedDB

miccycn added some commits Jun 4, 2016

Update env.js
Fixed: MutationObserver not working in iOS 9.3.* UIWebView

@yyx990803 yyx990803 merged commit 80ac5c6 into vuejs:dev Jun 4, 2016

1 check passed

ci/circleci Your tests passed on CircleCI!
Details

yyx990803 added a commit that referenced this pull request Jun 6, 2016

@GitHubXur

This comment has been minimized.

Show comment
Hide comment
@GitHubXur

GitHubXur Sep 21, 2016

@yyx990803

下面的代码只针对ios9.3的bug,现在ios10也出现了9.3滑动过快卡死的问题,
问题在 Number(iosVersion[1]) >= 3 这个判断。
当前系统版本:10.0.1
vuejs版本:1.0.26

// util/env.js

export const isIos = UA && /(iphone|ipad|ipod|ios)/i.test(UA)
export const iosVersionMatch = isIos && UA.match(/os ([\d_]+)/)
export const iosVersion = iosVersionMatch && iosVersionMatch[1].split('_')

// detecting iOS UIWebView by indexedDB
export const hasMutationObserverBug = iosVersion && Number(iosVersion[0]) >= 9 && Number(iosVersion[1]) >= 3 && !window.indexedDB

GitHubXur commented Sep 21, 2016

@yyx990803

下面的代码只针对ios9.3的bug,现在ios10也出现了9.3滑动过快卡死的问题,
问题在 Number(iosVersion[1]) >= 3 这个判断。
当前系统版本:10.0.1
vuejs版本:1.0.26

// util/env.js

export const isIos = UA && /(iphone|ipad|ipod|ios)/i.test(UA)
export const iosVersionMatch = isIos && UA.match(/os ([\d_]+)/)
export const iosVersion = iosVersionMatch && iosVersionMatch[1].split('_')

// detecting iOS UIWebView by indexedDB
export const hasMutationObserverBug = iosVersion && Number(iosVersion[0]) >= 9 && Number(iosVersion[1]) >= 3 && !window.indexedDB
@yyx990803

This comment has been minimized.

Show comment
Hide comment
@yyx990803

yyx990803 Sep 21, 2016

Member

@GitHubXur thanks, fixed in 9c6a341

Member

yyx990803 commented Sep 21, 2016

@GitHubXur thanks, fixed in 9c6a341

@GitHubXur

This comment has been minimized.

Show comment
Hide comment
@GitHubXur

GitHubXur Sep 21, 2016

非常感谢,处理速度真快

GitHubXur commented Sep 21, 2016

非常感谢,处理速度真快

@GitHubXur

This comment has been minimized.

Show comment
Hide comment
@GitHubXur

GitHubXur Sep 21, 2016

@yyx990803 经过测试,依然有问题

//问题在 &&

const hasMutationObserverBug =
  iosVersion &&
  !window.indexedDB && (
    iosVersion[0] > 9 || (
      iosVersion[0] === 9 &&
      iosVersion[1] >= 3
    )
)

// 我的理解:window.indexedDB不存在,或者 是ios9.3以上版本 都是为true ,表示是有bug的
// 修改为:
const hasMutationObserverBug =
  !window.indexedDB ||  (iosVersion && (
    iosVersion[0] > 9 || (
      iosVersion[0] === 9 &&
      iosVersion[1] >= 3
    )
))

GitHubXur commented Sep 21, 2016

@yyx990803 经过测试,依然有问题

//问题在 &&

const hasMutationObserverBug =
  iosVersion &&
  !window.indexedDB && (
    iosVersion[0] > 9 || (
      iosVersion[0] === 9 &&
      iosVersion[1] >= 3
    )
)

// 我的理解:window.indexedDB不存在,或者 是ios9.3以上版本 都是为true ,表示是有bug的
// 修改为:
const hasMutationObserverBug =
  !window.indexedDB ||  (iosVersion && (
    iosVersion[0] > 9 || (
      iosVersion[0] === 9 &&
      iosVersion[1] >= 3
    )
))
@yyx990803

This comment has been minimized.

Show comment
Hide comment
@yyx990803

yyx990803 Sep 21, 2016

Member

@GitHubXur 应该和你想的不是一回事。indexedDB 是用来检测是不是 UIWebView 的。

可能是 iOS 10 下的 useragent string 变了,我没升级,你能不能看下 iOS 10 下 UA 和 iosVersion 这两个变量分别是什么值

Member

yyx990803 commented Sep 21, 2016

@GitHubXur 应该和你想的不是一回事。indexedDB 是用来检测是不是 UIWebView 的。

可能是 iOS 10 下的 useragent string 变了,我没升级,你能不能看下 iOS 10 下 UA 和 iosVersion 这两个变量分别是什么值

@GitHubXur

This comment has been minimized.

Show comment
Hide comment
@GitHubXur

GitHubXur Sep 22, 2016

@yyx990803 抱歉,回复晚了

UA:

mozilla/5.0 (iphone; cpu iphone os 10_0_1 like mac os x) applewebkit/602.1.50 (khtml, like gecko) mobile/14a403 micromessenger/6.3.25 nettype/wifi language/zh_cn

iosVersion: 10,0,1

GitHubXur commented Sep 22, 2016

@yyx990803 抱歉,回复晚了

UA:

mozilla/5.0 (iphone; cpu iphone os 10_0_1 like mac os x) applewebkit/602.1.50 (khtml, like gecko) mobile/14a403 micromessenger/6.3.25 nettype/wifi language/zh_cn

iosVersion: 10,0,1

@yyx990803

This comment has been minimized.

Show comment
Hide comment
@yyx990803

yyx990803 Sep 22, 2016

Member

那逻辑上应该没有错啊。hasMutationObserverBug 的值是什么?

Member

yyx990803 commented Sep 22, 2016

那逻辑上应该没有错啊。hasMutationObserverBug 的值是什么?

@GitHubXur

This comment has been minimized.

Show comment
Hide comment
@GitHubXur

GitHubXur Sep 22, 2016

@yyx990803 我知道了,问题出在window.indexedDB ,ios10 window.indexedDB = true

GitHubXur commented Sep 22, 2016

@yyx990803 我知道了,问题出在window.indexedDB ,ios10 window.indexedDB = true

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment