⚡️ React Native 啓動速度優化——JS 篇【全網最全,值得收藏】

若是你喜歡個人文章,但願點贊👍 收藏 📁 評論 💬 三連支持一下,謝謝你,這對我真的很重要!

前言

上一篇文章主要從 Native 的角度分析了 React Native 的初始化流程,並從源碼出發,總結了幾個 React Native 容器初始化的優化點。本文主要從 JavaScript 入手,總結了一些 JS 側的優化要點。javascript

1.JSEngine

rn_start_jsEngine

Hermes

Hermes 是 FaceBook 2019 年中旬開源的一款 JS 引擎,從 release 記錄能夠看出,這個是專爲 React Native 打造的 JS 引擎,能夠說從設計之初就是爲 Hybrid UI 系統打造。前端

Hermes 支持直接加載字節碼,也就是說,BabelMinifyParseCompile 這些流程所有都在開發者電腦上完成,直接下發字節碼讓 Hermes 運行就行,這樣作能夠省去 JSEngine 解析編譯 JavaScript 的流程,JS 代碼的加載速度將會大大加快,啓動速度也會有很是大的提高。java

Hermes

更多關於 Hermes 的特性,你們能夠看個人舊文《移動端 JS 引擎哪家強》這篇文章,我作了更爲詳細的特性說明與數據對比,這裏就很少說了。react

2.JS Bundle

rn_start_jsBundle

前面的優化其實都是 Native 層的優化,從這裏開始就進入 Web 前端最熟悉的領域了。android

其實談到 JS Bundle 的優化,來來回回就是那麼幾條路:webpack

  • :縮小 Bundle 的整體積,減小 JS 加載和解析的時間
  • :動態導入(dynamic import),懶加載,按需加載,延遲執行
  • :拆分公共模塊和業務模塊,避免公共模塊重複引入

若是有 webpack 打包優化經驗的小夥伴,看到上面的優化方式,是否是腦海中已經浮現出 webpack 的一些配置項了?不過 React Native 的打包工具不是 webpack 而是 Facebook 自研的 Metro,雖然配置細節不同,但道理是相通的,下面我就這幾個點講講 React Native 如何優化 JS Bundle。git

2.1 減少 JS Bundle 體積

Metro 打包 JS 時,會把 ESM 模塊轉爲 CommonJS 模塊,這就致使如今比較火的依賴於 ESM 的 Tree Shaking 徹底不起做用,並且根據官方回覆,Metro 將來也不會支持 Tree Shaking :github

(Tree Shaking 太 low 了,咱們作了個更酷的 Hermes)

由於這個緣由,咱們減少 bundle 體積主要是三個方向:web

  • 對於一樣的功能,優先選擇體積更小的第三方庫
  • 利用 babel 插件,避免全量引用
  • 制定編碼規範,減小重複代碼

下面咱們舉幾個例子來解釋上面的三個思路。算法

2.1.0 使用 react-native-bundle-visualizer 查看包體積

優化 bundle 文件前,必定要知道 bundle 裏有些什麼,最好的方式就是用可視化的方式把全部的依賴包列出來。web 開發中,能夠藉助 Webpack 的 webpack-bundle-analyzer 插件查看 bundle 的依賴大小分佈,React Native 也有相似的工具,能夠藉助 react-native-bundle-visualizer 查看依賴關係:

Xnip2021-04-15_20-36-59

使用很是簡單,按照文檔安裝分析就可。


2.1.1 moment.js 替換爲 day.js

這是一個很是經典的例子。一樣是時間格式化的第三方庫, moment.js 體積 200 KB,day.js 體積只有 2KB,並且 API 與 moment.js 保持一致。若是項目裏用了 moment.js,替換爲 day.js 後能夠立馬減小 JSBundle 的體積。


2.1.2 lodah.js 配合 babel-plugin-lodash

lodash 基本上屬於 Web 前端的工程標配了,可是對於大多數人來講,對於 lodash 封裝的近 300 個函數,只會用經常使用的幾個,例如 getchunk,爲了這幾個函數全量引用仍是有些浪費的。

社區上面對這種場景,固然也有優化方案,好比說 lodash-es,以 ESM 的形式導出函數,再借助 Webpack 等工具的 Tree Sharking 優化,就能夠只保留引用的文件。可是就如前面所說,React Native 的打包工具 Metro 不支持 Tree Shaking,因此對於 lodash-es 文件,其實還會全量引入,並且 lodash-es 的全量文件比 lodash 要大得多。

