สหายทดสอบประสิทธิภาพสำหรับ React และ React Native
อ่านเอกสาร
reassure-tests.sh
).gitignore
reassure-tests.sh
)measureRenders
MeasureRendersOptions
ประเภทประเภทmeasureFunction
MeasureFunctionOptions
ประเภทconfigure
ค่าฟังก์ชันresetToDefaults
คุณต้องการให้แอพ React Native ของคุณทำงานได้ดีและรวดเร็วตลอดเวลา เป็นส่วนหนึ่งของเป้าหมายนี้คุณโปรไฟล์แอพสังเกตรูปแบบการแสดงผลใช้การบันทึกความทรงจำในสถานที่ที่เหมาะสม ฯลฯ แต่มันเป็นคู่มือและง่ายเกินไปที่จะแนะนำการถดถอยประสิทธิภาพที่ไม่ได้ตั้งใจซึ่งจะถูกจับในช่วง QA หรือแย่กว่านั้นโดยผู้ใช้ .
ให้ความมั่นใจกับคุณสามารถทำการทดสอบการถดถอยประสิทธิภาพของแอป Native โดยอัตโนมัติบน CI หรือเครื่องในท้องถิ่น ในทำนองเดียวกันคุณเขียนการรวมการรวมและการทดสอบหน่วยที่ตรวจสอบโดยอัตโนมัติว่าแอปของคุณยังคง ทำงานได้อย่างถูกต้อง คุณสามารถเขียนการทดสอบประสิทธิภาพที่ตรวจสอบว่าแอปของคุณยังคง ทำงานได้อย่างมีประสิทธิภาพ
คุณสามารถคิดว่ามันเป็นไลบรารีการทดสอบประสิทธิภาพการตอบสนอง ในความเป็นจริงความมั่นใจได้รับการออกแบบมาเพื่อนำกลับมาใช้ใหม่ในการทดสอบไลบรารีการทดสอบดั้งเดิมของคุณและตั้งค่าให้มากที่สุด
ให้ความมั่นใจในการทำงานโดยการวัดลักษณะการแสดงผล - ระยะเวลาและการนับ - ของสถานการณ์การทดสอบที่คุณให้และเปรียบเทียบกับเวอร์ชันที่เสถียร มันทำซ้ำสถานการณ์หลายครั้งเพื่อลดผลกระทบของการเปลี่ยนแปลงแบบสุ่มในเวลาแสดงผลที่เกิดจากสภาพแวดล้อมรันไทม์ จากนั้นใช้การวิเคราะห์ทางสถิติเพื่อตรวจสอบว่าการเปลี่ยนแปลงรหัสมีนัยสำคัญทางสถิติหรือไม่ เป็นผลให้มันสร้างรายงานที่มนุษย์อ่านได้สรุปผลลัพธ์และแสดงบน CI หรือเป็นความคิดเห็นต่อคำขอดึงของคุณ
นอกเหนือจากการวัดการเรนเดอร์ส่วนประกอบแล้วยังสามารถวัดการดำเนินการของฟังก์ชั่น JavaScript ปกติ
หากต้องการติดตั้งให้ความมั่นใจให้เรียกใช้คำสั่งต่อไปนี้ในโฟลเดอร์แอพของคุณ:
ใช้เส้นด้าย
yarn add --dev reassure
ใช้ NPM
npm install --save-dev reassure
นอกจากนี้คุณยังจะต้องมีการตั้งค่า jest jest ที่ใช้งานได้รวมถึงหนึ่งในไลบรารีการทดสอบดั้งเดิมหรือไลบรารีการทดสอบปฏิกิริยา
ดูคู่มือการติดตั้ง
คุณสามารถตรวจสอบโครงการตัวอย่างของเรา:
ให้ความมั่นใจว่าจะพยายามตรวจสอบว่าคุณติดตั้งไลบรารีการทดสอบใด หากมีการตอบสนองของไลบรารีการทดสอบดั้งเดิมและไลบรารีการทดสอบปฏิกิริยาจะมีอยู่มันจะเตือนคุณเกี่ยวกับสิ่งนั้นและให้ความสำคัญกับการตอบสนองของไลบรารีการทดสอบดั้งเดิม คุณสามารถระบุไลบรารีการทดสอบที่จะใช้อย่างชัดเจนโดยใช้ตัวเลือก configure
:
configure ( { testingLibrary : 'react-native' } ) ;
// or
configure ( { testingLibrary : 'react' } ) ;
คุณควรตั้งค่าไว้ในไฟล์การตั้งค่า jest ของคุณและคุณสามารถแทนที่ไฟล์ทดสอบโดยเฉพาะหากจำเป็น
ตอนนี้มีการติดตั้งไลบรารีแล้วคุณสามารถเขียนสถานการณ์การ .perf-test.tsx
ครั้งแรกของคุณในไฟล์ที่ .perf-test.js
// ComponentUnderTest.perf-test.tsx
import { measureRenders } from 'reassure' ;
import { ComponentUnderTest } from './ComponentUnderTest' ;
test ( 'Simple test' , async ( ) => {
await measureRenders ( < ComponentUnderTest / >);
} ) ;
การทดสอบนี้จะวัดเวลาเรนเดอร์ของ ComponentUnderTest
ระหว่างการติดตั้งและผลการซิงค์ที่เกิดขึ้น
หมายเหตุ : ให้ความมั่นใจว่าจะจับคู่ชื่อไฟล์ทดสอบโดยอัตโนมัติโดยใช้ตัวเลือก jest
--testMatch
กับค่า"**/__perf__/**/*.[jt]s?(x)", "**/*.(perf|perf-test).[jt]s?(x)"
อย่างไรก็ตามหากคุณต้องการผ่านตัวเลือกที่กำหนดเอง--testMatch
หรือ--testRegex
คุณสามารถเพิ่มลงในสคริปต์reassure measure
เพื่อส่งผ่านลูกโลกของคุณเอง เพิ่มเติมเกี่ยวกับ--testMatch
และ--testRegex
ในเอกสารตลก
หากส่วนประกอบของคุณมีตรรกะ async หรือคุณต้องการทดสอบการโต้ตอบบางอย่างคุณควรผ่านตัวเลือก scenario
:
import { measureRenders } from 'reassure' ;
import { screen , fireEvent } from '@testing-library/react-native' ;
import { ComponentUnderTest } from './ComponentUnderTest' ;
test ( 'Test with scenario' , async ( ) => {
const scenario = async ( ) => {
fireEvent . press ( screen . getByText ( 'Go' ) ) ;
await screen . findByText ( 'Done' ) ;
} ;
await measureRenders ( < ComponentUnderTest / >, { scenario });
} ) ;
ร่างกายของฟังก์ชั่น scenario
กำลังใช้วิธีการทดสอบรีแอกเนทีฟพื้นเมืองที่คุ้นเคย
ในกรณีของการใช้ไลบรารีการทดสอบ React Native Library ต่ำกว่า V10.1.0 ซึ่งไม่มีตัวช่วย screen
ฟังก์ชั่น scenario
แสดงเป็นอาร์กิวเมนต์แรก:
import { measureRenders } from 'reassure' ;
import { fireEvent } from '@testing-library/react-native' ;
test ( 'Test with scenario' , async ( ) => {
const scenario = async ( screen ) => {
fireEvent . press ( screen . getByText ( 'Go' ) ) ;
await screen . findByText ( 'Done' ) ;
} ;
await measureRenders ( < ComponentUnderTest / >, { scenario });
} ) ;
หากการทดสอบของคุณมีการเปลี่ยนแปลงแบบ async ใด ๆ คุณจะต้องตรวจสอบให้แน่ใจว่าสถานการณ์รอการเปลี่ยนแปลงเหล่านี้เพื่อชำระเช่นโดยใช้การค้นหา findBy
waitFor
หรือรอฟังก์ชั่น waitForElementToBeRemoved
จาก RNTL
ในการวัดประสิทธิภาพการทดสอบครั้งแรกของคุณคุณต้องเรียกใช้คำสั่งต่อไปนี้ในเทอร์มินัล:
yarn reassure
คำสั่งนี้จะเรียกใช้การทดสอบของคุณหลายครั้งโดยใช้ JEST รวบรวมสถิติประสิทธิภาพและจะเขียนลงในไฟล์ .reassure/current.perf
หากต้องการตรวจสอบการตั้งค่าของคุณให้ตรวจสอบว่าไฟล์เอาต์พุตมีอยู่หลังจากเรียกใช้คำสั่งเป็นครั้งแรกหรือไม่
หมายเหตุ: คุณสามารถเพิ่ม
.reassure/
โฟลเดอร์ลงในไฟล์.gitignore
ของคุณเพื่อหลีกเลี่ยงการกระทำของคุณโดยไม่ตั้งใจ
ให้ความมั่นใจกับ CLI จะพยายามตรวจจับชื่อสาขาซอร์สโค้ดของคุณโดยอัตโนมัติและส่งแฮชเมื่อคุณใช้ Git คุณสามารถแทนที่ตัวเลือกเหล่านี้เช่นหากคุณใช้ระบบควบคุมเวอร์ชันอื่น:
yarn reassure --branch [branch name] --commit-hash [commit hash]
ในการตรวจจับการเปลี่ยนแปลงประสิทธิภาพคุณต้องวัดประสิทธิภาพของรหัสสองเวอร์ชันปัจจุบันของคุณ (รหัสที่แก้ไขแล้ว) และพื้นฐาน (จุดอ้างอิงของคุณเช่นสาขา main
) ในการวัดประสิทธิภาพในสองสาขาคุณต้องสลับสาขาใน Git หรือโคลนสองสำนักที่เก็บของคุณ
เราต้องการให้งานนี้ทำงานบน CI โดยอัตโนมัติ ในการทำเช่นนั้นคุณจะต้องสร้างสคริปต์การทดสอบประสิทธิภาพ คุณควรบันทึกไว้ในที่เก็บของของคุณเช่น reassure-tests.sh
สคริปต์เวอร์ชันง่าย ๆ โดยใช้วิธีการเปลี่ยนแปลงสาขามีดังนี้:
#! /usr/bin/env bash
set -e
BASELINE_BRANCH= ${GITHUB_BASE_REF := " main " }
# Required for `git switch` on CI
git fetch origin
# Gather baseline perf measurements
git switch " $BASELINE_BRANCH "
yarn install
yarn reassure --baseline
# Gather current perf measurements & compare results
git switch --detach -
yarn install
yarn reassure
เพื่อให้การตั้งค่าการรวม CI และสิ่งที่จำเป็นต้องมีทั้งหมดสะดวกยิ่งขึ้นเราได้เตรียมคำสั่ง CLI เพื่อสร้างเทมเพลตที่จำเป็นทั้งหมดเพื่อให้คุณเริ่มต้นด้วย
เพียงแค่วิ่ง:
yarn reassure init
สิ่งนี้จะสร้างโครงสร้างไฟล์ต่อไปนี้
├── <ROOT>
│ ├── reassure-tests.sh
│ ├── dangerfile.ts/js (or dangerfile.reassure.ts/js if dangerfile.ts/js already present)
│ └── .gitignore
reassure-tests.sh
)สคริปต์พื้นฐานช่วยให้คุณสามารถให้ความมั่นใจกับ CI เพิ่มเติมเกี่ยวกับความสำคัญและโครงสร้างของไฟล์นี้ในส่วนต่อไปนี้
หากโครงการของคุณมี dangerfile.ts/js
อยู่แล้ว CLI จะไม่แทนที่ในทางใดทางหนึ่ง แต่จะสร้างไฟล์ dangerfile.reassure.ts/js
ซึ่งจะช่วยให้คุณเปรียบเทียบและอัปเดตของคุณเองตามความสะดวกของคุณ
.gitignore
หากไฟล์ .gitignore
มีอยู่และไม่มีการกล่าวถึง reassure
ปรากฏขึ้นสคริปต์จะผนวก .reassure/
ไดเรกทอรีจนถึงที่สุด
reassure-tests.sh
) ในการตรวจจับการเปลี่ยนแปลงประสิทธิภาพคุณต้องวัดประสิทธิภาพของรหัสสองเวอร์ชันของรหัสปัจจุบัน (รหัสที่คุณแก้ไข) และพื้นฐาน (จุดอ้างอิงของคุณเช่นสาขา main
) ในการวัดประสิทธิภาพในสองสาขาคุณต้องสลับสาขาใน Git หรือโคลนสองสำนักที่เก็บของคุณ
เราต้องการให้งานนี้ทำงานบน CI โดยอัตโนมัติ ในการทำเช่นนั้นคุณจะต้องสร้างสคริปต์การทดสอบประสิทธิภาพ คุณควรบันทึกไว้ในที่เก็บของของคุณเช่น reassure-tests.sh
สคริปต์เวอร์ชันง่าย ๆ โดยใช้วิธีการเปลี่ยนแปลงสาขามีดังนี้:
#! /usr/bin/env bash
set -e
BASELINE_BRANCH= ${GITHUB_BASE_REF := " main " }
# Required for `git switch` on CI
git fetch origin
# Gather baseline perf measurements
git switch " $BASELINE_BRANCH "
yarn install
yarn reassure --baseline
# Gather current perf measurements & compare results
git switch --detach -
yarn install
yarn reassure
เป็นขั้นตอนการตั้งค่าสุดท้ายคุณต้องกำหนดค่า CI ของคุณเพื่อเรียกใช้สคริปต์การทดสอบประสิทธิภาพและส่งออกผลลัพธ์ สำหรับการนำเสนอผลลัพธ์ในขณะนี้เรารวมเข้ากับ Danger JS ซึ่งรองรับเครื่องมือ CI ที่สำคัญทั้งหมด
คุณจะต้องมีการตั้งค่า JS ที่เป็นอันตรายต่อการทำงาน
จากนั้นเพิ่มความมั่นใจในอันตราย JS ปลั๊กอินให้กับ DangerFile ของคุณ:
// /<project_root>/dangerfile.reassure.ts (generated by the init script)
import path from 'path' ;
import { dangerReassure } from 'reassure' ;
dangerReassure ( {
inputFilePath : path . join ( __dirname , '.reassure/output.md' ) ,
} ) ;
หากคุณยังไม่มี Dangerfile ( dangerfile.js
หรือ dangerfile.ts
) คุณสามารถใช้หนึ่งที่สร้างโดยสคริปต์ reassure init
โดยไม่ต้องทำการเปลี่ยนแปลงเพิ่มเติมใด ๆ
นอกจากนี้ยังอยู่ในตัวอย่างไฟล์ DangerFile ของเรา
สุดท้ายเรียกใช้ทั้งสคริปต์การทดสอบประสิทธิภาพและอันตรายในการกำหนดค่า CI ของคุณ:
- name : Run performance testing script
run : ./reassure-tests.sh
- name : Run Danger.js
run : yarn danger ci
env :
GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
คุณยังสามารถตรวจสอบตัวอย่างเวิร์กโฟลว์ GitHub ของเรา
ตัวอย่างข้างต้นขึ้นอยู่กับการกระทำของ GitHub แต่ควรจะคล้ายกับไฟล์ ci -config อื่น ๆ และควรใช้เป็นข้อมูลอ้างอิงในกรณีดังกล่าวเท่านั้น
หมายเหตุ : การทดสอบประสิทธิภาพของคุณจะทำงานได้นานกว่าการทดสอบการรวมปกติ เป็นเพราะเราเรียกใช้แต่ละสถานการณ์การทดสอบหลายครั้ง (โดยค่าเริ่มต้น 10) และทำซ้ำสำหรับสองสาขาของรหัสของคุณ ดังนั้นการทดสอบแต่ละครั้งจะเรียกใช้ 20 ครั้งโดยค่าเริ่มต้น นั่นเว้นแต่คุณจะเพิ่มจำนวนนั้นให้สูงขึ้น
เราวัดเวลาเรนเดอร์ส่วนประกอบของปฏิกิริยาด้วยความแม่นยำของไมโครวินาทีในระหว่างการวัดประสิทธิภาพโดยใช้ React.Profiler
ซึ่งหมายความว่ารหัสเดียวกันจะทำงานได้เร็วขึ้นหรือช้าลงขึ้นอยู่กับเครื่อง ด้วยเหตุผลนี้การวัดพื้นฐานและปัจจุบันจำเป็นต้องทำงานบนเครื่องเดียวกัน อย่างดีที่สุดพวกเขาควรจะทำงานต่อไป
ยิ่งไปกว่านั้นตัวแทน CI ของคุณต้องมีประสิทธิภาพที่มั่นคงเพื่อให้ได้ผลลัพธ์ที่มีความหมาย ไม่สำคัญว่าตัวแทนของคุณจะเร็วหรือช้าตราบใดที่มันสอดคล้องกับประสิทธิภาพ นั่นเป็นเหตุผลที่ตัวแทนไม่ควรใช้ในระหว่างการทดสอบประสิทธิภาพสำหรับงานอื่น ๆ ที่อาจส่งผลกระทบต่อการวัดเวลาในการแสดงผล
เพื่อช่วยให้คุณประเมินความมั่นคงของเครื่องของคุณคุณสามารถใช้คำสั่ง reassure check-stability
มันเรียกใช้การวัดประสิทธิภาพสองครั้งสำหรับรหัสปัจจุบันดังนั้นการวัดพื้นฐานและปัจจุบันอ้างถึงรหัสเดียวกัน ในกรณีเช่นนี้การเปลี่ยนแปลงที่คาดหวังคือ 0% (ไม่มีการเปลี่ยนแปลง) ระดับของการเปลี่ยนแปลงประสิทธิภาพแบบสุ่มจะสะท้อนถึงความเสถียรของเครื่องของคุณ คำสั่งนี้สามารถรันได้ทั้งใน CI และเครื่องจักรท้องถิ่น
โดยปกติการเปลี่ยนแปลงแบบสุ่มควรต่ำกว่า 5% ผลลัพธ์ 10% และอื่น ๆ ถือว่าสูงเกินไปซึ่งหมายความว่าคุณควรทำงานเพื่อปรับความเสถียรของเครื่อง
หมายเหตุ : เป็นเคล็ดลับของทางเลือกสุดท้ายคุณสามารถเพิ่มตัวเลือก
run
จากค่าเริ่มต้นที่ 10 ถึง 20, 50 หรือ 100 สำหรับการทดสอบทั้งหมดหรือบางส่วนโดยขึ้นอยู่กับสมมติฐานที่ว่าการทดสอบเพิ่มเติมจะทำให้เกิดความผันผวนของการวัด อย่างไรก็ตามจะทำให้การทดสอบของคุณทำงานได้นานขึ้น
คุณสามารถอ้างถึงตัวอย่างเวิร์กโฟลว์ GitHub ของเรา
เมื่อดูตัวอย่างคุณสามารถสังเกตได้ว่าสถานการณ์การทดสอบสามารถกำหนดให้กับหมวดหมู่บางประเภท:
measureRenders
wrapper แบบกำหนดเองสำหรับฟังก์ชั่น RNTL render
ที่รับผิดชอบในการแสดงผลหน้าจอที่ผ่านภายใน React.Profiler
ส่วนประกอบการวัดประสิทธิภาพและการเขียนผลลัพธ์ไปยังไฟล์เอาต์พุต คุณสามารถใช้วัตถุ options
ที่เป็นตัวเลือกที่อนุญาตให้ปรับแต่งด้านการทดสอบ
async function measureRenders (
ui : React . ReactElement ,
options ?: MeasureRendersOptions ,
) : Promise < MeasureResults > {
MeasureRendersOptions
ประเภทประเภท interface MeasureRendersOptions {
runs ?: number ;
warmupRuns ?: number ;
wrapper ?: React . ComponentType < { children : ReactElement } > ;
scenario ?: ( view ?: RenderResult ) => Promise < any > ;
writeFile ?: boolean ;
}
runs
: จำนวนการรันต่อซีรีส์สำหรับการทดสอบเฉพาะwarmupRuns
: จำนวนการทำงานอุ่นเครื่องเพิ่มเติมที่จะทำและทิ้งก่อนการรันจริง (ค่าเริ่มต้น 1)wrapper
: ส่วนประกอบตอบสนองเช่น Provider
ซึ่ง ui
จะถูกห่อด้วย หมายเหตุ: ระยะเวลาการเรนเดอร์ของ wrapper
นั้นไม่รวมอยู่ในผลลัพธ์ มีการวัดส่วนประกอบที่ห่อหุ้มเท่านั้นscenario
: ฟังก์ชั่น async แบบกำหนดเองซึ่งกำหนดการโต้ตอบของผู้ใช้ภายใน UI โดยใช้ฟังก์ชัน RNTL หรือ RTLwriteFile
: (ค่าเริ่มต้น true
) ควรเขียนเอาต์พุตไปยังไฟล์ measureFunction
ช่วยให้คุณสามารถห่อฟังก์ชั่นซิงโครนัสใด ๆ วัดเวลาการดำเนินการและเขียนผลลัพธ์ลงในไฟล์เอาต์พุต คุณสามารถใช้ options
เพิ่มเติมเพื่อปรับแต่งแง่มุมของการทดสอบ หมายเหตุ: จำนวนการดำเนินการจะเป็นหนึ่งเสมอ
async function measureFunction (
fn : ( ) => void ,
options ?: MeasureFunctionOptions
) : Promise < MeasureResults > {
MeasureFunctionOptions
ประเภท interface MeasureFunctionOptions {
runs ?: number ;
warmupRuns ?: number ;
}
runs
: จำนวนการรันต่อซีรีส์สำหรับการทดสอบเฉพาะwarmupRuns
: จำนวนการอุ่นเครื่องเพิ่มเติมที่จะทำและทิ้งก่อนที่จะวิ่งจริง การกำหนดค่าเริ่มต้นซึ่งจะใช้โดยสคริปต์การวัด วัตถุการกำหนดค่านี้สามารถแทนที่ด้วยการใช้ฟังก์ชัน configure
type Config = {
runs ?: number ;
warmupRuns ?: number ;
outputFile ?: string ;
verbose ?: boolean ;
testingLibrary ?:
| 'react-native'
| 'react'
| { render : ( component : React . ReactElement < any > ) => any ; cleanup : ( ) => any } ;
} ;
const defaultConfig : Config = {
runs : 10 ,
warmupRuns : 1 ,
outputFile : '.reassure/current.perf' ,
verbose : false ,
testingLibrary : undefined , // Will try auto-detect first RNTL, then RTL
} ;
runs
: จำนวนการรันซ้ำในชุดต่อการทดสอบ (ช่วยให้มีความแม่นยำสูงขึ้นโดยการรวมข้อมูลเพิ่มเติม) ควรได้รับการจัดการด้วยความระมัดระวัง
warmupRuns
: จำนวนการอุ่นเครื่องเพิ่มเติมที่จะทำและทิ้งก่อนที่จะวิ่งจริง outputFile
: ชื่อของไฟล์บันทึกจะถูกบันทึกลงใน verbose
: สร้างความมั่นใจในบันทึกมากขึ้นเช่นสำหรับวัตถุประสงค์ในการแก้ไขจุดประสงค์ testingLibrary
: สถานที่ที่จะมองหาฟังก์ render
น 'react-native'
'react'
cleanup
ฟังก์ชั่น render
และ cleanup
configure
ค่าฟังก์ชัน function configure ( customConfig : Partial < Config > ) : void ;
ฟังก์ชั่น configure
สามารถแทนที่พารามิเตอร์การกำหนดค่าเริ่มต้น
resetToDefaults
resetToDefaults ( ) : void
รีเซ็ตการกำหนดค่าปัจจุบันเป็นวัตถุ defaultConfig
ดั้งเดิม
คุณสามารถใช้ตัวแปรสภาพแวดล้อมที่มีอยู่เพื่อเปลี่ยนการตั้งค่านักวิ่งทดสอบของคุณ
TEST_RUNNER_PATH
: เส้นทางอื่นสำหรับนักวิ่งทดสอบของคุณ ค่าเริ่มต้นเป็น 'node_modules/.bin/jest'
หรือบน windows 'node_modules/jest/bin/jest'
TEST_RUNNER_ARGS
: ชุดของอาร์กิวเมนต์ที่ป้อนให้กับนักวิ่ง ค่าเริ่มต้นเป็น '--runInBand --testMatch "**/__perf__/**/*.[jt]s?(x)", "**/*.(perf|perf-test).[jt]s?(x)"'
ตัวอย่าง:
TEST_RUNNER_PATH=myOwnPath/jest/bin yarn reassure
ดูคู่มือการสนับสนุนเพื่อเรียนรู้วิธีการมีส่วนร่วมในที่เก็บและเวิร์กโฟลว์การพัฒนา
มิกซ์
ให้ความมั่นใจเป็นโครงการโอเพ่นซอร์สและจะยังคงใช้งานได้ฟรีเสมอ โครงการได้รับการพัฒนาในการเป็นหุ้นส่วนอย่างใกล้ชิดกับ Entain และเดิมเป็นโครงการภายใน บริษัท ต้องขอบคุณความตั้งใจของพวกเขาที่จะพัฒนาระบบนิเวศของ React & React Native เราตัดสินใจที่จะทำให้เป็นโอเพนซอร์ส ถ้าคุณคิดว่ามันเจ๋งโปรดแสดงให้เห็นได้ไหม
CallStack เป็นกลุ่มของ React และ React Native Experts หากคุณต้องการความช่วยเหลือเกี่ยวกับสิ่งเหล่านี้หรือต้องการทักทายโปรดติดต่อเราที่ [email protected]!
ชอบโครงการ? ⚛เข้าร่วมทีม CallStack ที่ทำสิ่งที่น่าทึ่งสำหรับลูกค้าและไดรฟ์ตอบสนองโอเพนซอร์สพื้นเมือง!