[貝聊科技]一個(gè)頁(yè)面阻塞問題的排查過程

本文作者:Mr.Luo 举畸,貝聊前端經(jīng)理,作者博客 http://heeroluo.net 凳枝。

從今年(2017年)年初起抄沮,我們團(tuán)隊(duì)開始引入「Vue.js」開發(fā)移動(dòng)端的產(chǎn)品。在某個(gè)項(xiàng)目的測(cè)試過程中岖瑰,測(cè)試妹子跟我們反饋了一個(gè)奇怪的bug:在一個(gè)播放音樂的頁(yè)面中叛买,有一個(gè)地方同步顯示音樂的當(dāng)前播放位置;音樂開始播放后蹋订,這個(gè)地方的內(nèi)容會(huì)不斷改變率挣,但是滾動(dòng)頁(yè)面后,內(nèi)容卻不再變化露戒,看起來像是某個(gè)環(huán)節(jié)被阻塞了椒功。

這個(gè)問題只在我們iOS的客戶端內(nèi)出現(xiàn),在微信和Safari內(nèi)卻毫無(wú)問題智什,這讓我們一度懷疑是受到客戶端某些代碼的影響动漾。但仔細(xì)排查過后,發(fā)現(xiàn)問題并沒有這么簡(jiǎn)單荠锭。

iOS中的WebView

iOS中的WebView有兩種:UIWebViewWKWebView旱眯。

WKWebView是從iOS 8開始提供的,除了帶來了更好的性能與更少的內(nèi)存占用外节沦,它還改良了在UIWebView里面的一些不好的體驗(yàn)键思,比如scroll事件的觸發(fā)。在UIWebView內(nèi)甫贯,只會(huì)在滾動(dòng)完全停止后才會(huì)觸發(fā)scroll事件吼鳞;而在WKWebView內(nèi),則是在滾動(dòng)過程中不斷觸發(fā)叫搁。

然而赔桌,WKWebView并非向下兼容UIWebView,更換成本不小渴逻,所以仍然有相當(dāng)一部分的APP還在使用UIWebView疾党,例如我們貝聊的APP,以及新浪微博惨奕。

即便如此雪位,我們讓iOS組的同事臨時(shí)用一個(gè)WKWebView打開問題頁(yè)來測(cè)試,卻是很簡(jiǎn)單的事情梨撞。實(shí)測(cè)結(jié)果是:在WKWebView內(nèi)不會(huì)有阻塞問題發(fā)生雹洗。

Demo

為了更好地重現(xiàn)這個(gè)問題香罐,我們做了一個(gè)demo頁(yè),關(guān)鍵代碼如下:

<template>
    <div>
        <audio ref="player" :src="audioURL" @timeupdate="updateTime" controls></audio>
        <div class="current-time">{{ time }}</div>
    </div>
</template>

<script>
export default {
    data() {
        return {
            audioURL: require('./music.mp3'),
            time: ''
        };
    },
    methods: {
        updateTime() {
            this.time = this.$refs.player.currentTime;
            document.title = this.time;
        }
    }
};
</script>

頁(yè)面功能非常簡(jiǎn)單时肿,播放音樂的時(shí)候庇茫,通過timeupdate事件去更新數(shù)據(jù)字段「time」的值,從而把當(dāng)前播放位置不斷地更新到界面上螃成。同時(shí)旦签,也把「time」的值更新到頁(yè)面標(biāo)題,這樣做的目的是檢查「time」的賦值是否成功寸宏。

用新浪微博APP打開此頁(yè)宁炫,運(yùn)行效果如下:

運(yùn)行效果

可以看到,滾動(dòng)頁(yè)面結(jié)束后氮凝,頁(yè)面內(nèi)的數(shù)字不再更新淋淀,但是標(biāo)題還在繼續(xù)變化。這說明了timeupdate事件是在不斷觸發(fā)的覆醇,「time」字段的值也是在不斷更新朵纷,但是數(shù)據(jù)變化后更新到界面(刷新DOM)的過程被阻塞了。

被阻塞的其實(shí)是...

恰巧永脓,我們?cè)诔霈F(xiàn)bug的產(chǎn)品頁(yè)中發(fā)現(xiàn)了另一個(gè)現(xiàn)象:出現(xiàn)阻塞問題后袍辞,頁(yè)面中調(diào)用客戶端的功能也被阻塞了。這又讓我們懷疑是客戶端的鍋常摧,但后來發(fā)現(xiàn)并不是搅吁。我們把客戶端的功能調(diào)用都封裝成了Promise,在調(diào)試過程中落午,我們發(fā)現(xiàn)該P(yáng)romise實(shí)例既無(wú)法進(jìn)入then的流程谎懦,也沒有進(jìn)入catch的流程

