# React Helmet 完整指南

# 1. 简介

React Helmet 是一个用于管理 HTML 文档头的 React 组件。它允许您以声明式的方式修改文档的 <head> 部分,包括 title、meta、link 等标签。

# 2. 安装

# 使用 npm
npm install react-helmet-async

# 使用 yarn
yarn add react-helmet-async

# 使用 pnpm
pnpm add react-helmet-async

# 3. 基础配置

# 3.1 在应用根组件中设置

// src/App.tsx
import { HelmetProvider } from 'react-helmet-async';

const App = () => {
  return (
    <HelmetProvider>
      {/* 你的应用内容 */}
    </HelmetProvider>
  );
};

# 3.2 基本用法

import { Helmet } from 'react-helmet-async';

const HomePage = () => {
  return (
    <>
      <Helmet>
        <title>首页标题</title>
        <meta name="description" content="页面描述" />
      </Helmet>
      {/* 页面内容 */}
    </>
  );
};

# 4. 常用功能

# 4.1 设置多种 Meta 标签

<Helmet>
  {/* 基本 Meta 标签 */}
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="theme-color" content="#000000" />
  <meta name="description" content="网站描述" />
  <meta name="keywords" content="关键词1,关键词2" />

  {/* Open Graph Meta 标签 */}
  <meta property="og:title" content="页面标题" />
  <meta property="og:description" content="页面描述" />
  <meta property="og:image" content="图片URL" />
  <meta property="og:url" content="页面URL" />

  {/* Twitter Card Meta 标签 */}
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content="页面标题" />
  <meta name="twitter:description" content="页面描述" />
  <meta name="twitter:image" content="图片URL" />
</Helmet>

# 4.2 添加样式表和脚本

<Helmet>
  {/* 外部样式表 */}
  <link rel="stylesheet" href="styles.css" />
  
  {/* 内联样式 */}
  <style>{`
    body {
      background-color: #f0f0f0;
    }
  `}</style>

  {/* 外部脚本 */}
  <script src="external-script.js" type="text/javascript" />
  
  {/* 内联脚本 */}
  <script>{`
    console.log('Hello from Helmet!');
  `}</script>
</Helmet>

# 4.3 条件渲染

<Helmet>
  <title>{isLoggedIn ? '用户首页' : '请登录'}</title>
  {needsAnalytics && (
    <script src="analytics.js" type="text/javascript" />
  )}
</Helmet>

# 5. 高级用法

# 5.1 创建可重用的 SEO 组件

// src/components/SEO/index.tsx
import { Helmet } from 'react-helmet-async';

interface SEOProps {
  title: string;
  description?: string;
  keywords?: string;
  image?: string;
  url?: string;
  type?: string;
  children?: React.ReactNode;
}

export const SEO: React.FC<SEOProps> = ({
  title,
  description,
  keywords,
  image,
  url,
  type = 'website',
  children
}) => {
  const siteName = '网站名称';
  const defaultDescription = '默认描述';
  const defaultImage = '/default-og-image.jpg';
  
  return (
    <Helmet>
      {/* 基本标签 */}
      <title>{`${title} | ${siteName}`}</title>
      <meta name="description" content={description || defaultDescription} />
      {keywords && <meta name="keywords" content={keywords} />}

      {/* Open Graph */}
      <meta property="og:site_name" content={siteName} />
      <meta property="og:title" content={title} />
      <meta property="og:description" content={description || defaultDescription} />
      <meta property="og:image" content={image || defaultImage} />
      {url && <meta property="og:url" content={url} />}
      <meta property="og:type" content={type} />

      {/* Twitter Card */}
      <meta name="twitter:card" content="summary_large_image" />
      <meta name="twitter:title" content={title} />
      <meta name="twitter:description" content={description || defaultDescription} />
      <meta name="twitter:image" content={image || defaultImage} />

      {children}
    </Helmet>
  );
};

# 5.2 在页面中使用 SEO 组件

// src/pages/ProductPage.tsx
const ProductPage = ({ product }) => {
  return (
    <>
      <SEO
        title={product.name}
        description={product.description}
        keywords={product.tags.join(',')}
        image={product.image}
        url={`https://yoursite.com/products/${product.id}`}
        type="product"
      />
      {/* 页面内容 */}
    </>
  );
};

# 5.3 动态更新标题

const DynamicPage = () => {
  const [count, setCount] = useState(0);

  return (
    <>
      <Helmet>
        <title>{`(${count}) 新消息`}</title>
        {count > 0 && (
          <link rel="icon" href="/favicon-notification.ico" />
        )}
      </Helmet>
      <button onClick={() => setCount(count + 1)}>
        增加消息
      </button>
    </>
  );
};

# 6. 最佳实践

# 6.1 默认配置

// src/config/helmet.ts
export const defaultSEO = {
  title: '网站名称',
  description: '网站默认描述',
  keywords: '默认关键词',
  image: '/default-og-image.jpg',
  url: 'https://yoursite.com',
  twitterHandle: '@yourhandle',
};

// src/components/DefaultHelmet.tsx
import { defaultSEO } from '../config/helmet';

export const DefaultHelmet = () => (
  <Helmet>
    <html lang="zh-CN" />
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    
    <title>{defaultSEO.title}</title>
    <meta name="description" content={defaultSEO.description} />
    <meta name="keywords" content={defaultSEO.keywords} />
    
    <meta property="og:site_name" content={defaultSEO.title} />
    <meta property="og:title" content={defaultSEO.title} />
    <meta property="og:description" content={defaultSEO.description} />
    <meta property="og:image" content={defaultSEO.image} />
    <meta property="og:url" content={defaultSEO.url} />
    
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:site" content={defaultSEO.twitterHandle} />
    <link rel="icon" href="/favicon.ico" />
  </Helmet>
);

# 6.2 异步数据加载

const AsyncPage = () => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchData().then(result => {
      setData(result);
      setLoading(false);
    });
  }, []);

  return (
    <>
      <Helmet>
        <title>{loading ? '加载中...' : data.title}</title>
        {!loading && (
          <>
            <meta name="description" content={data.description} />
            <meta property="og:title" content={data.title} />
          </>
        )}
      </Helmet>
      {/* 页面内容 */}
    </>
  );
};

# 7. 注意事项

  1. 服务端渲染

    • 使用 react-helmet-async 而不是 react-helmet
    • 确保在服务端正确提取和注入头部信息
  2. 性能考虑

    • 避免频繁更新 Helmet 内容
    • 对于动态内容,考虑使用防抖或节流
  3. SEO 最佳实践

    • 确保每个页面都有唯一的标题和描述
    • 使用合适的 Open Graph 和 Twitter Card 标签
    • 保持标题长度在 60 个字符以内
    • 描述内容保持在 155-160 个字符以内

# 8. 常见问题解决

  1. 标题不更新

    • 检查是否正确嵌套了 HelmetProvider
    • 确保组件正确导入了 Helmet
  2. 重复标签

    • Helmet 会自动处理重复标签,保留最后定义的值
    • 检查是否在多个地方定义了相同的标签
  3. 动态内容

    • 使用条件渲染确保内容已加载
    • 考虑添加加载状态的处理