我作了個簡單的測試,對於一個剛剛初始化的 React Native 應用,全量引入 lodash 後,包體積增大了 71.23KB,全量引入 lodash-es 後,包體積會擴大 173.85KB。

既然 lodash-es 不適合在 RN 中用,咱們就只能在 lodash 上想辦法了。lodash 其實還有一種用法,那就是直接引用單文件,例如想用 join 這個方法,咱們能夠這樣引用:

// 全量
import { join } from 'lodash'

// 單文件引用
import join from 'lodash/join'

這樣打包的時候就會只打包 lodash/join 這一個文件。

可是這樣作仍是太麻煩了,好比說咱們要使用 lodash 的七八個方法,那咱們就要分別 import 七八次,很是的繁瑣。對於 lodash 這麼熱門的工具庫,社區上確定有高人安排好了,babel-plugin-lodash 這個 babel 插件,能夠在 JS 編譯時操做 AST 作以下的自動轉換:

import { join, chunk } from 'lodash'
// ⬇️
import join from 'lodash/join'
import chunk from 'lodash/chunk'

使用方式也很簡單,首先運行 yarn add babel-plugin-lodash -D 安裝,而後在 babel.config.js 文件裏啓用插件便可:

// babel.config.js

module.exports = {
  plugins: ['lodash'],
  presets: ['module:metro-react-native-babel-preset'],
};

我以 join 這個方法爲例,你們能夠看一下各個方法增長的 JS Bundle 體積:

全量 lodash 全量 loads-es lodash/join 單文件引用 lodash + babel-plugin-lodash
71.23 KB 173.85 KB 119 Bytes 119 Bytes

從表格可見 lodash 配合 babel-plugin-lodash 是最優的開發選擇。


2.1.3 babel-plugin-import 的使用

babel-plugin-lodash 只能轉換 lodash 的引用問題,其實社區還有一個很是實用的 babel 插件:babel-plugin-import基本上它能夠解決全部按需引用的問題

我舉個簡單的例子,阿里有個很好用的 ahooks 開源庫,封裝了不少經常使用的 React hooks,但問題是這個庫是針對 Web 平臺封裝的,好比說 useTitle 這個 hook,是用來設置網頁標題的,可是 React Native 平臺是沒有相關的 BOM API 的,因此這個 hooks 徹底沒有必要引入,RN 也永遠用不到這個 API。

這時候咱們就能夠用 babel-plugin-import 實現按需引用了,假設咱們只要用到 useInterval 這個 Hooks,咱們如今業務代碼中引入:

import { useInterval } from 'ahooks'

而後運行 yarn add babel-plugin-import -D 安裝插件,在 babel.config.js 文件裏啓用插件:

// babel.config.js

module.exports = {
  plugins: [
    [
      'import',
      {
        libraryName: 'ahooks',
        camel2DashComponentName: false, // 是否須要駝峯轉短線
        camel2UnderlineComponentName: false, // 是否須要駝峯轉下劃線
      },
    ],
  ],
  presets: ['module:metro-react-native-babel-preset'],
};

啓用後就能夠實現 ahooks 的按需引入:

import { useInterval } from 'ahooks'
// ⬇️
import useInterval from 'ahooks/lib/useInterval'

下面是各類狀況下的 JSBundle 體積增量,綜合來看 babel-plugin-import 是最優的選擇:

全量 ahooks ahooks/lib/useInterval 單文件引用 ahooks + babel-plugin-import
111.41 KiB 443 Bytes 443 Bytes

固然,babel-plugin-import 能夠做用於不少的庫文件,好比說內部/第三方封裝的 UI 組件,基本上均可以經過babel-plugin-import 的配置項實現按需引入。如有需求,能夠看網上其餘人總結的使用經驗,我這裏就很少言了。


2.1.4 babel-plugin-transform-remove-console

移除 console 的 babel 插件也頗有用,咱們能夠配置它在打包發佈的時候移除 console 語句,減少包體積的同時還會加快 JS 運行速度,咱們只要安裝後再簡單的配置一下就行了:

// babel.config.js

module.exports = {
    presets: ['module:metro-react-native-babel-preset'],
    env: {
        production: {
            plugins: ['transform-remove-console'],
        },
    },
};


2.1.5 制定良好的編碼規範

