ذخیرهسازی راه دور
ارسال کل وضعیت ویرایشگر با هر تغییر ناکارآمد است.
برای چنین حالتی، ویرایشگر یک API برای رونوشتبرداری بلادرنگ دادهها فراهم میکند.
نصب
npm install @dineug/erd-editor
مخزن رونوشت در زیرمسیر @dineug/erd-editor/engine.js قرار دارد، نه در ریشهٔ بسته.
این نقطهٔ ورود به DOM دست نمیزند، بنابراین در یک Web Worker یا هر جای دیگری بیرون از رشتهٔ اصلی اجرا میشود.
وارد کردن ریشهٔ بسته بهجای آن، عنصر سفارشی <erd-editor> را ثبت میکند که به یک سند DOM نیاز دارد.
نحوهٔ استفاده
type ReplicationStore = {
readonly value: string;
readonly runtimeValue: string;
on: (
listeners: Partial<{ change: (change: ReplicationChange) => void }>
) => Unsubscribe;
setInitialValue: (value: string) => void;
dispatch: (actions: Array<AnyAction> | AnyAction) => void;
dispatchSync: (actions: Array<AnyAction> | AnyAction) => void;
destroy: () => void;
};
type ReplicationChange = {
value: string;
changed: boolean;
};
type InjectEngineContext = {
toWidth: (text: string) => number;
};
type CreateReplicationStore = (
context: InjectEngineContext
) => ReplicationStore;
// example
import { createReplicationStore } from '@dineug/erd-editor/engine.js';
const replicationStore = createReplicationStore({
toWidth: text => text.length * 10,
});
رونوشتبرداری از ویرایشگر در حال اجرا
کنشها از مخزن مشترک یک ویرایشگر در حال اجرا میآیند.
ویرایش مشارکتی را ببینید.
import { createReplicationStore } from '@dineug/erd-editor/engine.js';
const replicationStore = createReplicationStore({ toWidth });
replicationStore.setInitialValue(editor.runtimeValue);
replicationStore.on({
change: ({ value, changed }) => {
if (changed) save(value);
},
});
const sharedStore = editor.getSharedStore();
sharedStore.subscribe(actions => {
replicationStore.dispatch(actions);
});
فقط تغییر از مرز عبور میکند و کل سند هرگز از آن نمیگذرد.
رونوشت آن را اعمال میکند و نتیجه را در سمت خودش سریالسازی میکند.
رویداد change آن نتیجه را بهصورت value در اختیارتان میگذارد و changed نشان میدهد چه زمانی نوشتن آن ارزش دارد.
از نسخهٔ 4.0.0، رونوشت را مانند مثال بالا با runtimeValue ویرایشگر مقداردهی اولیه کنید، نه با فایل ذخیرهشده یا value ویرایشگر.
فایل ویرایشهایی را که پس از ذخیره شدنش انجام شدهاند ندارد و value موجودیتهایی را که در نشست حذف شدهاند ندارد.
رونوشتی که بدون یک جدول حذفشده مقداردهی اولیه شده باشد، وقتی یک ویرایشگر آن حذف را واگرد کند، جدول را بدون نام و ستونهایش برمیگرداند و آن را به همان شکل ذخیره میکند.
InjectEngineContext
toWidth
یک رشته را بر حسب پیکسل اندازه میگیرد.
مخزن DOM ندارد، پس خودش نمیتواند متن را اندازه بگیرد، و هر بار که یک نام، نوع داده، مقدار پیشفرض یا توضیح تغییر کند، عرض ستونها را با این تابع دوباره محاسبه میکند.
همچنین هر بار که setInitialValue سندی را بارگذاری میکند، همهٔ جدولها و ستونها را دوباره اندازه میگیرد.
از نسخهٔ 4.0.0، عرضها فقط در runtimeValue نگه داشته میشوند و هرگز در value نگه داشته نمیشوند.
بنابراین سندی که این نسخه ذخیره کرده است، هر جا که ذخیره شده باشد و متن در آنجا هر طور اندازهگیری شده باشد، هنگام بازخوانی بایت به بایت همان value را میدهد.
به همان شیوهای اندازه بگیرید که ویرایشگر اندازه میگیرد — 400 12px در پشتهٔ فونت ویرایشگر، گردشده، بهعلاوهٔ 2 px فاصلهٔ داخلی.
در غیر این صورت، عرضهای موجود در runtimeValue از عرضهای روی صفحه فاصله میگیرند.
const TEXT_PADDING = 2;
const FONT =
"400 12px -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, " +
"'Helvetica Neue', 'Open Sans', system-ui, sans-serif, " +
"'Apple Color Emoji', 'Segoe UI Emoji'";
let context = null;
function getContext() {
if (context) return context;
try {
context = new OffscreenCanvas(0, 0).getContext('2d');
if (context) context.font = FONT;
} catch {
// no canvas available
}
return context;
}
function toWidth(text) {
const context = getContext();
const width = context ? context.measureText(text).width : text.length * 10;
return Math.round(width) + TEXT_PADDING;
}
const replicationStore = createReplicationStore({ toWidth });
text.length * 10 مقدار جایگزین برای زمانی است که هیچ بومی در دسترس نباشد.
این مقدار برای مخزنی که سندش فقط ذخیره میشود و هرگز رندر نمیشود کافی است.
ReplicationStore
value getter
وضعیت فعلی ویرایشگر را سریالسازی میکند.
یک رشتهٔ JSON برمیگرداند که همانطور که هست آمادهٔ ذخیرهسازی است — نه یک شیء تجزیهشده.
const data = replicationStore.value;
از نسخهٔ 4.0.0، value شکل ذخیرهسازی است: سند به همان صورتی که یک فایل آن را نگه میدارد، همان متنی که خاصیت value عنصر برمیگرداند.
این شکل فقط موجودیتهای حذفنشده را با ترتیبی ثابت نگه میدارد، بدون موجودیتهای حذفشده، ترتیب لایهها یا هر چیزی که بارگذاری دوباره محاسبه میکند، مانند عرضهایی که toWidth اندازه میگیرد.
با دو فاصله تورفتگی دارد و با یک نویسهٔ خط جدید پایان مییابد، بنابراین یک سند یکسان همیشه بایتهای یکسانی میدهد.
برای آنچه نگه میدارد، شکل ذخیرهسازی و مقدار زمان اجرا را ببینید.
هر تنظیمی که قفل آن روشن است با مقداری که در آن قفل شده نوشته میشود و هر تنظیم دیگر با مقدار فعلیاش.
قفل Viewport مبدأ نما و سطح بزرگنمایی را با هم پوشش میدهد، و زبانه، زبان کد در مولد کد، هر دو شیوهٔ نگارش نام و نوع علامت نقلقول هر کدام قفل جداگانهٔ خود را دارند.
این قفلها جایگزین فیلد ignoreSaveSettings سند شدهاند که از نسخهٔ 4.0.0 نه خوانده میشود و نه نوشته میشود.
پیش از نسخهٔ 4.0.0، value کل سند بود به همان صورتی که مخزن نگه میداشت، با اعمال بیتهای پیمایش و بزرگنمایی آن فیلد.
runtimeValue getter
از نسخهٔ 4.0.0، سند را به همان صورتی که رونوشت نگه میدارد سریالسازی میکند، همراه با موجودیتهای حذفشده و هر آنچه بارگذاری محاسبه میکند.
اینجا هم هر تنظیم قفلشده با مقدار قفلشدهاش نوشته میشود.
const snapshot = replicationStore.runtimeValue;
از آن برای مقداردهی اولیهٔ ویرایشگر یا مخزن دیگری که بعداً میپیوندد، از طریق setInitialValue آن، استفاده کنید؛ ویرایش مشارکتی را ببینید.
هرگز آن را بهعنوان فایل ذخیره نکنید: موجودیتهای حذفشده را با خود دارد و JSON Schema منتشرشده آن را رد میکند.
همین موجودیتهای حذفشده هستند که اجازه میدهند واگرد یک حذف، که بعداً ارسال میشود، موجودیت را در مخزنی که با آن مقداردهی اولیه شده است بهطور کامل برگرداند.
آنها تا بار بعدی که سند از فایلش باز شود باقی میمانند؛ شکل فایل آنها را در بر ندارد.
setInitialValue
وضعیت ویرایشگری را که پیشتر ذخیره شده است بارگذاری میکند.
replicationStore.setInitialValue('json...');
شکل ذخیرهسازی، یک مقدار زمان اجرا یا سندی را که نسخهای قدیمیتر ذخیره کرده است، از جمله یک سند v2، میپذیرد.
یک رشتهٔ خالی، یا هر چیزی که رشته نباشد، بهجای ایجاد خطا یک سند جدید و خالی بارگذاری میکند.
مخزنی که هرگز مقداری به آن داده نشده است با همان سند جدید آغاز میشود.
از نسخهٔ 4.0.0، در آن سند هر قفل روشن است، بنابراین value آن نما، زبانه، زبان کد در مولد کد، شیوههای نگارش نام و نوع علامت نقلقولی را که با آنها آغاز شده است حفظ میکند، در حالی که ویرایشگرهایی که از آنها رونوشتبرداری میکند پیمایش میکنند، بزرگنمایی میکنند، زبانه عوض میکنند یا آن تنظیمات را تغییر میدهند. در 3.10.0، این سند نه پیمایش را ذخیره میکرد و نه بزرگنمایی را، و پیش از آن هر دو را ذخیره میکرد.
یک سند ذخیرهشده قفلهایی را که نام میبرد حفظ میکند.
سندی که هیچ قفلی را نام نمیبرد، یعنی هر سندی که پیش از نسخهٔ 4.0.0 ذخیره شده باشد، از جمله یک سند v2، با همهٔ قفلها روشن بارگذاری میشود: مبدأ نمای آن در 0, 0، سطح بزرگنمایی آن در 1 و زبانهٔ آن روی ERD، هر چه ذخیره کرده باشد، و زبان کد در مولد کد، شیوههای نگارش نام و نوع علامت نقلقول آن به همان صورتی که ذخیره کرده است.
از نسخهٔ 4.0.0، setInitialValue رجیسترهای همکاری نگهداشتهشده برای سند قبلی را هم دور میریزد، همانطور که متد setInitialValue عنصر این کار را میکند.
متنی که JSON نیست، مانند فایلی که نشانگرهای تداخل ادغام در آن باقی مانده است، نیز خطایی ایجاد نمیکند: خطای تجزیه فقط در کنسول ثبت میشود، سند خالی میشود و تنظیمات همانطور که بودند باقی میمانند.
از نسخهٔ 4.0.0، بارگذاری هیچ جمعآوری زبالهای اجرا نمیکند، بنابراین موجودیتهای حذفشده در runtimeValue باقی میمانند.
پیش از آن، موجودیتهایی را که دیگر در doc نبودند و چهار روز یا بیشتر دست نخورده بودند دور میریخت.
بازنویسیهای بارگذاری پیش از بازگشت setInitialValue تمام میشوند: نشانههای کلید روی ستونها، که از گزینههای هر ستون و رابطهها خوانده میشوند؛ عرض همهٔ جدولها و ستونها، که دوباره با toWidth اندازهگیری میشوند؛ نقاطی که هر خط اتصال به جدولهایش میرسد؛ و پرچم شناسا بودن هر رابطه و نماد انتهای والد آن.
هیچیک از آنها بخشی از value نیست، اما replicationStore.runtimeValue اگر بلافاصله پس از فراخوانی خوانده شود، همهٔ آنها را از قبل در خود دارد.
کنشی که درست پس از بارگذاری میرسد، مانند یک پیمایش بازپخششده، با سند بارگذاریشده سنجیده میشود، بنابراین خود بارگذاری هرگز بهعنوان changed گزارش نمیشود.
بارگذاری و هیچیک از این بازنویسیها رویداد change را ارسال نمیکنند.
اگر میخواهید سند بارگذاریشده دوباره به شکل ذخیرهسازی نوشته شود، پس از بازگشت setInitialValue خودتان replicationStore.value را بخوانید.
on@change
مشترک تغییرات وضعیت ویرایشگر میشود.
const unsubscribe = replicationStore.on({
change: ({ value, changed }) => {
if (changed) {
// save value...
}
},
});
change با تأخیر تجمیعی 200 میلیثانیه ارسال میشود، بنابراین رگباری از کنشها بهصورت یک اطلاعرسانی واحد میرسد.
این رویداد فقط برای کنشهایی از نوعی که میتواند سند را تغییر دهد ارسال میشود، بهعلاوهٔ settings.changeIgnoreSaveSettings که ویرایشگری قدیمیتر ممکن است هنوز ارسال کند (dispatch, dispatchSync را ببینید).
یک کنش حضور بازارسالشده یا editor.mergeLWW هرگز آن را فعال نمیکند.
برای لغو اشتراک، تابع بازگرداندهشده را فراخوانی کنید.
از نسخهٔ 3.10.0، شنونده یک ReplicationChange دریافت میکند، نوعی که @dineug/erd-editor/engine.js در کنار ReplicationStore صادر میکند.
پیش از نسخهٔ 3.10.0، شنونده چیزی دریافت نمیکرد و شنوندهای که آرگومان را نادیده میگیرد همچنان کار میکند.
value سند سریالسازیشده در شکل ذخیرهسازی است، همان رشتهای که replicationStore.value در آن لحظه برمیگرداند، بنابراین نیازی به سریالسازی دوبارهٔ آن نیست.
changed زمانی false است که کنشهای پس از change قبلی، یا پس از بارگذاری، value را بایت به بایت همانطور که بود باقی گذاشته باشند.
تغییری که یک قفل آن را از value بیرون نگه میدارد همچنان change را ارسال میکند، با changed برابر با false، پس فقط زمانی بنویسید که changed برابر با true باشد.
این یعنی پیمایش یا بزرگنمایی وقتی Viewport قفل است، تعویض زبانه وقتی Canvas Type قفل است، و تغییر زبان کد در مولد کد، یک شیوهٔ نگارش نام یا نوع علامت نقلقول وقتی قفل خود آن روشن است.
رویداد change عنصر چنین تغییری را کنار میگذارد، اما رونوشت همچنان آن را گزارش میکند.
در یک سند جدید و در سندی که پیش از نسخهٔ 4.0.0 ذخیره شده باشد، همهٔ قفلها روشناند.
وقتی قفل Viewport باز باشد، پیمایش یا بزرگنمایی value را تغییر میدهد، و وقتی قفل Canvas Type باز باشد، باز کردن زبانهٔ دیگری از ویرایشگر نیز همین کار را میکند.
روشن یا خاموش کردن یک قفل نیز value را تغییر میدهد.
از نسخهٔ 4.0.0، changed شکل ذخیرهسازی را مقایسه میکند که نه موجودیتهای حذفشده را در خود دارد و نه ترتیب لایهها را.
بنابراین جدولی که در یک change اضافه و دوباره حذف شود، ویرایشی که روی موجودیتی اعمال شود که همکار دیگری حذفش کرده است، و یک بارگذاری بازارسالشده که فقط ترتیب لایهها را تغییر میدهد نیز همگی با changed برابر با false گزارش میشوند.
changed هرگز با فایلی که ذخیره کردهاید مقایسه نمیکند، فایلی که ممکن است از همان لحظهٔ بارگذاری با value متفاوت باشد: فایلی که نسخهای قدیمیتر ذخیره کرده است، از جمله یک فایل v2، چنین است و فایلی که با نشانهٔ ترتیب بایت آغاز میشود و فایلی که دستی ویرایش شده است نیز همینطور.
مقایسهٔ بایتها با چنین فایلی حتی پیمایشی را که یک قفل بیرون نگه میدارد یک ویرایش به حساب میآورد.
نخستین change با changed برابر با true چنین فایلی را به شکل ذخیرهسازی بازمیگرداند، بنابراین نوشتن آن فایل را یک بار بازنویسی میکند.
فایلی که این نسخه ذخیره کرده است، هر جا که ذخیره شده باشد، تفاوتی ندارد، همانطور که toWidth توضیح میدهد.
اگر setInitialValue زمانی اجرا شود که یک change هنوز در انتظار است و پیش از موعد آن هیچ کنشی ارسال نشود، آن change همچنان میرسد، با value بارگذاریشده و changed برابر با false.
dispatch, dispatchSync
تغییرات ویرایشگرهای راه دور را روی replicationStore اعمال میکند.
replicationStore.dispatch(actions); // async
replicationStore.dispatchSync(actions); // sync
dispatch اعمال را به یک ریزوظیفه موکول میکند و dispatchSync دسته را بیدرنگ اعمال میکند.
میتوانید دنبالهٔ کنشهای یک مخزن مشترک را بدون فیلتر تحویل دهید.
هر دو متد فقط کنشهایی از نوعی را که میتواند سند را تغییر دهد، بهعلاوهٔ دو کنشی که در پایان این بخش توضیح داده شدهاند، نگه میدارند و بقیه را دور میریزند، بنابراین اطلاعات حضوری که یک نشست مشارکتی منتقل میکند — نشانگر ماوس، فوکوس، انتخاب و مستطیل انتخاب هر کاربر — بدون هیچ اثری عبور میکند.
از نسخهٔ 3.10.0، کنشهای نگهداشتهشده شامل relationship.changeOnDelete و relationship.changeOnUpdate هستند و relationship.add فیلدهای onDelete و onUpdate را در خود دارد.
مخزنی از نسخهای قدیمیتر آن کنشها را دور میریزد و آن فیلدها را نادیده میگیرد، بنابراین کنشهای ON DELETE و ON UPDATE یک رابطه را از دست میدهد.
از نسخهٔ 4.0.0، کنشهای نگهداشتهشده همچنین شامل table.changeGroup، هفت کنش از هشت کنش tableGroup (tableGroup.add، tableGroup.move، tableGroup.moveTo، tableGroup.remove، tableGroup.resize، tableGroup.changeName و tableGroup.changeColor، یعنی همه بهجز tableGroup.changeZIndex که ترتیب لایهها را تغییر میدهد)، relationship.changeColumns، settings.changeLockSettings و settings.changeDDLScript هستند.
settings.changeRelationshipOptimization دیگر وجود ندارد، بنابراین هر دو متد آن را دور میریزند.
مخزنی از نسخهای قدیمیتر کنشهای جدید را دور میریزد، بنابراین گروههای جدول، قفلها، اسکریپتهای طرحواره SQL و ستونهایی را که نگاشت ستونها به یک رابطهٔ موجود میدهد از دست میدهد.
رونوشت را روی همان نسخهای که ویرایشگرها دارند، یا نسخهای جدیدتر، اجرا کنید.
نسخهٔ 4.1.0 هیچ نوع کنشی اضافه نمیکند و @dineug/erd-editor/engine.js را همانطور که بود باقی میگذارد، بنابراین رونوشتی با نسخهٔ 4.0.0 هر تغییری را که ویرایشگری با نسخهٔ 4.1.0 ارسال میکند نگه میدارد، درست همانطور که رونوشتی با نسخهٔ 4.1.0 نگه میدارد.
از نسخهٔ 4.1.0، Replace و Replace All در یافتن و جایگزینی نام شاخصها و گروههای جدول را نیز تغییر میدهند، از طریق کنشهای index.changeName و tableGroup.changeName که رونوشتی با نسخهٔ 4.0.0 از قبل نگه میدارد.
وضعیت خود کاوشگر هرگز ارسال نمیشود و تعویض زبانه یا پیمایشی که کاوشگر انجام میدهد مانند هر تعویض زبانه یا پیمایش دیگری میرسد؛ ویرایش مشارکتی را ببینید.
از نسخهٔ 4.0.0، هر دو متد editor.mergeLWW را نیز اعمال میکنند، کنشی که رجیسترهای همکاریای را در خود دارد که یک ویرایشگر هنگام پیوستن ویرایشگری دیگر به نشست با آنها پاسخ میدهد، بنابراین رونوشت ویرایشهای همزمان را به همان شیوهٔ ویرایشگرها حلوفصل میکند.
این کنش هیچ بایتی از value را تغییر نمیدهد و هیچ رویداد change ارسال نمیکند.
settings.changeIgnoreSaveSettings که ویرایشگری منتشرشده پیش از نسخهٔ 4.0.0 ممکن است هنوز ارسال کند، نگه داشته میشود اما چیزی را تغییر نمیدهد: رویداد change را با changed برابر با false ارسال میکند.
destroy
نمونهٔ replicationStore را بهطور کامل از بین میبرد.
replicationStore.destroy();
پس از آن، مخزن دیگر change ارسال نمیکند و فراخوانیهای بعدی dispatch را نادیده میگیرد.
سند بهجای ایجاد خطا در آخرین مقدار خود ثابت میماند.
برای شروع دوباره، یک مخزن جدید ایجاد کنید.