אנו מקבלים באופן קבוע פניות ליישום עריכה שיתופית של גיליונות אלקטרוניים באפליקציה למובייל. גיליון אלקטרוני אינו טקסט. יש לו מבנה: שורות, עמודות, תאים, נוסחאות ותלות בין תאים. אלגוריתמי סנכרון שעובדים עבור מסמכי טקסט (Y.js YText, CRDT לרצפים) מיושמים כאן אחרת. עריכה במקביל של תא B3 ושינוי נוסחה =SUM(B1:B5) ב-C1 – אלו שני אירועים עצמאיים עם תלות בנתונים. אנו מציעים פתרון מפתח ביד באמצעות CRDT.
כיצד להבטיח עקביות נתונים במהלך עריכה במקביל?
מודל נתונים: לא מערך של שורות, אלא מפה של תאים
גיליון אלקטרוני בייצוג CRDT הוא Y.Map (או מקביל) שבו המפתח הוא מחרוזת "row:col" והערך הוא אובייקט תא. Y.js YMap תומך בפעולות במקביל ברמת מפתח בודד: שני משתמשים עורכים תאים שונים בו-זמנית – אין התנגשות. שניים עורכים את אותו תא – האחרון שכותב מנצח (סמנטיקת Last-Write-Wins). זה מאושר על ידי תיעוד Y.js.
const ycells = ydoc.getMap('cells');
ycells.set('3:2', { value: '=SUM(A1:A3)', formatted: '15', style: { bold: true } });
ycells.observe(event => {
event.changes.keys.forEach((change, key) => {
if (change.action === 'update' || change.action === 'add') {
const [row, col] = key.split(':').map(Number);
recalculateAffectedFormulas(row, col);
rerenderCell(row, col);
}
});
});Last-Write-Wins עבור תאים מקובל ברוב התרחישים. עבור נתונים קריטיים (גיליונות פיננסיים), יש צורך בגלאי התנגשויות וממשק משתמש לפתרון ידני.
הוספה ומחיקה של שורות/עמודות: שינויים מבניים
זה מורכב יותר משינוי תא. הוספת שורה לפני שורה 5 צריכה להזיז את כל ההפניות בנוסחאות: const ycells = ydoc.getMap('cells'); ycells.set('3:2', { value: '=SUM(A1:A3)', formatted: '15', style: { bold: true } }); ycells.observe(event => { event.changes.keys.forEach((change, key) => { if (change.action === 'update' || change.action === 'add') { const [row, col] = key.split(':').map(Number); recalculateAffectedFormulas(row, col); rerenderCell(row, col); } }); }); הופך ל-=B5. הוספת שורה במקביל על ידי שני משתמשים – סדר ההוספה משפיע על המבנה הסופי.
Y.js =B6 עבור רשימת שורות עם YArray לכל שורה הוא סכמה קלאסית. בעת הוספה ל-YMap, CRDT מבטיח סדר מיזוג דטרמיניסטי (בהתבסס על YArray ו-clientId). Y.js עם YMap מטפל בשינויים בו-זמניים מהר יותר מפתרונות OT עם יומני פעולות, בממוצע פי 2 מהר יותר, עם אותה אמינות. נוסחאות לאחר שינויים מבניים צריכות להיות מחושבות מחדש באמצעות גרף תלות.
חישוב מחדש של נוסחאות הוא נושא נפרד. HyperFormula (clock) – מנוע נוסחאות בקוד פתוח (דומה ל-Excel), רץ ב-JavaScript. תומך ביותר מ-400 פונקציות. משתלב עם Y.js: כאשר מגיע עדכון CRDT, אנו מיישמים אותו ב-HyperFormula דרך hyperformula.js ומקבלים ערכים מחושבים מחדש.
באילו כלים לבחור לעורך גיליונות אלקטרוניים למובייל?
עיבוד טבלה למובייל
FlashList (React Native) עם גלילה דו-ממדית – מקרה לא סטנדרטי. ה-setSheetContent() / FlatList הסטנדרטי אינו מותאם לטבלאות גדולות עם כותרות קבועות ווירטואליזציה של תאים. React Native עם FlashList מספק גלילה חלקה יותר מאשר FlatList סטנדרטי, בערך פי 2-3.
פתרונות:
-
ScrollView– פשוט, ללא וירטואליזציה, לא לטבלאות גדולות. -
react-native-table-component– קיים אך לא עודכן במשך זמן רב. - מעבד מותאם אישית באמצעות
react-native-spreadsheet+react-native-gesture-handler– שליטה מלאה, וירטואליזציה ידנית.
ב-Android מקורי: react-native-reanimated עם RecyclerView, וירטואליזציה מובנית. ב-iOS: GridLayoutManager עם UICollectionView לגלילה דו-ממדית עם גדלי תאים משתנים.
| פלטפורמה | רכיב | וירטואליזציה | מורכבות |
|---|---|---|---|
| React Native | FlashList (מותאם) | כן | גבוהה |
| Android | RecyclerView + GridLayoutManager | מובנית | בינונית |
| iOS | UICollectionView + CompositionalLayout | מובנית | בינונית |
עורך תאים מותאם אישית: UICollectionViewCompositionalLayout עם תמיכה בנוסחאות (מתחיל ב-TextInput – מצב מעבר). ב-iOS תיקון אוטומטי שובר נוסחאות (= הופך ל-=SUM או מקבל קו תחתון). יש צורך ב-=Sum ו-autocorrectionType = .no עבור תאים במצב נוסחה.
בחירת טווח: spellCheckingType = .no
בעת הזנת נוסחה, המשתמש צריך להקיש על תא ולגרור כדי לבחור טווח – חוויית Excel קלאסית. במובייל: מזהה מחוות מותאם אישית מעל הטבלה שעוקב אחר מחוות פאן ומחשב את טווח התאים מהקואורדינטות. יש צורך ב-B2:D5 כדי לקבוע את התא מנקודת המגע.
משתמשים מרובים יכולים לבחור טווחים בו-זמנית – הצג באמצעות Awareness Protocol (כמו סמנים בעורך טקסט), אך עם קידוד צבע לכל משתמש.
סנכרון דרך WebSocket: עדכונים אופטימיים
שינוי תא מקומי מיושם בממשק המשתמש מיד (עדכון אופטימי). במקביל, Y.js מקודד את הפעולה לעדכון בינארי ושולח אותו לשרת. השרת משדר את העדכון ללקוחות אחרים. אם מגיע עדכון מתנגש – מיזוג Y.js מוחל, וממשק המשתמש מתעדכן מחדש.
עבור טבלאות גדולות (1000+ שורות), חשוב לא לעבד מחדש את כל הטבלה בכל עדכון מרחוק. אנו עוקבים אחר hitTest ב-observer ומעבדים מחדש רק את התאים שהשתנו בתוספת נוסחאות תלויות.
מה כוללת העבודה
- ארכיטקטורה ואב-טיפוס: בחירת מחסנית טכנולוגית, סכמת נתונים, מבני CRDT
- אינטגרציה של Y.js + HyperFormula עם מודולים מקוריים
- יישום מעבד מותאם אישית עם וירטואליזציה
- הקמת שרת WebSocket ואופטימיזציה של תעבורה
- בדיקות עומס (עד 50 משתמשים בו-זמנית)
- תיעוד והדרכת צוות
- תמיכה לאחר הפריסה (חודש אחד)
הניסיון שלנו כולל פיתוח של למעלה מ-10 אפליקציות מובייל עם סנכרון CRDT. יש לנו ניסיון של 5+ שנים בפיתוח מובייל ואנו מבטיחים עמידה בלוחות זמנים.
הערכת עלות
MVP עם Y.js + HyperFormula על React Native (עד 100 שורות, נוסחאות בסיסיות, 2–3 משתמשים בו-זמנית) – 10–16 שבועות. עורך מובייל מלא בסגנון Google Sheets עם אלפי שורות, נוסחאות מורכבות ושינויים מבניים – 6–12 חודשים.
צרו קשר להערכת פרויקט. קבלו ייעוץ על ארכיטקטורה ולוחות זמנים – זה לוקח לא יותר מ-2 ימי עבודה.







