react-native 性能優(yōu)化

什么樣的app才是一個優(yōu)秀的app呢?
  • 安裝包的體積小
  • 啟動速度快
  • 使用流暢擎浴、不卡頓
  • 用戶交互友好
  • 報錯或者閃退次數(shù)少

一歉嗓、安裝包大小

1赫段、第三方庫部分引用

使用第三方庫會增加安裝包的大小乌询,有一些第三方庫寫得很完美(代碼很多)榜贴,但是我們可能只需使用庫里面很小的一部分代碼。這種情況下妹田,我們可以把相應(yīng)的代碼拷貝到項目里面唬党,而不是直接引用整個庫。比如:助手里面使用了SDWebImage第三方庫鬼佣,里面支持很多圖片格式驶拱,但是項目中只是為了支持gif圖片,因此我們只需要導(dǎo)入與gif相關(guān)的文件沮趣。

2屯烦、資源文件壓縮

項目中一般都會有大量的圖片資源文件坷随,如果沒有進(jìn)行壓縮房铭,會增加app的體積。使用TinyPNG去進(jìn)行壓縮温眉,一般可以減少50%-70%的大小缸匪。手動使用tiny去壓縮,不但會增加開發(fā)時間类溢,而且容易遺忘凌蔬,因此建議寫成腳本露懒,在項目執(zhí)行打包的時候去統(tǒng)一處理。

3砂心、定期清除沒有使用的資源文件懈词、代碼

版本迭代會造成一些資源文件或者代碼被棄用,建議定期檢查項目辩诞,刪掉一些沒用到的資源文件(腳本坎弯?)和代碼。

二译暂、首屏渲染優(yōu)化

1抠忘、拆包

在 react-native 執(zhí)行 JS 代碼之前,必須將代碼加載到內(nèi)存中并進(jìn)行解析外永。如果你加載了一個 50MB 的 bundle崎脉,那么所有的 50mb 都必須被加載和解析才能被執(zhí)行,這會導(dǎo)致首屏渲染時間變得很長伯顶。使用拆包技術(shù)將bundle拆成不同的業(yè)務(wù)包, 啟動時根據(jù)需要加載相應(yīng)的模塊囚灼,之后再逐漸按需加載更多的包。

(1)moles-packer:由攜程框架團(tuán)隊研發(fā)的砾淌,與攜程moles框架配套使用的React Native 打包和拆包工具啦撮,同時支持原生的 React Native 項目。重寫了react native自帶的打包工具汪厨,適合RN0.4.0版本之前的分包赃春。維護(hù)少,現(xiàn)在基本沒有多少人使用劫乱,兼容性差织中。

(2)diff patch:由谷歌團(tuán)隊研發(fā),兼容多種語言衷戈。原理是舊包與新包進(jìn)行差異比對狭吼,生成補丁文件,壓縮上傳到服務(wù)器殖妇。app請求接口把補丁文件下載到本地刁笙,與舊包合成新包。
優(yōu)點:打包生成的補丁文件體積星ぁ疲吸;
缺點:跨版本升級的時候不容易維護(hù)
git: https://github.com/google/diff-match-patch

(3)metro bundle:facebook官方提供的,支持RN 0.50及以上版本前鹅,并隨著RN版本的迭代不斷完善摘悴。文檔鏈接:https://facebook.github.io/metro/docs/en/getting-started

2、內(nèi)聯(lián)引用

內(nèi)聯(lián)引用(require 代替 import)可以延遲模塊或文件的加載舰绘,直到實際需要該文件蹂喻。

import React, { Component } from 'react';
import { TouchableOpacity, View, Text } from 'react-native';

let VeryExpensive = null;

export default class Optimized extends Component {
  state = { needsExpensive: false };

  didPress = () => {
    if (VeryExpensive == null) {
      VeryExpensive = require('./VeryExpensive').default;
    }

    this.setState(() => ({
      needsExpensive: true,
    }));
  };

  render() {
    return (
      <View style={{ marginTop: 20 }}>
        <TouchableOpacity onPress={this.didPress}>
          <Text>Load</Text>
        </TouchableOpacity>
        {this.state.needsExpensive ? <VeryExpensive /> : null}
      </View>
    );
  }
}

3葱椭、bundle預(yù)加載

調(diào)用require會造成額外的開銷。因為當(dāng)遇到尚未加載的模塊時口四,require需要通過bridge來發(fā)送消息孵运。這主要會影響到啟動速度,因為在應(yīng)用程序加載初始模塊時可能觸發(fā)相當(dāng)大量的請求調(diào)用蔓彩。幸運的是掐松,我們可以配置一部分模塊進(jìn)行預(yù)加載。具體的步驟可以參考官方文檔:https://reactnative.cn/docs/performance/

三粪小、app的流暢度

