第33章 React生态系统与优秀库

Chapter-33 - React 生态精选库

33.1 动画:Framer Motion

33.1.1 motion 组件的基本用法

为什么需要动画库? 想象没有动画库的时候,你想让一个 div 淡入+移动+缩放,要写一堆 setInterval + CSS transition,还要手动管理动画状态。代码又臭又长,动画间的协调更是噩梦。

Framer Motion 是 React 中最流行的动画库,它的核心理念是:把动画当成组件的"属性"来声明——你要做的只是描述"从哪到哪",框架帮你搞定中间的一切。

1
npm install framer-motion
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import { motion } from 'framer-motion'

function AnimatedBox() {
  return (
    <motion.div
      initial={{ opacity: 0, scale: 0.5 }}  // 初始状态
      animate={{ opacity: 1, scale: 1 }}     // 最终状态
      transition={{ duration: 0.5 }}          // 过渡配置
      whileHover={{ scale: 1.1 }}           // 悬停时动画
    >
      Hello, Animation!
    </motion.div>
  )
}

33.1.2 过渡动画:animate / initial / exit

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import { motion, AnimatePresence } from 'framer-motion'

function Modal({ isOpen }) {
  return (
    <AnimatePresence>
      {isOpen && (
        <motion.div
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
        >
          <ModalContent />
        </motion.div>
      )}
    </AnimatePresence>
  )
}

33.1.3 手势动画:drag / hover / tap

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function DraggableBox() {
  return (
    <motion.div
      drag                          // 可拖拽
      dragConstraints={{ left: -100, right: 100, top: -100, bottom: 100 }}
      whileHover={{ scale: 1.1 }}
      whileTap={{ scale: 0.9 }}
    >
      拖我    </motion.div>
  )
}

33.1.4 页面过渡动画

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import { motion } from 'framer-motion'

const pageVariants = {
  initial: { opacity: 0, x: '-100%' },
  in: { opacity: 1, x: 0 },
  out: { opacity: 0, x: '100%' }
}

function Page({ children }) {
  return (
    <motion.div
      initial="initial"
      animate="in"
      exit="out"
      variants={pageVariants}
      transition={{ duration: 0.3 }}
    >
      {children}
    </motion.div>
  )
}

33.2 拖拽:dnd-kit

33.2.1 DndContext 的使用

为什么需要拖拽库? 拖拽听起来简单(鼠标按下→移动→松开),但背后涉及:碰撞检测、坐标计算、DOM 位置更新、性能优化(拖拽时 60fps 不能卡)……自己写能写,但很容易写出一堆 bug。

dnd-kit 是目前 React 生态中最现代的拖拽库,专为性能优化而生——它用 CSS transform 而不是 top/left 定位,天然避开了重排(reflow)的性能陷阱。

1
npm install @dnd-kit/core @dnd-kit/sortable
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import { DndContext, closestCenter } from '@dnd-kit/core'

function App() {
  return (
    <DndContext collisionDetection={closestCenter}>
      <SortableContext items={items}>
        {items.map(id => <SortableItem key={id} id={id} />)}
      </SortableContext>
    </DndContext>
  )
}

33.2.2 sortable 列表的实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'

function SortableItem({ id }) {
  const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id })

  const style = {
    transform: CSS.Transform.toString(transform),
    transition
  }

  return (
    <div ref={setNodeRef} style={style} {...attributes} {...listeners}>
      {id}
    </div>
  )
}

33.3 图表:Recharts / ECharts

33.3.1 Recharts 的基础图表

为什么需要图表库? Canvas / SVG 画图表并不难,但坐标轴、刻度、图例、Tooltip、响应式……这些"配套零件"要写半天。图表库的价值就是把这些繁琐的东西封装好,让你"告诉它数据,它帮你画图"。

Recharts 是 React 中最流行的图表库,API 设计直观,用法接近 React 组件思维——每个图表都是 React 组件,数据是 props,灵活组合。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import { LineChart, Line, BarChart, Bar, PieChart, Pie, Cell } from 'recharts'

function ChartDemo() {
  const data = [
    { name: '1月', value: 400 },
    { name: '2月', value: 300 },
    { name: '3月', value: 200 }
  ]

  return (
    <>
      {/* 折线图 */}
      <LineChart data={data}>
        <Line type="monotone" dataKey="value" stroke="#8884d8" />
      </LineChart>

      {/* 柱状图 */}
      <BarChart data={data}>
        <Bar dataKey="value" fill="#82ca9d" />
      </BarChart>

      {/* 饼图 */}
      <PieChart>
        <Pie
          data={data}
          dataKey="value"
          nameKey="name"
          cx="50%"
          cy="50%"
          outerRadius={80}
          label
        >
          {data.map((_, index) => (
            <Cell key={`cell-${index}`} fill={['#8884d8', '#82ca9d', '#ffc658'][index % 3]} />
          ))}
        </Pie>
      </PieChart>
    </>
  )
}

Recharts 约定:data 数组中每项的 name 字段自动作为 X 轴或标签,其他字段绑定 dataKey 即可。上手成本极低,画个折线图通常不超过 10 行代码。

33.3.2 ECharts for React:echarts-for-react

如果 Recharts 满足不了你的图表复杂度和视觉要求,可以试试 ECharts(百度开源,图表类型非常全面)。配合 echarts-for-react 封装成 React 组件:

1
npm install echarts echarts-for-react
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import ReactECharts from 'echarts-for-react'

function EChartsDemo() {
  const option = {
    title: { text: '月度销售额' },
    tooltip: {},
    xAxis: { data: ['1月', '2月', '3月'] },
    yAxis: {},
    series: [{
      name: '销售额',
      type: 'bar',
      data: [400, 300, 200]
    }]
  }

  return <ReactECharts option={option} style={{ height: 400 }} />
}

ECharts 的优点是图表类型极其丰富(地图、热力图、关系图等),自定义能力强;缺点是配置项较多、学习曲线比 Recharts 陡一些。


33.4 国际化:react-i18next

33.4.1 i18n 的基本配置

1
npm install react-i18next i18next
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'

i18n.use(initReactI18next).init({
  resources: {
    en: { translation: { greeting: 'Hello' } },
    zh: { translation: { greeting: '你好' } }
  },
  lng: 'zh',
  fallbackLng: 'en'
})

33.4.2 useTranslation Hook 的使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import { useTranslation } from 'react-i18next'

function Greeting() {
  const { t, i18n } = useTranslation()

  return (
    <div>
      <p>{t('greeting')}</p>
      <button onClick={() => i18n.changeLanguage('en')}>EN</button>
      <button onClick={() => i18n.changeLanguage('zh')}>中文</button>
    </div>
  )
}

本章小结

本章我们学习了 React 生态中的精选库:

  • Framer Motion:最流行的动画库,支持过渡动画、手势动画、页面过渡
  • dnd-kit:现代拖拽库,性能优秀
  • Recharts:图表库,支持折线图、柱状图、饼图
  • react-i18next:国际化解决方案

这些库覆盖了动画、拖拽、图表、国际化等常见需求!下一章我们将学习 React 与 AI!🤖