Cally日历组件与主流前端框架集成指南
前言
Cally是一款基于Web Components技术构建的日历组件库,具有框架无关的特性。本文将详细介绍如何将Cally与React、Vue和Svelte等主流前端框架集成,并提供TypeScript支持的最佳实践。
Web Components基础
Web Components是一组浏览器原生支持的API,允许开发者创建可重用的自定义元素。Cally正是基于这一技术构建,因此具备以下优势:
- 框架无关性:可以在任何前端框架中使用
- 原生浏览器支持:不需要额外的运行时库
- 封装性:样式和行为都被封装在组件内部
与React集成
React的特殊处理
虽然React官方支持Web Components,但在实际使用中存在一些限制:
- 属性传递:React会将所有属性转换为字符串
- 事件监听:需要手动添加事件监听器
- 方法调用:需要通过ref直接访问DOM元素
解决方案:创建React包装组件
我们可以通过创建高阶组件来封装Cally的原生组件,使其更符合React的开发模式:
import { useEffect, useRef, forwardRef, useImperativeHandle } from "react";
import "cally";
// 自定义Hook处理事件监听
function useListener(ref, event, listener) {
useEffect(() => {
const current = ref.current;
if (current && listener) {
current.addEventListener(event, listener);
return () => current.removeEventListener(event, listener);
}
}, [ref, event, listener]);
}
// 自定义Hook处理属性设置
function useProperty(ref, prop, value) {
useEffect(() => {
if (ref.current) {
ref.current[prop] = value;
}
}, [ref, prop, value]);
}
// 包装CalendarMonth组件
export const CalendarMonth = forwardRef(function CalendarMonth(props, forwardedRef) {
return <calendar-month offset={props.offset} ref={forwardedRef} />;
});
// 包装CalendarRange组件
export const CalendarRange = forwardRef(function CalendarRange(
{ onChange, showOutsideDays, firstDayOfWeek, isDateDisallowed, ...props },
forwardedRef
) {
const ref = useRef();
useImperativeHandle(forwardedRef, () => ref.current, []);
useListener(ref, "change", onChange);
useProperty(ref, "isDateDisallowed", isDateDisallowed);
return (
<calendar-range
ref={ref}
show-outside-days={showOutsideDays || undefined}
first-day-of-week={firstDayOfWeek}
{...props}
/>
);
});
React中使用示例
import { useState } from "react";
import { CalendarRange, CalendarMonth } from "./Cally";
function App() {
const [value, setValue] = useState("");
const onChange = (event) => setValue(event.target.value);
return (
<>
<p>当前选择: {value}</p>
<CalendarRange value={value} onChange={onChange}>
<CalendarMonth />
<CalendarMonth offset={1} />
</CalendarRange>
</>
)
}
TypeScript支持
为React组件添加TypeScript类型定义:
declare global {
namespace JSX {
interface IntrinsicElements {
"calendar-month": unknown;
"calendar-range": unknown;
"calendar-date": unknown;
}
}
}
// 使用Cally提供的类型定义
import type {
CalendarRangeProps,
CalendarMonthProps,
CalendarDateProps,
} from "cally";
// 为包装组件添加类型
export const CalendarRange = forwardRef(function CalendarRange(
{
onChange,
showOutsideDays,
firstDayOfWeek,
isDateDisallowed,
...props
}: PropsWithChildren<CalendarRangeProps>,
forwardedRef
) {
// 实现代码...
});
与Vue集成
Vue的天然支持
Vue对Web Components有很好的支持,只需简单配置即可使用:
- 在main.js中配置忽略自定义元素解析
- 直接使用自定义元素标签
基本使用
<script setup>
import 'cally';
</script>
<template>
<calendar-range :months="2">
<calendar-month />
<calendar-month :offset="1" />
</calendar-range>
</template>
使用v-model绑定
Cally组件通过change事件通知值变化,可以使用v-model.lazy进行双向绑定:
<script setup>
import 'cally';
const selected = ref("")
</script>
<template>
<p>选择范围: {{ selected }}</p>
<calendar-range :months="2" v-model.lazy="selected">
<calendar-month />
<calendar-month :offset="1" />
</calendar-range>
</template>
TypeScript支持
创建globals.d.ts文件扩展Vue类型:
import type { DefineComponent } from "vue";
import type {
CalendarRangeProps,
CalendarMonthProps,
CalendarDateProps,
} from "cally";
interface CallyComponents {
"calendar-range": DefineComponent<CalendarRangeProps>;
"calendar-date": DefineComponent<CalendarDateProps>;
"calendar-month": DefineComponent<CalendarMonthProps>;
}
declare module "vue" {
interface GlobalComponents extends CallyComponents {}
}
与Svelte集成
Svelte的简单集成
Svelte对Web Components有原生支持,无需额外配置:
<script lang="ts">
import "cally";
</script>
<calendar-range months={2}>
<calendar-month></calendar-month>
<calendar-month offset={1}></calendar-month>
</calendar-range>
TypeScript支持
创建globals.d.ts文件扩展Svelte类型:
import type {
CalendarRangeProps,
CalendarMonthProps,
CalendarDateProps,
} from "cally";
type MapEvents<T> = {
[K in keyof T as K extends `on${infer E}` ? `on:${Lowercase<E}` : K]: T[K];
};
declare module "svelte/elements" {
interface SvelteHTMLElements {
"calendar-range": MapEvents<CalendarRangeProps>;
"calendar-month": MapEvents<CalendarMonthProps>;
"calendar-date": MapEvents<CalendarDateProps>;
}
}
最佳实践总结
- React项目:建议创建包装组件以获得更好的开发体验
- Vue项目:直接使用,推荐v-model.lazy进行数据绑定
- Svelte项目:开箱即用,无需额外配置
- TypeScript支持:通过声明合并扩展框架类型定义
- 性能考虑:Web Components具有原生性能优势,适合复杂日历场景
通过以上方法,开发者可以在不同框架中充分利用Cally日历组件的功能,同时保持类型安全和良好的开发体验。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考