JSI (JavaScript Interface) là một lớp phần mềm trong React Native cung cấp quyền truy cập đồng bộ trực tiếp từ JavaScript đến các đối tượng và hàm C++, thay thế cầu nối JSON bất đồng bộ Bridge. Không giống như người tiền nhiệm, JSI cho phép gọi các phương thức gốc mà không cần tuần tự hóa thông điệp và truyền tham chiếu đến các đối tượng C++ trực tiếp vào môi trường JS. Theo React Native Team (2025), JSI cung cấp khả năng tăng tốc lên đến 10 lần cho tương tác JS-mã gốc trong các kịch bản trao đổi dữ liệu chuyên sâu.
Những điểm chính
JSI (JavaScript Interface) là một lớp C++ cung cấp cho công cụ JavaScript (Hermes, JavaScriptCore, V8) khả năng truy cập trực tiếp vào các đối tượng, hàm và bộ nhớ C++. Không giống như Bridge, nó tuần tự hóa các cuộc gọi thành JSON và truyền chúng qua hàng đợi bất đồng bộ, JSI cho phép mã JS gọi các phương thức C++ một cách đồng bộ và nhận kết quả ngay lập tức.
JSI được giới thiệu trong React Native 0.64 như một phần của Kiến trúc mới. Mục tiêu chính là loại bỏ nút thắt cổ chai mà Bridge đại diện: mỗi tương tác giữa JS và mã gốc tiêu tốn thời gian cho tuần tự hóa, giải tuần tự hóa và truyền qua hàng đợi thông điệp. JSI giải quyết vấn đề này bằng cách cung cấp cho công cụ JS quyền truy cập trực tiếp vào các đối tượng C++ thông qua các wrapper triển khai các giao diện jsi::Value, jsi::Object và jsi::Function.
JSI không phải là sự thay thế một-một cho Bridge — đó là một cách tiếp cận tích hợp khác về cơ bản. Bridge hoạt động như một hộp thư: JS gửi một thông điệp, nó đi qua hàng đợi, phía gốc xử lý nó và gửi phản hồi. JSI hoạt động như một con trỏ: JS nhận được tham chiếu đến một đối tượng C++ và có thể gọi các phương thức của nó một cách đồng bộ, giống như các hàm JS thông thường. Đây là một khác biệt cơ bản trong kiến trúc tương tác giữa hai môi trường.
Sự cần thiết của JSI nảy sinh từ những hạn chế của Bridge ban đầu, được thiết lập trong React Native 2015. Khi framework ngày càng phổ biến và các ứng dụng trở nên phức tạp hơn, vấn đề hiệu suất trở nên rõ ràng: mỗi cuộc gọi mô-đun gốc yêu cầu ít nhất 3–5 ms để tuần tự hóa. Đối với các thao tác đơn giản như đọc giá trị cảm biến hoặc lấy kích thước màn hình, điều này có thể chấp nhận được, nhưng đối với hoạt ảnh, xử lý đồ họa và xử lý dữ liệu luồng — thì rất quan trọng. Nhóm React Native bắt đầu làm việc trên kiến trúc mới vào năm 2019, và JSI trở thành nền tảng của nó.
JSI được thiết kế như một lớp trừu tượng trên các công cụ JavaScript. Nó cung cấp một API C++ thống nhất được triển khai cho từng công cụ cụ thể: Hermes, JavaScriptCore (iOS), V8 (Android). Điều này có nghĩa là các nhà phát triển không cần lo lắng về sự khác biệt giữa các công cụ — Fabric và TurboModules hoạt động giống nhau bất kể công cụ JS nào được sử dụng bên dưới.
Ở cốt lõi của JSI là khái niệm về Đối tượng chủ (Host Objects) — các đối tượng C++ được xuất ra môi trường JS như các đối tượng JS gốc. Khi mã JS truy cập vào một thuộc tính hoặc phương thức của một đối tượng như vậy, JSI chặn cuộc gọi và ủy quyền cho phương thức C++ tương ứng. Điều này xảy ra đồng bộ, trong cùng một luồng, không chuyển đổi ngữ cảnh và không cấp phát bộ nhớ cho chuỗi JSON.
Mỗi Đối tượng chủ triển khai giao diện jsi::HostObject với các phương thức get, set và getPropertyNames. Công cụ JS gọi các phương thức này mỗi khi một thuộc tính của đối tượng được truy cập. Ví dụ, khi gọi NativeModule.someMethod() trong JS, JSI chuyển đổi cuộc gọi này thành một cuộc gọi C++ đến phương thức Đối tượng chủ tương ứng. Giá trị trả về được truyền lại cho JS dưới dạng jsi::Value — một kiểu chung có thể đại diện cho một số, chuỗi, boolean, đối tượng hoặc undefined.
Một tính năng quan trọng của JSI là không có hàng đợi thông điệp. Bridge sử dụng hàng đợi bất đồng bộ: JS gửi một yêu cầu, chuyển sang các tác vụ khác, phía gốc xử lý yêu cầu và kết quả được trả về qua callback. JSI hoạt động đồng bộ: nếu JS gọi một phương thức mô-đun gốc qua JSI, việc thực thi mã JS bị tạm dừng cho đến khi nhận được kết quả. Điều này đơn giản hóa logic (không cần chờ callbacks) và loại bỏ điều kiện cạnh tranh, nhưng đòi hỏi sự thận trọng — các cuộc gọi đồng bộ dài chặn luồng JS.
Các giá trị được tạo qua JSI tồn tại trong thời gian chạy của công cụ JS và được quản lý bởi bộ thu gom rác. Khi mã C++ tạo một jsi::String hoặc jsi::Object và trả nó về JS, môi trường tự động quản lý bộ nhớ. Nếu mã C++ muốn giữ một tham chiếu đến giá trị JS giữa các cuộc gọi, jsi::Value::getWeak() hoặc jsi::Object::setProperty toàn cục với tham chiếu được lưu trữ trên đối tượng gốc thời gian chạy được sử dụng. Điều này ngăn chặn việc thu gom rác sớm.
JSI không an toàn cho luồng theo mặc định. Tất cả các cuộc gọi phương thức JSI phải xảy ra từ luồng nơi JS thực thi (thường là luồng JS của React Native). Nếu một mô-đun gốc bắt đầu công việc nền trên một luồng riêng biệt, kết quả phải được truyền lại qua luồng JS bằng cách sử dụng runOnJS từ TurboModules. Hạn chế này là cái giá của tính đồng bộ và sự vắng mặt của tuần tự hóa.
Sự khác biệt giữa JSI và Bridge là cơ bản và ảnh hưởng đến tất cả các khía cạnh của tương tác JS-mã gốc. Bridge không đồng bộ, tuần tự hóa dữ liệu thành JSON và sử dụng hàng đợi thông điệp; JSI đồng bộ, hoạt động với các tham chiếu gốc và không yêu cầu tuần tự hóa.
| Tham số | Bridge | JSI |
|---|---|---|
| Mô hình gọi | Hàng đợi bất đồng bộ | Gọi trực tiếp đồng bộ |
| Tuần tự hóa | JSON (tuần tự hóa + giải tuần tự hóa) | Không (tham chiếu trực tiếp đến đối tượng C++) |
| Độ trễ | 3–10 ms mỗi cuộc gọi | 0.1–0.5 ms mỗi cuộc gọi |
| Kiểu | Động (qua JSON) | Tĩnh (qua Codegen) |
| Tích hợp C++ | Chỉ qua mô-đun gốc (Java/ObjC) | Trực tiếp, không qua trung gian |
| Luồng | Luồng gốc riêng biệt | Luồng JS (đồng bộ) |
Theo React Native Team, việc di chuyển từ Bridge sang JSI trong ứng dụng Facebook Marketplace đã giảm thời gian khởi động 35% và giảm mức tiêu thụ bộ nhớ 20% bằng cách loại bỏ trùng lặp dữ liệu giữa phía JS và gốc.
Bridge không phải là một “sai lầm” — đó là một quyết định kiến trúc hợp lý tại thời điểm tạo React Native năm 2015. Phát triển gốc cho hai nền tảng với các ngôn ngữ khác nhau đòi hỏi một định dạng trao đổi phổ quát. JSON như một định dạng tuần tự hóa có sẵn trên tất cả các nền tảng và cho phép thống nhất tương tác. Vấn đề trở nên rõ ràng sau đó, khi React Native bắt đầu được sử dụng cho các ứng dụng phức tạp với hàng ngàn cuộc gọi mô-đun gốc mỗi giây.
React Native duy trì tương thích ngược: các mô-đun gốc được viết cho Bridge tiếp tục hoạt động trong kiến trúc mới thông qua một lớp tương thích. Tuy nhiên, đối với các mô-đun mới, nên sử dụng JSI trực tiếp qua TurboModules. Di chuyển các mô-đun hiện có bao gồm việc thay thế giao thức tương tác mà không thay đổi logic nghiệp vụ của mô-đun.
JSI là một lớp nền tảng mà trên đó tất cả các thành phần của kiến trúc React Native mới được xây dựng. Nếu không có JSI, cả Fabric (trình kết xuất mới) và TurboModules (mô-đun gốc được tối ưu hóa) đều không thể tồn tại. JSI cung cấp một cách thống nhất để JS tương tác với C++ ở tất cả các cấp độ.
Fabric là trình kết xuất React Native mới sử dụng JSI để truy cập đồng bộ vào các biểu diễn C++ của giao diện người dùng. Trong kiến trúc cũ, việc kết xuất diễn ra qua Bridge: JS tạo các phần tử React, tuần tự hóa chúng thành JSON, gửi qua Bridge, phía gốc giải tuần tự hóa và tạo giao diện người dùng. Fabric qua JSI tạo các đối tượng C++ Shadow Tree trực tiếp từ JS, tính toán đồng bộ bố cục qua Yoga và truyền các khung đã sẵn sàng đến trình kết xuất gốc — không cần một lần tuần tự hóa nào.
TurboModules là sự tiến hóa của các mô-đun gốc React Native. Thay vì đăng ký một mô-đun trong Bridge và gọi các phương thức của nó qua JSON, TurboModules sử dụng JSI để tải chậm và gọi trực tiếp. Khi mã JS lần đầu tiên truy cập vào một mô-đun, JSI tạo một Đối tượng chủ — nó tải mô-đun gốc và hiển thị các phương thức của nó dưới dạng các hàm C++. Tải chậm có nghĩa là mô-đun không tiêu thụ bộ nhớ cho đến lần truy cập đầu tiên — điều này đặc biệt quan trọng đối với các ứng dụng có hàng chục mô-đun gốc, nhiều trong số đó chỉ được sử dụng trong các màn hình cụ thể.
Để làm việc với JSI trong kiến trúc mới, Codegen được sử dụng — một công cụ tạo ra các liên kết C++ từ các đặc tả JavaScript. Nhà phát triển mô tả giao diện mô-đun gốc trong TypeScript hoặc Flow, và Codegen tạo ra mã C++ triển khai một Đối tượng chủ tương thích JSI. Điều này tự động hóa công việc thường ngày và đảm bảo rằng các kiểu ở phía JS và C++ được đồng bộ hóa.
Hãy xem việc làm việc với JSI trông như thế nào trong thực tế. Trong ví dụ này, chúng ta tạo một lớp C++ đơn giản được xuất sang JS qua JSI và gọi phương thức của nó từ mã JavaScript trong React Native.
// Calculator.h — tiêu đề lớp C++ có thể truy cập từ JS
class Calculator {
public:
double add(double a, double b) { return a + b; }
double multiply(double a, double b) { return a * b; }
};
Lớp Calculator chứa hai phương thức số học. Chúng ta cần làm cho nó có thể truy cập được từ JS. Để làm điều này, một Đối tượng chủ được tạo ra bao bọc Calculator và hiển thị các phương thức của nó qua JSI.
// CalculatorHostObject.cpp — triển khai wrapper JSI
class CalculatorHostObject : public jsi::HostObject {
private:
Calculator calc;
public:
jsi::Value get(jsi::Runtime& runtime,
const jsi::PropNameID& name) override {
auto propName = name.utf8(runtime);
if (propName == "add") {
return jsi::Function::createFromHostFunction(
runtime, name, 2,
[this](jsi::Runtime& runtime,
const jsi::Value& thisVal,
const jsi::Value* args,
size_t count) -> jsi::Value {
return jsi::Value(calc.add(
args[0].asNumber(),
args[1].asNumber()));
});
}
return jsi::Value::undefined();
}
};
Trong mã này, phương thức get được gọi mỗi khi JS truy cập vào một thuộc tính của đối tượng. Nếu tên thuộc tính là “add”, một hàm C++ được trả về nhận hai đối số từ JS và gọi calc.add(). Giá trị được trả về dưới dạng jsi::Value — JSI tự động chuyển đổi double thành số JS.
Sau khi đăng ký Đối tượng chủ trong môi trường JS, cuộc gọi trông giống như một hàm JS thông thường. Tất cả các kiểu được kiểm tra ở giai đoạn tạo mã, loại bỏ lỗi không khớp kiểu trong thời gian chạy.
// JavaScript — gọi máy tính C++ qua JSI
import { Calculator } from 'react-native-calculator'
const result = Calculator.add(5, 3)
console.log(result) // 8 — đồng bộ, không trễ
const product = Calculator.multiply(4, 2.5)
console.log(product) // 10 — kết quả ngay lập tức
Lưu ý: kết quả được trả về ngay lập tức, không Promise, không await, không callbacks. Đây là một cuộc gọi đồng bộ mà không thể có trong kiến trúc Bridge. Đối với các hoạt động dài hạn (đọc tệp, yêu cầu mạng), nên sử dụng các mẫu không đồng bộ — JSI không loại bỏ nhu cầu về các luồng nền cho các tác vụ nặng.
Trong thực tế, hầu hết các nhà phát triển không viết Đối tượng chủ JSI thủ công — công việc này được thực hiện bởi Codegen, tạo ra các wrapper C++ dựa trên các đặc tả TypeScript. Tuy nhiên, hiểu cách JSI hoạt động bên trong là cần thiết để gỡ lỗi hiệu suất hiệu quả và khi tạo các mô-đun gốc phức tạp yêu cầu truy cập trực tiếp vào các thư viện C++ (Skia, FFmpeg, OpenCV).
Câu hỏi thường gặp
Bridge hoạt động bất đồng bộ qua tuần tự hóa JSON và hàng đợi thông điệp — mỗi cuộc gọi mất 3–10 ms để chuyển đổi dữ liệu. JSI cung cấp quyền truy cập trực tiếp đồng bộ vào các đối tượng C++ mà không cần tuần tự hóa, giảm độ trễ xuống 0.1–0.5 ms. JSI cũng hỗ trợ truyền tham chiếu đối tượng thay vì bản sao.
Có, JSI cung cấp một API C++ thống nhất được triển khai cho Hermes (mặc định React Native), JavaScriptCore (iOS) và V8 (Android). Các nhà phát triển không cần viết mã khác nhau cho các công cụ khác nhau — Fabric và TurboModules hoạt động giống hệt nhau trên tất cả các công cụ được hỗ trợ.
Có, React Native cung cấp một lớp tương thích ngược. Các mô-đun gốc được viết cho Bridge tiếp tục hoạt động trong kiến trúc mới. Tuy nhiên, nên di chuyển chúng sang TurboModules để nhận được lợi ích của JSI — tải chậm và các cuộc gọi đồng bộ.
Đối với phát triển hàng ngày — không. Các đặc tả TypeScript của mô-đun gốc được biên dịch tự động thành các liên kết C++ qua Codegen. Kiến thức C++ chỉ cần thiết khi tạo các thư viện C++ tùy chỉnh hoặc khi gỡ lỗi hiệu suất JSI ở cấp độ thời gian chạy.
JSI giải quyết ba vấn đề chính của Bridge: độ trễ cao do tuần tự hóa JSON, thiếu các cuộc gọi đồng bộ và không thể truyền các đối tượng phức tạp bằng tham chiếu. JSI cũng cho phép tích hợp các thư viện C++ trực tiếp, không cần qua trung gian Java hoặc Objective-C.
Tóm tắt
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm