effector-storage v6 – DEV Community

عجب پرش بلندی بود! اما حالا effector-storage نسخه 6 فرود آمد.
بنابراین، چه چیز جدیدی است؟
پشتیبانی از قراردادها
این می تواند یک تغییر جزئی برای اکثر کاربران این کتابخانه باشد، اما من این را گامی بزرگ به سوی تجارت جدی می دانم. “قرارداد” جدی به نظر می رسد، اینطور نیست؟
مانند هر منبعی که کد شما کنترل کاملی روی آن ندارد، localStorage ارزش ها هرگز نباید قابل اعتماد باشند و نیاز به تایید دارند. هر بچه اسکریپتی می تواند DevTools را باز کند و تغییر دهد localStorage ارزش، بنابراین برنامه پول کلان شما را شکست. برای جلوگیری از آن، داده ها باید در دو سطح – ساختاری و تجاری – اعتبارسنجی شوند. effector-storage کتابخانه نمی تواند در منطق کسب و کار شما به شما کمک کند، اما حداقل، اکنون می تواند ارزش ها را در مقابل قراردادها تأیید کند.
در یک مورد ساده، قرارداد می تواند فقط یک محافظ نوع ساده باشد:
persist({
store: $counter,
key: 'counter',
contract: (raw): raw is number => typeof raw === 'number',
fail: failed
})
بنابراین، اگر localStorage اکنون به نوعی شامل یک رشته خواهد شد – persist بازیابی فروشگاه ناموفق خواهد بود $counter، و ماشه failed رویداد با operation: "validate".
البته توجه داشته باشید که قراردادها پس از تجزیه (جداسازی) کار می کنند، بنابراین اگر localStorage مقدار اصلاً یک رشته JSON معتبر نیست — persist شکست خواهد خورد، باعث می شود failed رویداد با operation: "get".
در موارد پیچیده تر، زمانی که باید داده ها را در برابر برخی ساختارهای پیچیده با قوانین پیچیده تأیید کنید – effector-storage به طور کامل از قراردادهای Farfetched پشتیبانی می کند، بنابراین می توانید از هر آداپتور Farfetched استفاده کنید – runtypes، zod، io-ts، superstruct، typed-contracts:
import { Record, Literal, Number } from 'runtypes'
import { runtypeContract } from '@farfetched/runtypes'
const Asteroid = Record({
type: Literal('asteroid'),
mass: Number,
})
persist({
store: $asteroid,
key: 'asteroid',
contract: runtypeContract(Asteroid),
fail: failed
})
دو مشکل در قراردادها وجود دارد:
- از جانب
effector-storageاز نظر زمانی که هنوز هیچ مقدار ثابتی در ذخیره سازی وجود ندارد، کاملاً طبیعی است. بنابراین،undefinedارزش است همیشه معتبر است، رویداد در صورتی که قرارداد به صراحت اجازه آن را نمی دهد. -
effector-storageاز باقی ماندن داده های نامعتبر در حافظه جلوگیری نمی کند، اما با این وجود، پس از ماندگاری، آن را تأیید می کند، بنابراین، اگر داده های نامعتبر را در حافظه بنویسید،failفعال خواهد شد، اما داده ها باقی خواهند ماند.
آداپتورهای صادراتی
گاهی اوقات لازم است که فروشگاه های مختلف در حافظه های مختلف در یک ماژول باقی بمانند. و برای جلوگیری از درگیری نام ها در نسخه 5 باید چیزی شبیه به این بنویسید:
import { persist as localPersist } from 'effector-storage/local'
import { persist as queryPersist } from 'effector-storage/query'
localPersist({ store: $session })
queryPersist({ store: $page })
در نسخه 6 همه آداپتورها در حال حاضر همراه با صادر می شوند persist عملکرد، و می تواند به تنهایی استفاده شود. علاوه بر این، همه آداپتورهای (عمده) از بسته ریشه نیز صادر می شوند، بنابراین می توانید همه آنها را یکباره با یک عبارت import استفاده کنید:
import { persist, local, query } from 'effector-storage'
persist({ adapter: local, store: $session })
persist({ adapter: query, store: $page })
به طور دقیق، وارداتی است local و query در مثال بالا نیست آداپتورها، ولی کارخانه های آداپتور. بنابراین می توانید با کارخانه تماس بگیرید تا یک نمونه آداپتور، اگر به تنظیمات آداپتور نیاز دارید:
persist({
store: $date,
adapter: local({
serialize: (date) => String(date.getTime()),
deserialize: (timestamp) => new Date(Number(timestamp)),
}),
})
یا می توانید هر آرگومان کارخانه آداپتور را مستقیماً به آن منتقل کنید persist!
persist({
store: $date,
adapter: local,
serialize: (date) => String(date.getTime()),
deserialize: (timestamp) => new Date(Number(timestamp)),
})
ابزارهای آداپتور
با آداپتورهای صادراتی ایده جدیدی به ذهن من خطور کرد: اگر بتوانید یک تابع سفارش بالا بنویسید که آداپتور (یا کارخانه آداپتور) را می پذیرد و کمی رفتار آن را تغییر می دهد، چه می شود؟
بنابراین، من چند مورد ساختم:
async تابع سودمند
هر آداپتور ذخیره سازی همزمان (یا کارخانه ای) را می گیرد و آن را ناهمزمان می کند:
persist({
adapter: async(local),
store: $counter,
done: doneEvent,
})
این می تواند مفید باشد، زمانی که نیاز دارید بازیابی فروشگاه را کمی به تعویق بیندازید. اگر استفاده نمی کنید pickup گزینه، persist شروع به بازیابی وضعیت فروشگاه می کند درست در همین لحظه. بنابراین، در صورت همزمان localStorage، بلافاصله بعد persist اجرا می شود – مقدار ذخیره قبلاً از فضای ذخیره سازی بازیابی شده است (البته اگر قبلاً ادامه داشت). گاهی اوقات می تواند منطق کسب و کار شما را تحت تاثیر قرار دهد، برای مثال، اگر شما نیاز داشته باشید واکنش نشان دهند در بازیابی فروشگاه، باید آن رفتار را در نظر بگیرید و همه اتصالات را تعریف کنید (مانند sampleث) قبل از تماس persist.
async تابع ابزار می تواند در این زمینه به شما کمک کند و شما را از بار حفظ نظم اعلامیه ها در ذهن رها کند.
either تابع سودمند
دو آداپتور (یا کارخانه) می گیرد و اولی آنها را برمی گرداند که “بدون عملیات” نیست.
آداپتور “no-op” چیست؟
نسخه 6 دو “اصطلاح” یا “حالت” جداگانه برای آداپتورها معرفی می کند:
- پشتیبانی شده – به این معنی که فضای ذخیره سازی توسط محیط پشتیبانی می شود، به عنوان مثال،
localStorageدر مرورگرها پشتیبانی می شود، اما در Nodejs پشتیبانی نمی شود. - در دسترس – به این معنی که فضای ذخیره سازی پشتیبانی می شود و در دسترس است، برای مثال،
localStorageممکن است توسط سیاست های امنیتی مرورگر ممنوع شود.
بنابراین، اگر آداپتور در حال انجام است هیچ چی (پسندیدن nil آداپتور)، یا اینطور نیست پشتیبانی – این آداپتور “بدون عملیات” است.
persist({
store: $store,
adapter: either(local, log),
key: 'store'
})
در مثال بالا فروشگاه $store در ادامه خواهد داشت localStorage در مرورگر، و از new استفاده خواهد کرد log آداپتور، که فقط عملیات را در Nodejs چاپ می کند.
شکستن تغییر❗️در نسخه 5 local آداپتور مثل این رفتار می کرد پشتیبانی نشده (و هیچ کاری نکرد) وقتی localStorage توسط سیاست های امنیتی مرورگر ممنوع است. در نسخه 6 این در نظر گرفته شده است در دسترس نیست، بدین ترتیب، local آداپتور خطای امنیتی را در هر عملیات ایجاد می کند. شما می توانید این کار را انجام دهید و از کاربر بخواهید که فعال کند localStorage، مثلا.
farcached تابع سودمند
هر آداپتور کش را از Farfetched می گیرد و آن را برای استفاده به عنوان تبدیل می کند persist آداپتور 🙂
این یکی را بیشتر از روی سرگرمی و برای نشان دادن امکانات ساختم که با رویکرد توابع سودمند باز می شود.
از نقطه نظر استفاده واقعی، استفاده از آداپتورهای کش Farfetched می تواند مفید باشد، زمانی که به منطق برای باطل کردن کش نیاز دارید، زیرا همه آداپتورهای آن دارای maxAge گزینه خارج از جعبه
همچنین، میتوانید از آداپتورهای کش Farfetched برای تزریق آداپتورهای کش مختلف استفاده کنید fork استفاده كردن cache.__.$instance فروشگاه داخلی
import { localStorageCache } from '@farfetched/core'
persist({
store: $counter3,
adapter: farcached(localStorageCache({ maxAge: '15m' })),
})
زمینه آداپتور
این یک ویژگی آزمایشی است و من فکر می کنم برای مدتی مانند اسب تیره خواهد بود 🤫 اما من قصد دارم آن را به طور کامل در نسخه های کوچک بعدی نشان دهم.
در چند کلمه، زمینه دو هدف اصلی را هدف قرار داده است:
- به روز رسانی ناهمزمان از ذخیره سازی در محدوده
- استفاده از محیط های مختلف در آداپتور
می توانید بگذرید متن نوشته به دو صورت:
- مانند
pickupبار رویداد - به عنوان نو
contextبار رویداد
persist به خاطر خواهد آورد محدوده، که در آن تماس گرفته اید pickup یا context، و از این محدوده برای هر گونه به روز رسانی ناهمزمان از فضای ذخیره سازی (مانند روشن "storage" رویداد).
همچنین، متن نوشته به عنوان آرگومان دوم در آداپتور ارسال خواهد شد get و set توابع، و آداپتور می تواند آن را به دلخواه استفاده کند.
حالت همگام سازی اجباری
به دلیل بهینه سازی عملکرد جزئی، زمانی که local آداپتور دریافت می کند "storage" رویداد – مقدار فضای ذخیرهسازی جدیدی را از محموله این رویداد میگیرد. بدیهی است که نیازی به خواندن نیست (نسبتا کند) localStorage زمانی که مقدار جدید دقیقاً در اینجا، در داخل رویداد از قبل دریافت شده باشد.
اما در برخی موارد این رویکرد میتواند منجر به همگامسازی فروشگاهها با بهروزرسانیهای همزمان یا تقریباً همزمان از برگههای مختلف شود. می تونی به این زنگ بزنی شرایط مسابقه.
این اتفاق می افتد زیرا "storage" رویداد در حال وقوع است فقط هنگامی که ذخیره سازی از اصلاح شده است یکی دیگر برگه/پنجره بنابراین، در هر شرایطی با همزمان یا تقریباً همزمان localStorage به روز رسانی از بیش از یک برگه – همیشه یک برگه بازنده وجود خواهد داشت که ناهمگام می شود. من یک تصویر با دو برگه ترسیم کردهام: هر برگه از یک برگه دیگر بهروزرسانی دریافت میکند و یکی از برگهها به ناچار با آن همگام نمیشود. localStorage:
من این مورد بسیار نادر را در نظر میگیرم، بنابراین، نسخه 6 رفتار پیشفرض را تغییر نمیدهد (با استفاده از مقدار جدید از رویداد)، اما مقدار ممکن جدیدی را برای آن اضافه میکند. sync در عوض گزینه – 'force':
persist({
store: $balance,
key: 'balance',
sync: 'force'
})
این مقدار جدید از را نادیده می گیرد "storage" رویداد، و به اجبار مقدار جدید را از آن می خواند localStorage در عوض، بنابراین مشکل عدم هماهنگی را از بین می برد.
دریچه گاز/بچ می نویسد
نسخه 6 گزینه جدیدی را اضافه می کند timeout برای سه آداپتور: local، session و query. برای آنها کمی متفاوت رفتار می کند، اما معنی آن واضح است – نوشتن را به یک حافظه به تعویق بیندازید.
برای local و session (و پایه storage) آداپتورها timeout گزینه شبیه به دریچه گاز برای مجرد persist (بدون نوشتن پیشرو). بنابراین، اگر فروشگاهی دارید که بهطور مکرر بهروزرسانی میشود و در آن ادامه دارد localStorage، می توانید با محدود کردن دسترسی به آن، عملکرد را کمی بهبود بخشید localStorage با این گزینه
برای query آداپتور timeout گزینه شبیه به دریچه گاز (بدون نوشتن پیشرو) و دسته ای. بنابراین، اگر فروشگاههای زیادی دارید که در یک query string وجود دارند، میتوانید از این گزینه برای بهروزرسانی و دستهبندی بهروزرسانیهای رشته query استفاده کنید.
پشتیبانی از Drop Effector v21
نسخه 5 به طور کامل از Effector v21 پشتیبانی می کند که در 6 ژوئیه 2020 – تقریباً سه سال پیش منتشر شد. این امکان پذیر است، زیرا effector-storage فقط از عملکرد پایه افکتور استفاده می کند، و به این دلیل که Effector سازگاری عالی با عقب دارد.
اما زمان ادامه دارد و Effector v22 در 31 آگوست 2021 – تقریباً دو سال پیش – منتشر شد. effector-storage در حال حاضر چند ترفند کوچک برای پشتیبانی از نسخههای مختلف و ویژگیهای جدید دارد، بنابراین، فکر میکنم، زمان آن رسیده که پشتیبانی از نسخه 21 را متوقف کنیم.

همچنین، Nodejs v14 در 30 آوریل 2023 به پایان عمر خود رسیده است – بنابراین، نسخه 6 نیز آن را حذف می کند. و شما هم باید همینطور 😉
این تمام چیزی است که امروز برای شما گرفتم، با ما همراه باشید 😉