使用 React Native 替代基于 WebView 的框架來開發(fā) App 的一個強有力的理由大磺,就是為了使 App 可以達(dá)到每秒 60 幀(足夠流暢),并且能有類似原生 App 的外觀和手感探膊。因此我們也盡可能地優(yōu)化 React Native 去實現(xiàn)這一目標(biāo)杠愧,使開發(fā)者能集中精力處理 App 的業(yè)務(wù)邏輯,而不用費心考慮性能逞壁。但是流济,總還是有一些地方有所欠缺,以及在某些場合 React Native 還不能夠替你決定如何進(jìn)行優(yōu)化(用原生代碼寫也無法避免)腌闯,因此人工的干預(yù)依然是必要的绳瘟。

1、減少不必要的渲染

在一個復(fù)雜應(yīng)用的根組件上調(diào)用了this.setState姿骏,從而導(dǎo)致一次開銷很大的子組件樹的重繪糖声,可想而知,這可能會花費 200ms 也就是整整 12 幀的丟失分瘦。此時蘸泻,任何由 JavaScript 控制的動畫都會卡住。只要卡頓超過 100ms嘲玫,用戶就會明顯的感覺到悦施。

你可以實現(xiàn)shouldComponentUpdate函數(shù)來指明在什么樣的確切條件下,你希望這個組件得到重繪去团。如果你編寫的是純粹的組件(界面完全由 props 和 state 所決定)抡诞,你可以利用PureComponent來為你做這個工作。再強調(diào)一次土陪,不可變的數(shù)據(jù)結(jié)構(gòu)(immutable昼汗,即對于引用類型數(shù)據(jù),不修改原值旺坠,而是復(fù)制后修改并返回新值)在提速方面非常有用 —— 當(dāng)你不得不對一個長列表對象做一個深度的比較乔遮,它會使重繪你的整個組件更加快速扮超,而且代碼量更少取刃。

2蹋肮、使用動畫改變圖片的尺寸時,UI 線程掉幀

在 iOS 上璧疗,每次調(diào)整 Image 組件的寬度或者高度坯辩,都需要重新裁剪和縮放原始圖片。這個操作開銷會非常大崩侠,尤其是大的圖片漆魔。比起直接修改尺寸,更好的方案是使用transform: [{scale}]的樣式屬性來改變尺寸却音。比如當(dāng)你點擊一個圖片改抡,要將它放大到全屏的時候,就可以使用這個屬性系瓢。

3阿纤、Touchable 系列組件的優(yōu)化

有些時候,如果我們有一項操作與點擊事件所帶來的透明度改變或者高亮效果發(fā)生在同一幀中夷陋,那么有可能在onPress函數(shù)結(jié)束之前我們都看不到這些效果欠拾。比如在onPress執(zhí)行了一個setState的操作,這個操作需要大量計算工作并且導(dǎo)致了掉幀骗绕。對此的一個解決方案是將onPress處理函數(shù)中的操作封裝到requestAnimationFrame中:

handleOnPress() {
  this.requestAnimationFrame(() => {
    this.doExpensiveAction();
  });
}
4藐窄、動畫優(yōu)化

React Native 提供了兩個互補的動畫系統(tǒng):用于創(chuàng)建精細(xì)的交互控制的動畫Animated和用于全局的布局動畫LayoutAnimation

Animated的接口一般會在 JavaScript 線程中計算出所需要的每一個關(guān)鍵幀,而LayoutAnimation則利用了Core Animation酬土,使動畫不會被 JS 線程和主線程的掉幀所影響荆忍。但是,LayoutAnimation只工作在“一次性”的動畫上("靜態(tài)"動畫) -- 如果動畫可能會被中途取消撤缴,你還是需要使用Animated东揣。

5、耗時的操作等動畫結(jié)束后再進(jìn)行

Interactionmanager可以將一些耗時較長的工作安排到所有互動或動畫完成之后再進(jìn)行腹泌。這樣可以保證JavaScript動畫的流暢運行嘶卧。

InteractionManager.runAfterInteractions(() => {
  // ...耗時較長的同步的任務(wù)...
});
6、ListView 初始化渲染太慢以及列表過長時滾動性能太差

用新的FlatList或者SectionList組件替代凉袱。除了簡化了API芥吟,這些新的列表組件在性能方面都有了極大的提升, 其中最主要的一個是無論列表有多少行,它的內(nèi)存使用都是常數(shù)級的专甩。

如果你的FlatList渲染得很慢, 請確保你使用了getItemLayout钟鸵,它通過跳過對items的處理來優(yōu)化你的渲染速度。除此之后涤躲,你還可以使用initialNumToRender設(shè)置首屏渲染數(shù)量棺耍。

四、用戶體驗優(yōu)化

1种樱、TextInput
  • 自動聚焦第一個字段
  • 使用占位符文本作為預(yù)期數(shù)據(jù)格式的示例
  • 文本格式化(電話蒙袍、身份證俊卤、銀行卡)
  • 選擇鍵盤類型(例如電子郵件,數(shù)字)
  • 確保返回按鈕聚焦下一個字段或提交表單
  • 點擊空白處收起鍵盤
  • 編輯時顯示清除按鈕
2害幅、鍵盤可見時管理布局

軟件鍵盤幾乎占據(jù)了屏幕的一半消恍。如果您有可以被鍵盤覆蓋的交互式元素,請確保使用該KeyboardAvoidingView組件仍可訪問它們以现。

3狠怨、擴大響應(yīng)區(qū)域