編碼規範的最佳實踐太多了,爲了切合主題(減小代碼體積),我就隨便舉幾點:

  • 代碼的抽象和複用:代碼中重複的邏輯根據可複用程度,儘可能抽象爲一個方法,不要用一次複製一次
  • 刪除無效的邏輯:這個也很常見,隨着業務的迭代,不少代碼都不會用了,若是某個功能下線了,就直接刪掉,哪天要用到再從 git 記錄裏找
  • 刪除冗餘的樣式:例如引入 ESLint plugin for React Native,開啓 "react-native/no-unused-styles" 選項,藉助 ESLint 提示無效的樣式文件

說實話這幾個優化其實減小不了幾 KB 的代碼,更大的價值在於提高項目的健壯性和可維護性

2.2 Inline Requires

Inline Requires 能夠理解爲懶執行,注意我這裏說的不是懶加載,由於通常狀況下,RN 容器初始化以後會全量加載解析 JS Bundle 文件,Inline Requires 的做用是延遲運行,也就是說只有須要使用的時候纔會執行 JS 代碼,而不是啓動的時候就執行。React Native 0.64 版本里,默認開啓了 Inline Requires

首先咱們要在 metro.config.js 裏確認開啓了 Inline Requires 功能:

// metro.config.js

module.exports = {
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true, // <-- here
      },
    }),
  },
};

其實 Inline Requires 的原理很是簡單,就是把 require 導入的位置改變了一下。

好比說咱們寫了個工具函數 join 放在 utils.js 文件裏:

// utils.js

export function join(list, j) {
  return list.join(j);
}

而後咱們在 App.js 裏 import 這個庫:

// App.js

import { join } from 'my-module';

const App = (props) => {
  const result = join(['a', 'b', 'c'], '~');

  return <Text>{result}</Text>;
};

上面的寫法,被 Metro 編譯後,至關於編譯成下面的樣子:

const App = (props) => {
  const result = require('./utils').join(['a', 'b', 'c'], '~');

  return <Text>{result}</Text>;
};

實際編譯後的代碼其實長這個樣子:

rn_start_inlineRequire

上圖紅線中的 r() 函數,實際上是 RN 本身封裝的 require() 函數,能夠看出 Metro 自動把頂層的 import 移動到使用的位置。

值得注意的是,Metro 的自動 Inline Requires 配置,目前是不支持 export default 導出的,也就是說,若是你的 join 函數是這樣寫的:

export default function join(list, j) {
  return list.join(j);
}

導入時是這樣的:

import join from './utils';

const App = (props) => {
  const result = join(['a', 'b', 'c'], '~');

  return <Text>{result}</Text>;
};

Metro 編譯轉換後的代碼,對應的 import 仍是處於函數頂層

rn_start_require

這個須要特別注意一下,社區也有相關的文章,呼籲你們不要用 export default 這個語法,感興趣的能夠了解一下:

深刻解析 ES Module(一):禁用 export default object

深刻解析 ES Module(二):完全禁用 default export

2.3 JSBundle 分包加載

分包的場景通常出如今 Native 爲主,React Native 爲輔的場景裏。這種場景每每是這樣的:

  • 假設有兩條基於 RN 的業務線 A 和 B,它們的 JSBundle 都是動態下發的
  • A 的 JSBundle 大小爲 700KB,其中包括 600KB 的基礎包(React,React Native 的 JS 代碼)和 100KB 的業務代碼
  • A 的 JSBundle 大小爲 800KB,其中包括 600KB 的基礎包和 200KB 的業務代碼
  • 每次從 Native 跳轉到 A/B 的 RN 容器,都要全量下載解析運行

你們從上面的例子裏能夠看出,600KB 的基礎包在多條業務線裏是重複的,徹底沒有必要屢次下載和加載,這時候一個想法天然而然就出來了:

把一些共有庫打包到一個 common.bundle 文件裏,咱們每次只要動態下發業務包 businessA.bundlebusinessB.bundle,而後在客戶端實現先加載 common.bundle 文件,再加載 business.bundle 文件就能夠了

這樣作的好處有幾個:

  • common.bundle 能夠直接放在本地,省去多業務線的屢次下載,節省流量和帶寬
  • 能夠在 RN 容器預初始化的時候就加載 common.bundle二次加載的業務包體積更小,初始化速度更快

順着上面的思路,上面問題就會轉換爲兩個小問題:

  • 如何實現 JSBundle 的拆包?
  • iOS/Android 的 RN 容器如何實現多 bundle 加載?


