You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

咨询React组件多视口宽度预览方案:触发CSS媒体查询

作为组件库开发者,我刚好折腾过类似的需求,分享几个实用的方案,帮你让开发者直观看到组件在不同视口下的表现:

1. 内置可调节的响应式预览面板

这是最直接的方案——在组件文档页里加一个带控制的容器,把目标组件放进去,通过拖拽滑块或点击预设按钮来改变容器宽度,实时触发组件的CSS媒体查询。

优点是轻量、无需额外依赖,用户可以自由调整任意宽度。示例代码大概是这样:

import { useState } from 'react';

const ComponentPreview = ({ children }) => {
  const [viewportWidth, setViewportWidth] = useState(1200);

  return (
    <div className="preview-wrapper">
      {/* 控制区 */}
      <div style={{ marginBottom: '1.5rem', display: 'flex', gap: '1rem', alignItems: 'center' }}>
        <input
          type="range"
          min="320"
          max="1920"
          value={viewportWidth}
          onChange={(e) => setViewportWidth(Number(e.target.value))}
          style={{ flex: 1 }}
        />
        <span>{viewportWidth}px</span>
        <button onClick={() => setViewportWidth(375)}>手机</button>
        <button onClick={() => setViewportWidth(768)}>平板</button>
        <button onClick={() => setViewportWidth(1200)}>桌面</button>
      </div>
      {/* 预览容器 */}
      <div
        style={{
          width: `${viewportWidth}px`,
          margin: '0 auto',
          border: '1px solid #e5e7eb',
          padding: '1rem',
          borderRadius: '8px',
          overflowX: 'auto'
        }}
      >
        {children}
      </div>
    </div>
  );
};

// 使用时直接包裹你的组件
<ComponentPreview>
  <YourButtonComponent variant="primary" />
</ComponentPreview>
2. 用iframe做隔离式预览

如果你的组件样式容易和文档页的全局样式冲突(比如全局重置、CSS变量污染),iframe是更好的选择——它能模拟完全独立的浏览器视口,避免样式干扰。

你可以动态控制iframe的宽度,甚至在iframe内部的页面里监听外部的尺寸变化,确保媒体查询准确触发:

import { useState, useRef } from 'react';

const IframePreview = ({ componentUrl }) => {
  const [viewportWidth, setViewportWidth] = useState(375);
  const iframeRef = useRef(null);

  const syncViewport = () => {
    if (!iframeRef.current) return;
    const iframeWindow = iframeRef.current.contentWindow;
    iframeRef.current.style.width = `${viewportWidth}px`;
    // 强制设置iframe内部的视口宽度,确保媒体查询触发
    iframeWindow.document.body.style.width = `${viewportWidth}px`;
    iframeWindow.dispatchEvent(new Event('resize'));
  };

  return (
    <div>
      <div style={{ marginBottom: '1rem' }}>
        <input
          type="range"
          min="320"
          max="1920"
          value={viewportWidth}
          onChange={(e) => {
            setViewportWidth(Number(e.target.value));
            syncViewport();
          }}
        />
      </div>
      <iframe
        ref={iframeRef}
        src={componentUrl}
        style={{
          width: `${viewportWidth}px`,
          height: '500px',
          border: '1px solid #e5e7eb',
          borderRadius: '8px'
        }}
        onLoad={syncViewport}
      />
    </div>
  );
};
3. 集成Storybook的Viewport插件

如果你的组件库已经用Storybook做文档,那直接用它自带的@storybook/addon-viewport插件就完事了——不用自己写任何UI,插件已经帮你做好了设备切换面板,还能自定义预设的视口尺寸。

步骤很简单:

  1. 安装插件:npm install @storybook/addon-viewport --save-dev
  2. 在.storybook/main.js里注册:
module.exports = {
  addons: ['@storybook/addon-viewport']
};
  1. 在组件的故事文件里,还可以设置默认视口或者自定义设备:
export default {
  title: 'Components/Button',
  component: Button,
  parameters: {
    viewport: {
      defaultViewport: 'mobile1',
      viewports: {
        customMobile: {
          name: '我的自定义手机',
          styles: {
            width: '390px',
            height: '844px'
          }
        }
      }
    }
  }
};
4. 多视口并排预览

如果想让开发者一眼对比多个尺寸的效果,可以做一个多列布局,同时展示手机、平板、桌面三种尺寸下的组件:

const MultiViewportPreview = ({ children }) => {
  const presetViewports = [
    { width: 375, label: '手机' },
    { width: 768, label: '平板' },
    { width: 1200, label: '桌面' }
  ];

  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: '1.5rem' }}>
      {presetViewports.map((vp) => (
        <div key={vp.width}>
          <h3 style={{ fontSize: '1rem', marginBottom: '0.5rem' }}>{vp.label} ({vp.width}px)</h3>
          <div
            style={{
              width: `${vp.width}px`,
              border: '1px solid #e5e7eb',
              padding: '1rem',
              borderRadius: '8px',
              overflowX: 'auto'
            }}
          >
            {children}
          </div>
        </div>
      ))}
    </div>
  );
};
方案选择建议
  • 快速实现选内置预览面板;
  • 样式隔离需求选iframe预览;
  • 已经用Storybook的话直接用Viewport插件;
  • 需要对比多尺寸效果选多视口并排预览。

内容的提问来源于stack exchange,提问作者ljenkins

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:23:47