在手機上按按鈕時很難非常精確。確保所有交互式元素都是44x44或更大邑遏。一種方法是為元素留出足夠的空間padding佣赖,minWidth并且minHeight樣式值對此有用〖呛校或者茵汰,您可以使用[hitSlop]屬性來增加交互區(qū)域而不影響布局。

4孽鸡、使用Android Ripple

Android API 21+使用材質(zhì)設(shè)計紋波蹂午,在用戶觸摸屏幕上的可交互區(qū)域時為其提供反饋。React Native通過TouchableNativeFeedback組件公開它彬碱。使用這種可觸摸效果而不是不透明度或高光效果通常會讓您的應(yīng)用感覺更適合平臺豆胸。也就是說,使用它時需要小心巷疼,因為它不適用于iOS或Android API <21晚胡,因此您需要回退使用iOS上的其他可觸摸組件之一。您可以使用像react-native-platform-touchable這樣的庫來為您處理平臺差異嚼沿。

五估盘、錯誤上報

1、官方提供了方法:global.ErrorUtils.setGlobalHandler來監(jiān)聽全局的錯誤骡尽,可以參考以下代碼:
global.ErrorUtils.setGlobalHandler((e) => {
      if (!e || !(e instanceof Error) || !e.stack) return {}
      try {
        const stack = e.stack.toString().split(/\r\n|\n/), frameRE = /:(\d+:\d+)[^\d]*$/
        while (stack.length) {
          const frame = frameRE.exec(stack.shift())
          if (frame) {
            // 堆棧信息里面的行數(shù)和列數(shù)
            const position = frame[1].split(':')
            return { line: position[0], column: position[1] }
          }
        }
      } catch ( e ) {
        return {}
      }
    })
 
2遣妥、使用SourceMap查看

我們可以通過完整的srouceMap和出錯的line、column來準(zhǔn)確還原發(fā)生錯誤時代碼上下文攀细。在RN當(dāng)中我們可以在執(zhí)行打包命令的時候帶上--sourcemap-output youbundle.map來生成箫踩。

const { SourceMapConsumer, SourceMapGenerator, SourceNode } = require('source-map')
const fs = require('fs')

/**
 * rawMap
 */
const rawMap = JSON.parse(fs.readFileSync('./index.map').toString())

const smc = new SourceMapConsumer(rawMap)
const position = smc.originalPositionFor({
    line: 1,
    column: 75422
  })

const { source, line, column } = position

// output
console.log(`事故發(fā)生現(xiàn)場:${source},位于第${line}行谭贪,第${column}列境钟!`)
3、第三方庫:Sentry
  • 提供了完善的客戶端SDK俭识,Android慨削、iOS、react-native、web全平臺支持缚态。各端只需配置上報地址磁椒,其余SDK全搞定;
  • 支持上傳sourcemap進(jìn)行源碼定位猿规。由于RN運行在客戶端中,與普通運行在瀏覽器的js相比錯誤堆棧有自己的特點宙橱。Sentry是目前調(diào)研中唯一一個默認(rèn)支持react-native錯誤分析的平臺姨俩。
  • Sentry從SDK到后端服務(wù)都開源,可以私有化部署

文檔:https://docs.sentry.io/clients/react-native/
github:https://github.com/getsentry/sentry-react-native

參考鏈接

  1. 性能:https://reactnative.cn/docs/performance/
  2. 體驗優(yōu)化:https://reactnative.cn/docs/improvingux/
  3. 錯誤上報: https://hasaki.xyz/blog/2017-09-12-rn%E6%80%A7%E8%83%BD%E6%A3%80%E6%B5%8B%E5%92%8C%E9%94%99%E8%AF%AF%E6%8D%95%E6%8D%89/
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末师郑,一起剝皮案震驚了整個濱河市环葵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宝冕,老刑警劉巖张遭,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異地梨,居然都是意外死亡菊卷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門宝剖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洁闰,“玉大人,你說我怎么就攤上這事万细∑嗣迹” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵赖钞,是天一觀的道長腰素。 經(jīng)常有香客問我,道長雪营,這世上最難降的妖魔是什么弓千? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮献起,結(jié)果婚禮上计呈,老公的妹妹穿的比我還像新娘。我一直安慰自己征唬,他們只是感情好捌显,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著总寒,像睡著了一般扶歪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天善镰,我揣著相機與錄音妹萨,去河邊找鬼。 笑死炫欺,一個胖子當(dāng)著我的面吹牛乎完,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播品洛,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼树姨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了桥状?” 一聲冷哼從身側(cè)響起帽揪,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎辅斟,沒想到半個月后转晰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡士飒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年查邢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酵幕。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡侠坎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出裙盾,到底是詐尸還是另有隱情实胸,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布番官,位于F島的核電站庐完,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏徘熔。R本人自食惡果不足惜门躯,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酷师。 院中可真熱鬧讶凉,春花似錦、人聲如沸山孔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽台颠。三九已至褐望,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瘫里。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工实蔽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谨读。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓局装,卻偏偏與公主長得像狰晚,于是被迫代替她去往敵國和親弹灭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

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