Cally日历组件与主流前端框架集成指南

Cally日历组件与主流前端框架集成指南

前言

Cally是一款基于Web Components技术构建的日历组件库,具有框架无关的特性。本文将详细介绍如何将Cally与React、Vue和Svelte等主流前端框架集成,并提供TypeScript支持的最佳实践。

Web Components基础

Web Components是一组浏览器原生支持的API,允许开发者创建可重用的自定义元素。Cally正是基于这一技术构建,因此具备以下优势:

  1. 框架无关性:可以在任何前端框架中使用
  2. 原生浏览器支持:不需要额外的运行时库
  3. 封装性:样式和行为都被封装在组件内部

与React集成

React的特殊处理

虽然React官方支持Web Components,但在实际使用中存在一些限制:

  1. 属性传递:React会将所有属性转换为字符串
  2. 事件监听:需要手动添加事件监听器
  3. 方法调用:需要通过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有很好的支持,只需简单配置即可使用:

  1. 在main.js中配置忽略自定义元素解析
  2. 直接使用自定义元素标签

基本使用

<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>;
  }
}

最佳实践总结

  1. React项目:建议创建包装组件以获得更好的开发体验
  2. Vue项目:直接使用,推荐v-model.lazy进行数据绑定
  3. Svelte项目:开箱即用,无需额外配置
  4. TypeScript支持:通过声明合并扩展框架类型定义
  5. 性能考虑:Web Components具有原生性能优势,适合复杂日历场景

通过以上方法,开发者可以在不同框架中充分利用Cally日历组件的功能,同时保持类型安全和良好的开发体验。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凌骊洵Perfect

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值