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 نیز آن را حذف می کند. و شما هم باید همینطور 😉
این تمام چیزی است که امروز برای شما گرفتم، با ما همراه باشید 😉