2.3.1 JS Bundle 拆包

拆包以前要先了解一下 Metro 這個打包工具的工做流程。Metro 的打包流程很簡單,只有三個步驟:

  • Resolution:能夠簡單理解爲分析各個模塊的依賴關係,最後會生成一個依賴圖
  • Transformation:代碼的編譯轉換,主要是藉助 Babel 的編譯轉換能力
  • Serialization:全部代碼轉換完畢後,打印轉換後的代碼,生成一個或者多個 bundle 文件

從上面流程能夠看出,咱們的拆包步驟只會在 Serialization 這一步。咱們只要藉助 Serialization 暴露的各個方法就能夠實現 bundle 分包了。

正式分包前,咱們先拋開各類技術細節,把問題簡化一下:對於一個全是數字的數組,如何把它分爲偶數數組和奇數數組?

這個問題太簡單了,剛學編程的人應該都能想到答案,遍歷一遍原數組,若是當前元素是奇數,就放到奇數數組裏,若是是偶數,放偶數數組裏。

Metro 對 JS bundle 分包實際上是一個道理。Metro 打包的時候,會給每一個模塊設置 moduleId,這個 id 就是一個從 0 開始的自增 number。咱們分包的時候,公有的模塊(例如 react react-native)輸出到 common.bundle,業務模塊輸出到 business.bundle 就好了。

由於要兼顧多條業務線,如今業內主流的分包方案是這樣的:

1.先創建一個 common.js 文件,裏面引入了全部的公有模塊,而後 Metro 以這個 common.js 爲入口文件,打一個 common.bundle 文件,同時要記錄全部的公有模塊的 moduleId

// common.js

require('react');
require('react-native');
......

2. 對業務線 A 進行打包,Metro 的打包入口文件就是 A 的項目入口文件。打包過程當中要過濾掉上一步記錄的公有模塊 moduleId,這樣打包結果就只有 A 的業務代碼了

// indexA.js

import {AppRegistry} from 'react-native';
import BusinessA from './BusinessA';
import {name as appName} from './app.json';

AppRegistry.registerComponent(appName, () => BusinessA);

3. 業務線 B C D E...... 打包流程同業務線 A


上面的思路看起來很美好,可是仍是存在一個問題:每次啓動 Metro 打包的時候,moduleId 都是從 0 開始自增,這樣會致使不一樣的 JSBundle ID 重複

爲了不 id 重複,目前業內主流的作法是把模塊的路徑看成 moduleId(由於模塊的路徑基本上是固定且不衝突的),這樣就解決了 id 衝突的問題。Metro 暴露了 createModuleIdFactory 這個函數,咱們能夠在這個函數裏覆蓋原來的自增 number 邏輯:

module.exports = {
  serializer: {
    createModuleIdFactory: function () {
      return function (path) {
        // 根據文件的相對路徑構建 ModuleId
        const projectRootPath = __dirname;
        let moduleId = path.substr(projectRootPath.length + 1);
        return moduleId;
      };
    },
  },
};


整合一下第一步的思路,就能夠構建出下面的 metro.common.config.js 配置文件:

// metro.common.config.js

const fs = require('fs');

module.exports = {
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),
  },
  serializer: {
    createModuleIdFactory: function () {
      return function (path) {
        // 根據文件的相對路徑構建 ModuleId
        const projectRootPath = __dirname;
        let moduleId = path.substr(projectRootPath.length + 1);
        
        // 把 moduleId 寫入 idList.txt 文件,記錄公有模塊 id
        fs.appendFileSync('./idList.txt', `${moduleId}\n`);
        return moduleId;
      };
    },
  },
};

而後運行命令行命令打包便可:

# 打包平臺:android
# 打包配置文件:metro.common.config.js
# 打包入口文件:common.js
# 輸出路徑:bundle/common.android.bundle

npx react-native bundle --platform android --config metro.common.config.js --dev false --entry-file common.js --bundle-output bundle/common.android.bundle

經過以上命令的打包,咱們能夠看到 moduleId 都轉換爲了相對路徑,而且 idList.txt 也記錄了全部的 moduleId:

common.android.bundle

idList.js


第二步的關鍵在於過濾公有模塊的 moduleId,Metro 提供了 processModuleFilter 這個方法,藉助它能夠實現模塊的過濾。具體的邏輯可見如下代碼:

// metro.business.config.js

