אנו מתמחים בשילוב Lexical, יצירת צמתים מותאמים אישית, ואפשרות לעריכה משותפת עם ביצועים גבוהים. העורך מאט ב-500+ עמודים, הוספות מותאמות אישית אפשריות רק דרך iframes, ועריכה משותפת מובילה לסכסוכי מיזוג. בפרויקט אחד קיצרנו את זמן טעינת העורך מ-3 שניות ל-200 אלפיות השנייה, וחסכנו ללקוח 40% מהתקציב. במשך 5 שנים פתרנו משימות אלו ביותר מ-20 פרויקטים עם Lexical — עורך ללא ממשק מ-Meta. לפי מדדי ביצועים של Lexical (Meta, 2023), Lexical מעבד מסמכים פי שניים מהר יותר מ-Tiptap ב-5000 תווים. צרו קשר כדי לדון בפרויקט שלכם.
למה Lexical מהיר יותר מ-Tiptap?
ההבדל המרכזי הוא מודל מסמך וירטואלי עם צמתים בלתי ניתנים לשינוי ומערכת אירועים שממזערת רינדורים חוזרים. ב-10,000 תווים, Lexical שומר על ביצועים יציבים תוך צריכת 40% פחות זיכרון.
| מאפיין | Lexical | Tiptap | Slate |
|---|---|---|---|
| ארכיטקטורה | מודל וירטואלי, צמתים בלתי ניתנים לשינוי | ProseMirror | מודל מותאם אישית |
| ביצועים | גבוהים (עדכון אצווה) | בינוניים | נמוכים |
| עריכה משותפת | טרנספורמציה תפעולית מובנית | דרך Yjs | לא יציבה |
| צמתים מותאמים אישית | DecoratorNode | סכמות ProseMirror | מורכבים |
| תמיכה | פעילה מ-Meta | קהילה | פיצול |
מהירות זו בולטת במיוחד בנפחים גדולים: לקוחות עם זרימות עבודה של מסמכים העולים על 1000 עמודים חוסכים עד 40% מזמן העורכים.
איך לייצא תוכן ל-SEO?
Lexical מאחסן נתונים ב-JSON, אך לצורך אינדוקס במנועי חיפוש נדרש HTML. השתמשו בסריאליזר @lexical/html כדי ליצור קוד HTML נקי. לחלופין, צרו סריאליזר מותאם אישית לצמתים ספציפיים. לדוגמה, עבור ImageNode, ייצאו alt ו-src. זה משפר את LCP ואת נראות התוכן.
איך אנו משלבים את Lexical ב-CMS שלכם
אנו מטמיעים את העורך בארכיטקטורה שלכם: backend, frontend, API. שקלו דוגמה עם React 18 ו-TypeScript 5. לאחר התקנת חבילות, אנו יוצרים קונפיגורציה עם ערכת נושא ורשימת צמתים — הסט הבסיסי מכסה 80% מהמשימות. הוסיפו תוספים להיסטוריה, פוקוס אוטומטי וקישורים.
import { LexicalComposer } from '@lexical/react/LexicalComposer';
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin';
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
import { ListPlugin } from '@lexical/react/LexicalListPlugin';
import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
import { HeadingNode, QuoteNode } from '@lexical/rich-text';
import { ListNode, ListItemNode } from '@lexical/list';
import { LinkNode, AutoLinkNode } from '@lexical/link';
import { CodeNode, CodeHighlightNode } from '@lexical/code';
const editorConfig = {
namespace: 'cms-editor',
theme: {
paragraph: 'mb-3',
heading: {
h1: 'text-3xl font-bold',
h2: 'text-2xl font-bold',
h3: 'text-xl font-bold'
},
list: {
ol: 'list-decimal ml-6',
ul: 'list-disc ml-6'
},
text: {
bold: 'font-bold',
italic: 'italic',
underline: 'underline'
}
},
nodes: [HeadingNode, QuoteNode, ListNode, ListItemNode, LinkNode, AutoLinkNode, CodeNode, CodeHighlightNode],
onError: console.error
};
function LexicalEditor({ initialState, onChange }) {
return (
<LexicalComposer initialConfig={{ ...editorConfig, editorState: initialState }}>
<div className="relative border rounded-lg">
<ToolbarPlugin />
<div className="relative">
<RichTextPlugin
contentEditable={<ContentEditable className="outline-none p-4 min-h-96" />}
placeholder={<div className="absolute top-4 left-4 text-gray-400">Начните вводить...</div>}
ErrorBoundary={({ children }) => children}
/>
<HistoryPlugin />
<AutoFocusPlugin />
<ListPlugin />
<LinkPlugin />
<OnChangePlugin
onChange={(editorState) => {
editorState.read(() => {
const json = editorState.toJSON();
onChange(JSON.stringify(json));
});
}}
/>
</div>
</div>
</LexicalComposer>
);
} יצירת צומת מותאם אישית: מדריך שלב אחר שלב
- ירשו מ-
import { LexicalComposer } from '@lexical/react/LexicalComposer'; import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'; import { ContentEditable } from '@lexical/react/LexicalContentEditable'; import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin'; import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin'; import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin'; import { ListPlugin } from '@lexical/react/LexicalListPlugin'; import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin'; import { HeadingNode, QuoteNode } from '@lexical/rich-text'; import { ListNode, ListItemNode } from '@lexical/list'; import { LinkNode, AutoLinkNode } from '@lexical/link'; import { CodeNode, CodeHighlightNode } from '@lexical/code'; const editorConfig = { namespace: 'cms-editor', theme: { paragraph: 'mb-3', heading: { h1: 'text-3xl font-bold', h2: 'text-2xl font-bold', h3: 'text-xl font-bold' }, list: { ol: 'list-decimal ml-6', ul: 'list-disc ml-6' }, text: { bold: 'font-bold', italic: 'italic', underline: 'underline' } }, nodes: [HeadingNode, QuoteNode, ListNode, ListItemNode, LinkNode, AutoLinkNode, CodeNode, CodeHighlightNode], onError: console.error }; function LexicalEditor({ initialState, onChange }) { return ( <LexicalComposer initialConfig={{ ...editorConfig, editorState: initialState }}> <div className="relative border rounded-lg"> <ToolbarPlugin /> <div className="relative"> <RichTextPlugin contentEditable={<ContentEditable className="outline-none p-4 min-h-96" />} placeholder={<div className="absolute top-4 left-4 text-gray-400">Начните вводить...</div>} ErrorBoundary={({ children }) => children} /> <HistoryPlugin /> <AutoFocusPlugin /> <ListPlugin /> <LinkPlugin /> <OnChangePlugin onChange={(editorState) => { editorState.read(() => { const json = editorState.toJSON(); onChange(JSON.stringify(json)); }); }} /> </div> </div> </LexicalComposer> ); }(לרכיבי React) או מ-DecoratorNode(לאלמנטים בלוק). - הגדירו מתודות סטטיות
ElementNodeו-getType(). - יישמו את
clone(),createDOM()ו-decorate(). - רשמו את הצומת בקונפיגורציה דרך שדה
exportJSON(). - השתמשו בתוסף לשילוב עם סרגל הכלים.
דוגמה ל-ImageNode מותאם אישית:
class ImageNode extends DecoratorNode<React.ReactElement> {
static getType() {
return 'image';
}
static clone(node: ImageNode) {
return new ImageNode(node.__src, node.__alt, node.__key);
}
constructor(private __src: string, private __alt: string, key?: string) {
super(key);
}
createDOM() {
return document.createElement('div');
}
decorate() {
return <img src={this.__src} alt={this.__alt} className="max-w-full rounded" />;
}
exportJSON(): SerializedLexicalNode {
return {
type: 'image',
src: this.__src,
alt: this.__alt,
version: 1,
};
}
} איך להגדיר עריכה משותפת ללא סכסוכים?
Lexical תומך בעריכה משותפת דרך טרנספורמציה תפעולית (OT) מובנית. אנו מגדירים שרת backend (לדוגמה, על Node.js) לסנכרון דרך WebSockets. בפרויקט אחד, יישמנו עריכה משותפת ל-10 עורכים — זמן השהיה היה פחות מ-50 אלפיות השנייה, והשגנו 99.9% זמינות בשרת העריכה המשותפת. סכסוכים נפתרים אוטומטית. אנו משתמשים בנוסף ב-Yjs ליציבות.
הניסיון והאחריות שלנו
לצוות שלנו יש ניסיון של למעלה מ-5 שנים עם Lexical ותרומה לפרויקט הקוד הפתוח. אנו מציעים אחריות ל-6 חודשים על כל השילובים. לקוחות בדרך כלל חוסכים בין $5,000 ל-$15,000 בעלויות פיתוח לשילוב סטנדרטי.
שגיאות נפוצות במהלך שילוב Lexical
- רישום צומת שגוי: יש להוסיף צמתים מותאמים אישית במפורש לקונפיגורציה.
- התעלמות מייצוא/ייבוא JSON: ללא
nodesתקין, נתונים אינם מסודרים. - חוסר בטיפול בשגיאות: השתמשו תמיד ב-
class ImageNode extends DecoratorNode<React.ReactElement> { static getType() { return 'image'; } static clone(node: ImageNode) { return new ImageNode(node.__src, node.__alt, node.__key); } constructor(private __src: string, private __alt: string, key?: string) { super(key); } createDOM() { return document.createElement('div'); } decorate() { return <img src={this.__src} alt={this.__alt} className="max-w-full rounded" />; } exportJSON(): SerializedLexicalNode { return { type: 'image', src: this.__src, alt: this.__alt, version: 1 }; } }בקונפיגורציה.
שלבי השילוב
| שלב | מה אנו עושים | תוצרים |
|---|---|---|
| ניתוח | איסוף דרישות: צמתים נדרשים, עריכה משותפת, פורמט אחסון | מפרט |
| אב טיפוס | פריסת אפליקציה מינימלית עם 3–5 צמתים בסיסיים | מאגר עם Docker |
| יישום | כתיבת צמתים מותאמים אישית, תוספים, סרגל כלים, שילוב backend | בקשת משיכה |
| בדיקות | בדיקות אוטומטיות עם Jest + Playwright לתרחישים קריטיים | כיסוי > 80% |
| פריסה | הגדרת CI/CD, ניטור דרך Sentry | גישה לייצור |
לוחות זמנים ותקציב
פרויקט טיפוסי אורך 2 עד 4 ימי עבודה. אם נדרשים יותר מ-5 צמתים מותאמים אישית, אנו מוסיפים יומיים. הודות לניסיון המצטבר, אנו מפחיתים את התקציב ב-30–50% בהשוואה לפיתוח פנימי. העלות מחושבת באופן אישי לפי מורכבות.
מה אתם מקבלים
- עורך פונקציונלי מלא עם סרגל כלים, תמיכה ב-Rich Text, רשימות, קישורים וקוד
- צמתים מותאמים אישית לפי המפרט הטכני שלכם
- שילוב backend (שמירה ל-JSON, ייצוא ל-HTML)
- תיעוד והדרכה לעורכים
- חודשיים של תמיכה חינם לאחר המסירה
פרטים טכניים לעבודה עם צמתים מותאמים אישית
כדי לרשום צומת ב-Lexical, עליכם לבצע את השלבים הבאים: לרשת מ-DecoratorNode, ליישם getType(), clone(), createDOM(), decorate() ו-exportJSON(). לאחר מכן הוסיפו את הצומת לרשימת הצמתים בקונפיגורציה. שימו לב שכאשר מייצאים JSON, קריטי ליישם את המתודה exportJSON(), אחרת נתונים לא יישמרו. לייבוא, השתמשו ב-editor.parseEditorState() עם הסריאליזר המתאים.הזמינו שילוב Lexical — קבלו עורך מוכן תוך יומיים. צרו קשר כדי לדון בפרויקט שלכם. קבלו ייעוץ — פשוט כתבו לנו.







