前言

笔者目前业务主要围绕ant design mobile组件库来交付H5前端工作。

故此出此专栏来解读每一个组件是如何实现的。

本文基于AI来解读image组件。

文档链接:

https://mobile.ant.design/zh/components/image

源码:

https://github.com/ant-design/ant-design-mobile/blob/master/src/components/image/image.tsx

设计prompt

解读antd-mobile image组件,结构化思维输出整个组件实现流程,并且解读lazy-detector.tsx是如何实现组件懒加载的

// 附组件代码

实现步骤

1. antd-mobile Image 组件实现流程(结构化步骤+关键代码)

步骤 1:参数和默认属性合并

  • 支持图片各种参数:src、alt、尺寸、fit、懒加载、占位图、失败图、事件等
  • 合并默认属性(如 fit、placeholder、fallback、lazy、draggable)

关键代码

const defaultProps = {
  fit: 'fill',
  placeholder: (
    <div className={`${classPrefix}-tip`}>
      <ImageIcon />
    </div>
  ),
  fallback: (
    <div className={`${classPrefix}-tip`}>
      <BrokenImageIcon />
    </div>
  ),
  lazy: false,
  draggable: false,
}
const props = mergeProps(defaultProps, p)

步骤 2:声明状态和ref

  • 维护加载/失败状态 loaded/failed
  • 维护组件根节点ref和img标签ref
  • 懒加载初始化状态:initialized(lazy模式初始为false,否则为true)

关键代码

const [loaded, setLoaded] = useState(false)
const [failed, setFailed] = useState(false)
const ref = useRef<HTMLDivElement>(null)
const imgRef = useRef<HTMLImageElement>(null)
const [initialized, setInitialized] = useState(!props.lazy)

步骤 3:决定是否加载图片资源

  • 组件未初始化(未在 viewport/lazy时)则不传 src 给 img(浏览器不会自动加载图片)。
  • 只有当 initialized==true,src/srcSet 才真正传给 img 标签。

关键代码

src = initialized ? props.src : undefined
srcSet = initialized ? props.srcSet : undefined

步骤 4:图片状态/事件处理

  • 图片加载成功 setLoaded(true)
  • 加载失败 setFailed(true)
  • 加载未完成时显示 placeholder,占位图标
  • 加载失败时显示 fallback 故障图片

关键代码

onLoad={e => {
  setLoaded(true)
  props.onLoad?.(e)
}}
onError={e => {
  setFailed(true)
  props.onError?.(e)
}}

步骤 5:图片显示逻辑

  • renderInner():如果 failed 或没有资源,返回 fallback。否则渲染 <img>
  • 显示顺序:未 loaded -> 先显示占位图,loaded -> 显示图片

关键代码

function renderInner() {
  if (failed || (src === undefined && !srcSet)) {
    return <>{props.fallback}</>
  }
  // ...img code
  return (
    <>
      {!loaded && props.placeholder}
      {img}
    </>
  )
}

步骤 6:调整尺寸样式

  • 支持 width/height 用 CSS 变量和 style 生效,适配灵活布局

关键代码

if (props.width) { style['--width'] = toCSSLength(props.width); style['width'] = toCSSLength(props.width) }
if (props.height) { style['--height'] = toCSSLength(props.height); style['height'] = toCSSLength(props.height) }

步骤 7:懒加载处理逻辑

  • 如果 lazy 为 true 且 initialized 为 false,渲染一个专门的 LazyDetector
  • 只有当 LazyDetector 触发 onActive,才激活图片资源加载

关键代码

{props.lazy && !initialized && (
  <LazyDetector
    onActive={() => setInitialized(true) }
  />
)}

步骤 8:主结构渲染

  • 组件整体为 div 包裹,挂载所有自定义事件和样式
  • withNativeProps 让原生属性/变量支持自动注入

关键代码

return withNativeProps(
  props,
  <div ref={ref} className={classPrefix} style={style} onClick={props.onContainerClick}>
    {props.lazy && !initialized && <LazyDetector onActive={() => setInitialized(true)} />}
    {renderInner()}
  </div>
)

2. LazyDetector 的懒加载实现方式解读

功能:

检测当前 LazyDetector 元素是否进入视口,进入视口时自动触发图片加载

实现机制

  • 内部用 ref 挂载一个空 div
  • ahooksuseInViewport(ref) 检测此 div 是否在视口
  • 监听 inViewport 状态变化,只要进入视口则执行 props.onActive(钩子里 setInitialized(true))
  • 整个组件渲染时无需监听 scroll/resize,完全用 IntersectionObserver 封装好的钩子自动化

关键代码:

const ref = useRef<HTMLDivElement>(null)
const [inViewport] = useInViewport(ref)
useEffect(() => {
  if (inViewport) {
    props.onActive()
  }
}, [inViewport])
return <div ref={ref} />

流程概览

  1. Image 组件初始为 lazy、未初始化状态,渲染
  2. LazyDetector 渲染后,ahooks/useInViewport 自动观察该 ref 是否进入可见区域
  3. 一旦进入,调用 onActive,Image组件 setInitialized(true)
  4. initialized == true,重新渲染,传 src 给 img,触发图片加载

3. 整体简要流程串联

  1. 初始化参数与状态(合并props/loaded/failed/initialized)
  2. 渲染时根据 initialized 决定是否加载图片资源
  3. 懒加载模式下先渲染 LazyDetector
  4. 图片进入视口,自动触发 onActive,更新initialized
  5. 图片进入加载流程,根据加载状态显示 loading/图片/fallback
  6. 响应所有原生事件和自定义样式

以上就是笔者基于AI返回的解读信息稍加了一些补充和修改,结合起来看源码提效真是太多了,对于前端本身就是基于视图所完成编码,因此把组件逻辑层交给AI来解读太适合不过了。

希望对大家有所帮助,共同学习源码。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