const fs = require('fs');

// 讀取 idList.txt,轉換爲數組
const idList = fs.readFileSync('./idList.txt', 'utf8').toString().split('\n');

function createModuleId(path) {
  const projectRootPath = __dirname;
  let moduleId = path.substr(projectRootPath.length + 1);
  return moduleId;
}

module.exports = {
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),
  },
  serializer: {
    createModuleIdFactory: function () {
      // createModuleId 的邏輯和 metro.common.config.js 徹底同樣
      return createModuleId;
    },
    processModuleFilter: function (modules) {
      const mouduleId = createModuleId(modules.path);
      
      // 經過 mouduleId 過濾在 common.bundle 裏的數據
      if (idList.indexOf(mouduleId) < 0) {
        console.log('createModuleIdFactory path', mouduleId);
        return true;
      }
      return false;
    },
  },
};

最後運行命令行命令打包便可:

# 打包平臺:android
# 打包配置文件:metro.business.config.js
# 打包入口文件:index.js
# 輸出路徑:bundle/business.android.bundle

npx react-native bundle --platform android --config metro.business.config.js --dev false --entry-file index.js --bundle-output bundle/business.android.bundle

最後的打包結果只有 11 行(不分包的話得 398 行),能夠看出分包的收益很是大。

business.android.bundle

固然使用相對路徑做爲 moduleId 打包時,不可避免的會致使包體積變大,咱們可使用 md5 計算一下相對路徑,而後取前幾位做爲最後的 moduleId;或者仍是採用遞增 id,只不過使用更復雜的映射算法來保證 moduleId 的惟一性和穩定性。這部分的內容其實屬於很是經典的 Map key 設計問題,感興趣的讀者能夠了解學習一下相關的算法理論知識。


2.3.2 Native 實現多 bundle 加載

分包只是第一步,想要展現完整正確的 RN 界面,還須要作到「合」,這個「合」就是指在 Native 端實現多 bundle 的加載。

common.bundle 的加載比較容易,直接在 RN 容器初始化的時候加載就行了。容器初始化的流程上一節我已經詳細介紹了,這裏就很少言了。這時候問題就轉換爲 business.bundle 的加載問題。

React Native 不像瀏覽器的多 bundle 加載,直接動態生成一個 <script /> 標籤插入 HTML 中就能夠實現動態加載了。咱們須要結合具體的 RN 容器實現來實現 business.bundle 加載的需求。這時候咱們須要關注兩個點:

  1. 時機:何時開始加載?
  2. 方法:如何加載新的 bundle?


對於第一個問題,咱們的答案是 common.bundle 加載完成後再加載 business.bundle

common.bundle 加載完成後,iOS 端會發送事件名稱是 RCTJavaScriptDidLoadNotification 的全局通知,Android 端則會向 ReactInstanceManager 實例中註冊的全部 ReactInstanceEventListener 回調 onReactContextInitialized() 方法。咱們在對應事件監聽器和回調中實現業務包的加載便可。


對於第二個問題,iOS 咱們可使用 RCTCxxBridge 的 executeSourceCode 方法在當前的 RN 實例上下文中執行一段 JS 代碼,以此來達到增量加載的目的。不過值得注意的是,executeSourceCode 是 RCTCxxBridge 的私有方法,須要咱們用 Category 將其暴露出來。

Android 端可使用剛剛創建好的 ReactInstanceManager 實例,經過 getCurrentReactContext() 獲取到當前的 ReactContext 上下文對象,再調用上下文對象的 getCatalystInstance() 方法獲取媒介實例,最終調用媒介實例的 loadScriptFromFile(String fileName, String sourceURL, boolean loadSynchronously) 方法完成業務 JSBundle 的增量加載。

iOS 和 Android 的示例代碼以下:

NSURL *businessBundleURI = // 業務包 URI
NSError *error = nil;
NSData *sourceData = [NSData dataWithContentsOfURL:businessBundleURI options:NSDataReadingMappedIfSafe error:&error];
if (error) { return }
[bridge.batchedBridge executeSourceCode:sourceData sync:NO]
ReactContext context = RNHost.getReactInstanceManager().getCurrentReactContext();
CatalystInstance catalyst = context.getCatalystInstance();
String fileName = "businessBundleURI"
catalyst.loadScriptFromFile(fileName, fileName, false);