我們開始懷疑被阻塞的是Promise溃斋,于是就在demo中增加兩個(gè)按鈕「Button1」和「Button2」:

<template>
    <div>
        <audio ref="player" :src="audioURL" @timeupdate="updateTime" controls></audio>
        <div class="current-time">{{ time }}</div>
        <input type="button" value="Button1" @click="click1" />
        <input type="button" value="Button2" @click="click2" />
    </div>
</template>

<script>
export default {
    data() {
        return {
            audioURL: require('./music.mp3'),
            time: ''
        };
    },
    methods: {
        click1() { alert('click1'); },
        click2() {
            Promise.resolve().then(() => {
                alert('click2');
            });
        },
        updateTime() {
            this.time = this.$refs.player.currentTime;
            document.title = this.time;
        }
    }
};
</script>

就如料想的那樣界拦,點(diǎn)擊播放音樂并滾動(dòng)頁(yè)面后,點(diǎn)擊「Button1」彈出了「click 1」梗劫,但是點(diǎn)擊「Button2」卻沒有任何響應(yīng)享甸。這證明了被阻塞的確實(shí)就是Promise了。

罪魁禍?zhǔn)拙谷皇?..

找到了問題梳侨,就去搜索引擎找答案蛉威,但竟然搜到了「Vue.js」的源代碼。在本地打開該文件走哺,也確實(shí)有這片代碼:

Vue.js對(duì)阻塞問題的修復(fù)

從這里的注釋可以發(fā)現(xiàn)蚯嫌,「Vue.js」的開發(fā)團(tuán)隊(duì)也知道Promise在UIWebView下的阻塞問題,并進(jìn)行了修復(fù),但為什么在demo頁(yè)中仍然有問題呢择示?

排查bug很重要的一點(diǎn)就是盡量減少重現(xiàn)問題所需的代碼和依賴妒牙。于是,我用「Vue-CLI」初始化一個(gè)新項(xiàng)目对妄,并把demo頁(yè)放到此項(xiàng)目中。此時(shí)再用新浪微博打開頁(yè)面進(jìn)行同樣的操作敢朱,并沒有出現(xiàn)阻塞的問題剪菱。

然后,把項(xiàng)目中用到的「SASS」拴签、「postcss-px2rem」孝常、「Vuex」和「babel-polyfill」依次安裝,并在每次安裝后都重新打開demo頁(yè)進(jìn)行操作蚓哩。最后發(fā)現(xiàn)构灸,裝完「babel-polyfill」之后問題就重現(xiàn)了。

babel-polyfill

iOS 8以上的Safari和WebView都已經(jīng)支持Promise岸梨,但是實(shí)測(cè)發(fā)現(xiàn)喜颁,「babel-polyfill」會(huì)用自己的Promise覆蓋原生的Promise!查看「babel-polyfill」所依賴的「corejs」的代碼可以發(fā)現(xiàn)曹阔,它對(duì)Promise的特性檢查比較嚴(yán)格:

Promise特性檢查

由于iOS下的Promise并沒有完全支持這些特性半开,所以「corejs」用自己的Promise把原生的Promise覆蓋了。而且赃份,看起來「Vue.js」對(duì)阻塞問題的修復(fù)對(duì)「corejs」的Promise無(wú)效寂拆。

解決方案

解決方案有三個(gè):

  1. 不要安裝「babel-polyfill」,但這樣會(huì)造成舊版本瀏覽器下無(wú)法運(yùn)行「Vuex」抓韩。
  2. 把UIWebView更換為WKWebView纠永,但這不是短期內(nèi)可以完成的事情。
  3. 加載「babel-polyfill」后谒拴,把瀏覽器的Promise重置回原生的Promise尝江。

考慮到那些額外的特性在實(shí)際開發(fā)中基本用不上,方案3反而是一種比較好的臨時(shí)解決方案英上。

先調(diào)整「babel-polyfill」的引入方式茂装,把它的代碼文件通過其他方式傳到服務(wù)器上。然后修改項(xiàng)目入口文件善延,也就是根目錄下的「index.html」:

