دليل الترحيل
تتناول هذه الصفحة أمرين: مخطّط المفاهيم العام للانتقال من Vue (lh-ui-plus / lh-ui-vue2) إلى React، والفروق المتعمَّدة بين تنفيذ هذه المكتبة ونُسخ Vue.
جدول مخطّط المفاهيم
| Vue (lh-ui-plus) | React (@lh-ui/react) |
|---|---|
defineProps<T>() | interface LhXxxProps + معاملات افتراضية مفكوكة |
withDefaults(...) | قيم افتراضية مفكوكة (دون defaultProps) |
emits + emit('change', [val, e]) | onChange?: (value, event) => void |
v-model="x" | value={x} onChange={...} |
v-model:visible="x" | visible={x} onVisibleChange={...} |
defineExpose({ ... }) | forwardRef + useImperativeHandle |
<slot> | children |
<slot name="footer"> | prop footer?: ReactNode |
الفتحة المسمَّاة #header | prop header?: ReactNode |
provide(...) / inject(...) | Context.Provider / useContext |
يجب أن تكون provide عبارة عن computed | يجب تغليف قيمة Context بـ useMemo |
computed(() => ...) | useMemo(() => ..., [deps]) |
watch(src, cb) | useEffect(() => cb(), [src]) |
watch(src, cb, { immediate: true }) | useEffect (يُنفَّذ effect مرة واحدة فورًا بطبيعته) |
onMounted / onUnmounted | setup / cleanup في useEffect |
<Transition name="lh-x-fade"> | useTransition(visible, duration) يقود الأسماء نفسها |
<Teleport to="body"> | createPortal(..., document.body) |
ref="<name>" | useRef<HTMLDivElement>(null) |
nextTick() | flushSync() أو effect (غالبًا لا حاجة) |
app.use(LhUI) | لا شيء — استيراد مسمَّى عند الحاجة |
<LhXxx v-bind="obj"> | <LhXxx {...obj}> |
التحكم وعدم التحكم
تنقسم modelValue / update:modelValue في Vue إلى مجموعتي props في React:
<!-- Vue -->
<LhInput v-model="text" />
// React: متحكَّم
<LhInput value={text} onChange={(val) => setText(val)} />
// React: غير متحكَّم
<LhInput defaultValue="قيمة افتراضية" />
الجمع بينهما (تمرير value وdefaultValue معًا) يُطبع تحذيرًا في وضع التطوير، وذلك يتوافق مع دلالات النماذج الأصلية في React.
استدعاءات الأحداث
توحَّد جميع أحداث props في تسمية onXxx، ويُوحَّد معامل الاستدعاء في معاملين (value, event):
<!-- Vue -->
<LhSelect @change="onChange" />
// React
<LhSelect onChange={(value, option) => console.log(value, option)} />
الحركة الانتقالية
إن <Transition> في Vue تصريحية، بينما تستخدم نسخة React hookًا ذاتي التطوير useTransition لقيادة الأسماء نفسها:
import { useTransition } from '@lh-ui/react'
const { mounted, phase } = useTransition(visible, 300)
// أسماء الأصناف: `lh-dialog-fade-${phase}` —— phase ∈ enter-from / enter-active / enter-to
أسماء الأصناف مطابقة تمامًا لـ SCSS في share، والمظهر البصري متماثل إطارًا بإطار مع نسخة Vue.
الطبقات المنبثقة والنقل
يقابل <Teleport to="body"> في React كلٌّ من createPortal، وتُعلَّق مربعات الحوار وقوائم Select المنسدلة وTooltip جميعها على document.body. ويُحتسَب هندسة التموضع عبر useLhPopper (بمشاركة @lh-ui/share/core/shared/popper مع نسخة Vue).
ref الحتمي
يقابل defineExpose في React كلٌّ من forwardRef + useImperativeHandle:
<!-- Vue -->
<LhForm ref="formRef" />
// React
const formRef = useRef<LhFormInstance>(null)
<LhForm ref={formRef} />
<formRef.current?.validate() />
التكوين العام
<!-- Vue -->
<LhConfigProvider :size="size">
<App />
</LhConfigProvider>
// React
<LhConfigProvider size={size}>
<App />
</LhConfigProvider>
فروق التنفيذ بين هذه المكتبة ونُسخ Vue
الفروق التالية تعديلات مقصودة في الشكل Reactي، يُرجى الانتباه إليها عند الترحيل. وكل بند مُسجَّل داخل المستودع.
1. حقن الأنماط عند الحاجة
على خلاف lh-ui-plus (حقن كامل)، تتوافق هذه المكتبة مع lh-ui-vue2: لا يُحقِن مدخل الحزمة أنماطًا، بل يستورِد المستخدم صراحةً theme.css + style/<name>.