本小節的示例代碼都屬於 demo 級別,若是想要真正接入生產環境,須要結合實際的架構和業務場景作定製。有一個 React Native 分包倉庫 react-native-multibundler 內容挺不錯的,你們能夠參考學習一下。

3.Network

rn_start_network

咱們通常會在 React Component 的 componentDidMount() 執行後請求網絡,從服務器獲取數據,而後再改變 Component 的 state 進行數據的渲染。

網絡優化是一個很是龐大很是獨立的話題,有很是多的點能夠優化,我這裏列舉幾個和首屏加載相關的網絡優化點:

  • DNS 緩存:提早緩存 IP 地址,跳過 DNS 尋址時間
  • 緩存複用:進入 RN 頁面前,先提早請求網絡數據並緩存下來,打開 RN 頁面後請求網絡前先檢查緩存數據,若是緩存未過時,直接從本地緩存裏拿數據
  • 請求合併:若是還在用 HTTP/1.1,若首屏有多個請求,能夠合併多個請求爲一個請求
  • HTTP2:利用 HTTP2 的並行請求和多路複用優化速度
  • 減少體積:去除接口的冗餘字段,減小圖片資源的體積等等
  • ......

因爲網絡這裏相對來講比較獨立,iOS/Android/Web 的優化經驗其實均可以用到 RN 上,這裏按照你們以往的優化經驗來就能夠了。

4.Render

rn_start_render

渲染這裏的耗時,基本上和首屏頁面的 UI 複雜度成正相關。能夠經過渲染流程查看哪裏會出現耗時:

  • VDOM 計算:頁面複雜度越高,JavaScript 側的計算耗時就會越長(VDOM 的生成與 Diff)
  • JS Native 通信:JS 的計算結果會轉爲 JSON 經過 Bridge 傳遞給 Native 側,複雜度越高,JSON 的數據量越大,有可能阻塞 Bridge 通信
  • Native 渲染:Native 側遞歸解析 render tree,佈局越複雜,渲染時間越長

咱們能夠在代碼裏開啓 MessageQueue 監視,看看 APP 啓動後 JS Bridge 上面有有些啥:

// index.js

import MessageQueue from 'react-native/Libraries/BatchedBridge/MessageQueue'
MessageQueue.spy(true);

rn_start_MessageQueue

從圖片裏能夠看出 JS 加載完畢後有大量和 UI 相關的 UIManager.createView() UIManager.setChildren() 通信,結合上面的耗時總結,咱們對應着就有幾條解決方案:

  • 經過必定的佈局技巧下降 UI 嵌套層級,下降 UI 視圖的複雜度
  • 減小 re-render,直接在 JS 側截斷重繪流程,減小 bridge 通信的頻率和數據量
  • 若是是 React Native 爲主架構的 APP,首屏能夠直接替換爲 Native View,直接脫離 RN 的渲染流程

上面的這些技巧我都在舊文《React Native 性能優化指南——渲染篇》裏作了詳細的解釋,這裏就很少解釋了。

Fraic

從上面的咱們能夠看出,React Native 的渲染須要在 Bridge 上傳遞大量的 JSON 數據,在 React Native 初始化時,數據量過大會阻塞 bridge,拖慢咱們的啓動和渲染速度。React Native 新架構中的 Fraic 就能解決這一問題,JS 和 Native UI 再也不是異步的通信,能夠實現直接的調用,能夠大大加速渲染性能。

Fraic 能夠說是 RN 新架構裏最讓人期待的了,想了解更多內容,能夠去官方 issues 區圍觀。

總結

本文主要從 JavaScript 的角度出發,分析了 Hermes 引擎的特色和做用,並總結分析了 JSBundle 的各類優化手段,再結合網絡和渲染優化,全方位提高 React Native 應用的啓動速度。

參考

⚡️ React Native 啓動速度優化——Native 篇(內含源碼分析)

🎨 React Native 性能優化指南——渲染篇

🤔 移動端 JS 引擎哪家強?

招商證券 react-native 熱更新優化實踐

React Native中如何實現拆包?


若是你喜歡個人文章,但願點贊👍 收藏 📁 評論 💬 三連支持一下,謝謝你,這對我真的很重要!

歡迎你們關注個人微信公衆號:滷蛋實驗室,目前專一前端技術,對圖形學也有一些微小研究。

原文連接 👉 ⚡️ React Native 啓動速度優化——JS 篇:更新更及時,閱讀體驗更佳

相關文章
相關標籤/搜索