<script>
var _Promise;
// 檢查是否iOS9+(iOS9+才支持Symbol)
var useNativePromise = typeof Promise === 'function' &&    
    /^(iPhone|iPad|iPod)/.test(navigator.platform) &&
   typeof Symbol === 'function';
if (useNativePromise) { _Promise = Promise; }
</script>
<script src="http://s2.imgbeiliao.com/assets/js/lib/babel-polyfill/6.23.0/polyfill.min.js"></script>
<script>
if (_Promise) { Promise = _Promise; }
</script>

上面代碼的流程就是:檢查到是iOS>=9時(shí)少态,就把原生Promise保存下來,待「babel-polyfill」加載執(zhí)行完之后易遣,再把保存下來的Promise覆蓋回去彼妻。那iOS<9的怎么辦呢?測(cè)試妹子很不容易找到了一臺(tái)iOS 8的iPhone來測(cè)試,結(jié)論是不會(huì)出現(xiàn)阻塞問題侨歉,所以iOS<9可以不用管了屋摇。

既然「babel-polyfill」已通過script標(biāo)簽引入,那就可以刪除對(duì)它的依賴了:

npm uninstall babel-polyfill --save

然后修改「/build/webpack.base.conf.js」幽邓,移除「babel-polyfill」的打包入口:

entry: {
    // app: ['babel-polyfill', './src/main.js']
    app: ['./src/main.js']
}

這種臨時(shí)的解決方案其實(shí)并不優(yōu)雅炮温,讓客戶端盡快更換為WKWebView才是正道。

后記

最近蘋果發(fā)布了iOS 11牵舵。在iOS 11的WebView中柒啤,Promise已經(jīng)是完全體,可以通過「corejs」的特性檢查畸颅,所以不會(huì)再有這個(gè)阻塞的問題担巩。

本文同步發(fā)布在 https://zhuanlan.zhihu.com/p/29515460

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市没炒,隨后出現(xiàn)的幾起案子涛癌,更是在濱河造成了極大的恐慌,老刑警劉巖送火,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拳话,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡种吸,警方通過查閱死者的電腦和手機(jī)假颇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骨稿,“玉大人笨鸡,你說我怎么就攤上這事√构冢” “怎么了形耗?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)辙浑。 經(jīng)常有香客問我激涤,道長(zhǎng),這世上最難降的妖魔是什么判呕? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任倦踢,我火速辦了婚禮,結(jié)果婚禮上侠草,老公的妹妹穿的比我還像新娘辱挥。我一直安慰自己,他們只是感情好边涕,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布晤碘。 她就那樣靜靜地躺著褂微,像睡著了一般。 火紅的嫁衣襯著肌膚如雪园爷。 梳的紋絲不亂的頭發(fā)上宠蚂,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音童社,去河邊找鬼求厕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛扰楼,可吹牛的內(nèi)容都是我干的呀癣。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼灭抑,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了抵代?” 一聲冷哼從身側(cè)響起腾节,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎荤牍,沒想到半個(gè)月后案腺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡康吵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年劈榨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晦嵌。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡同辣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惭载,到底是詐尸還是另有隱情旱函,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布描滔,位于F島的核電站棒妨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏含长。R本人自食惡果不足惜券腔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拘泞。 院中可真熱鬧纷纫,春花似錦、人聲如沸陪腌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至商叹,卻和暖如春燕刻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背剖笙。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工卵洗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人弥咪。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓过蹂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親聚至。 傳聞我的和親對(duì)象是個(gè)殘疾皇子酷勺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件扳躬、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,033評(píng)論 4 62
  • 環(huán)縣一中 劉緒洲 師:同學(xué)們脆诉,今天我們來學(xué)習(xí)歸有光的《項(xiàng)脊軒志》,請(qǐng)大家先閱讀課文注釋①贷币,了解相關(guān)作家作品知識(shí)击胜。 ...
    學(xué)舟語(yǔ)文閱讀 2,995評(píng)論 1 1
  • 你是我在他鄉(xiāng)偶遇的失散多年的兄弟 你是一本書的封面我是封底 你是我饑餓時(shí)的糧食病中的藥 你是我的曉夢(mèng)怕一睜眼你便飛去
    醒后夢(mèng)不還閱讀 162評(píng)論 0 2
  • 練習(xí):與神對(duì)話 我先用了數(shù)息法和漸進(jìn)法放松,然后用下樓梯法引導(dǎo)自己穿過任意門役纹。想象自己正在走下一段白色的階梯偶摔,每走...
    EmmaJW閱讀 357評(píng)論 0 0