news 2026/9/19 17:53:04

将 HTML 静态站点迁移到 Gatsby:从零到生产部署的完整实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
将 HTML 静态站点迁移到 Gatsby:从零到生产部署的完整实战指南

将 HTML 静态站点迁移到 Gatsby:从零到生产部署的完整实战指南

【免费下载链接】gatsbyReact-based framework with performance, scalability, and security built in.项目地址: https://gitcode.com/gh_mirrors/ga/gatsby

导读

本文基于 Gatsby 官方文档《Porting an HTML Site to Gatsby》,以一家虚构的园艺公司 "Taylor's Tidy Trees" 的纯 HTML/CSS 网站为案例,完整演示如何将一个传统静态站点迁移到 Gatsby。你将掌握:搭建 Gatsby 项目、管理静态资源与全局 CSS、用 Gatsby Head API 与<Link>组件重构页面结构、用<Layout>组件消除重复代码、以及构建产物public目录的部署与 path prefix 配置。文章同时结合当前仓库源码,揭示路由生成、链接预取、Head 渲染等底层实现原理。

适用说明:本指南聚焦于静态网站的迁移场景。本指南同样适用于只迁移网站的一部分(与现有静态文件共存于同一域名),请特别关注文末 托管新网站 一节。更全面的功能讲解请参考仓库中的 Gatsby 教程。

准备工作:了解示例站点与假设前提

示例网站结构

本指南要迁移的示例静态网站结构如下:

website-domain ├── assets │ ├── favicon.ico │ ├── person.png │ ├── normalize.css │ └── style.css ├── index.html ├── 404.html ├── about.html ├── contact.html ├── services │ ├── index.html │ ├── growing.html │ ├── cleaning.html │ └── shrinking.html └── who ├── index.html ├── ellla-arborist.html ├── marin-leafer.html └── sam-surgeon.html

假设前提

  • 样式方案:示例站点使用全局 CSS 文件(style.cssnormalize.css)。Sass 架构(参考 Sass 文档)或 CSS-in-JS(参考 CSS-in-JS 文档)也能支持,但本指南不展开。
  • 无客户端 JS:示例站点没有 jQuery 等客户端 JavaScript。如果你的站点包含客户端脚本,迁移时若未妥善处理或移除,可能在构建期与 Gatsby 冲突,可参考 Debugging HTML Builds。

开发环境

Gatsby 通过编译与构建流程生成生产环境站点,也提供针对本地开发优化的工具链。CLI 与开发环境的安装请参考 Gatsby 教程 Part Zero。

创建 Gatsby 项目

用 hello-world starter 初始化

使用 Gatsby 与 npm CLI 创建新项目:

gatsby new gatsby-site https://github.com/gatsbyjs/gatsby-starter-hello-world

gatsby new会基于 starter 模板生成一个包含基础 Gatsby 应用的gatsby-site文件夹(当前仓库中的 hello-world starter 即为该模板:src/pages/index.jsstatic/favicon.icogatsby-config.jspackage.json等)。然后进入目录:

cd gatsby-site

理解src/pages的文件系统路由

src文件夹存放站点的大部分前端代码。在 Gatsby 构建流程中,src/pages下的每个组件文件都会自动生成一个 HTML 页面。路由规则(见 Creating Routes 文档):

  • src/pages/contact.jsyoursite.com/contact
  • src/pages/information/contact.jsyoursite.com/information/contact
  • 例外:名为index.js的文件匹配其所在目录的根,src/pages/index.js对应yoursite.com/src/pages/information/index.js对应yoursite.com/information

新应用中唯一的页面来自src/pages/index.js

import React from "react" export default function Home() { return <div>Hello world!</div> }

启动开发服务器

gatsby develop

在浏览器访问http://localhost:8000即可看到页面。

迁移首页:Porting index.html

示例站点的index.html如下:

<html lang="en"> <head> <title>Taylor's Tidy Trees</title> <link href="/assets/favicon.ico" rel="shortcut icon" type="image/x-icon" /> <link rel="stylesheet" type="text/css" href="/assets/normalize.css" /> <link rel="stylesheet" type="text/css" href="/assets/style.css" /> </head> <body> <header> <a href="/" class="brand-color logo-text">Taylor's Tidy Trees</a> <nav> <ul> <li><a href="/about.html">About</a></li> <li><a href="/services/index.html">Services</a></li> <li><a href="/who/index.html">Who We Are</a></li> <li><a href="/contact.html">Contact</a></li> </ul> </nav> </header> <main> <h1>Welcome To Taylor's Tidy Trees</h1> <h2>We care about trees of all kinds!</h2> </main> </body> </html>

下面把它逐块转换成 Gatsby 代码。

静态资源:static文件夹与 CSS 导入

Gatsby 项目中的static文件夹里已有一个favicon.ico(即 Gatsby 默认 favicon)。static中的所有文件都会被原样服务在应用根路径下,把你的 favicon 放进去,浏览器刷新即可看到效果。示例中的另一张图片person.png也应移入static供后续使用。

Gatsby 的打包系统(基于 webpack/Parcel)消除了手动编写 CSS<link>标签的需要:在组件文件中用import引入 CSS 即可,Gatsby 会高效地将 CSS 随站点一起交付。

创建src/styles文件夹,把项目所有 CSS 文件移入,并在首页组件中为每个 CSS 文件添加 import:

import React from "react" import "../styles/normalize.css" // highlight-line import "../styles/style.css" // highlight-line export default function Home() { return <div>Hello world!</div> }

关于资源处理的更多进阶方式(直接 import 图片文件、使用gatsby-plugin-image等),见 Importing Assets Into Files 与 Using gatsby-plugin-image。

Head 元素:Gatsby Head API

你可能注意到src/pages/index.js的组件中没有<html><head><body>——Gatsby 会为每个页面生成默认 HTML 结构,并把组件的输出放入<body>更多<head>子元素与 HTML 属性通过 Gatsby Head API 添加(见 Gatsby Head API 文档,该 API 自gatsby@4.19.0起支持)。

接下来把<header><main>元素搬进来。复制原<head>标签的内容,但不再需要 CSS 的<link>标签。Gatsby 组件在代码结构上必须有单一根节点,一个常用技巧是用 React Fragment 包裹:

import React from "react" import "../styles/normalize.css" import "../styles/style.css" export default function Home() { return ( <> <header></header> <main> <div>Hello world!</div> </main> </> ) } // highlight-start export const Head = () => ( <> <title>Taylor's Tidy Trees</title> <link href="/favicon.ico" rel="shortcut icon" type="image/x-icon" /> </> ) // highlight-end

这里混合了组件与原生 HTML 元素,正是JSX模板语言——Gatsby 会把它编译成浏览器可解析渲染的 HTML。从源码层面看,Head 中允许的合法标签为linkmetastyletitlebasescriptnoscript;当多个同id元素重复时,最后一个生效(可用于按页面覆盖全局 favicon 等场景)。浏览器端渲染由 head-export-handler-for-browser.js 负责:它会将 Head 中渲染的<html>/<body>属性提取出来应用到真实 DOM,并避免重复渲染造成元素重复。

站点导航:<Link>组件

Gatsby 自带一组核心构建块,<Link>就是其中之一。它在文件顶部从gatsby包导入,替代内部链接的<a>标签,用toprop 代替href属性。站点构建时,<Link>会产出原生 HTML 锚点,并附带性能优化:在用户触发链接前预取页面内容。

<header>内容复制过来,把<a>改成<Link>

import React from "react" import { Link } from "gatsby" // highlight-line import "../styles/normalize.css" import "../styles/style.css" export default function Home() { return ( <> <header> {/* highlight-start */} <Link to="/" className="brand-color logo-text"> Taylor's Tidy Trees </Link> <nav> <ul> <li> <Link to="/about">About</Link> </li> <li> <Link to="/services">Services</Link> </li> <li> <Link to="/who">Who We Are</Link> </li> <li> <Link to="/contact">Contact</Link> </li> </ul> </nav> {/* highlight-end */} </header> <main> <div>Hello world!</div> </main> </> ) } export const Head = () => ( <> <title>Taylor's Tidy Trees</title> <link href="/favicon.ico" rel="shortcut icon" type="image/x-icon" /> </> )

预取的底层实现可见于 gatsby-link 源码:GatsbyLink组件利用浏览器IntersectionObserver监听链接是否进入视口,一旦进入视口便调用___loader.enqueue(newPathName)预取目标页面资源;当链接离开视口时中止未完成的预取。此外它还支持activeClassNameactiveStylepartiallyActive等属性,用于高亮当前路由。

页面内容:classclassName

<main>的内容从index.html复制到index.js后基本不用改动,但有一点必须处理:React 中class是 JavaScript 保留字,HTML 的class属性必须重命名为className

再次在浏览器打开http://localhost:8000,你应该看到一个视觉完整的首页!下一步移植更多页面让链接真正可用。

HTML from JavaScript:Gatsby 的组件哲学

Gatsby 页面的代码是 JavaScript 与 HTML 的混合体。每个页面通常是一个 JavaScript 函数,描述给定一组输入("props")对应的 HTML 块。Gatsby 在构建过程中执行每个页面的 JavaScript 函数,产出静态 HTML 文件。

组件外观取决于内容与行为的动态程度:

  • 非常静态的页面:几乎全是 HTML 标记,外面包一层供 Gatsby 装配的 JavaScript;
  • 带 props(输入)与逻辑的组件:在 JSX 中穿插更多 JavaScript,例如用 GraphQL 数据层 或 从文件导入数据 生成动态标记(如相关链接列表)。

本指南偏向 HTML 一侧以适配静态站点。但尽早用 React 组织客户端 JavaScript 会打开很多未来可能性:Gatsby 既能从组件产出静态页面,也能在页面加载后下发动态客户端 JavaScript,让站点 水合(hydration) 成完整的 React 应用。

迁移子页面:Porting pages

移植一个子索引页

Taylor's Tidy Trees 的who区有 4 个团队成员页面,其索引页如下:

<html lang="en"> <head> <title>Taylor's Tidy Trees - Who We Are</title> <link href="/assets/favicon.ico" rel="shortcut icon" type="image/x-icon" /> <link rel="stylesheet" type="text/css" href="/assets/normalize.css" /> <link rel="stylesheet" type="text/css" href="/assets/style.css" /> </head> <body> <header> <a href="/" class="brand-color logo-text">Taylor's Tidy Trees</a> <nav> <ul> <li><a href="/about.html">About</a></li> <li><a href="/services/index.html">Services</a></li> <li><a href="/index.html">Who We Are</a></li> <li><a href="/contact.html">Contact</a></li> </ul> </nav> </header> <main> <h1>Who We Are</h1> <h2>These are our staff:</h2> <ul> <li><a href="/who/ella-arborist.html">Ella (Arborist)</a></li> <li><a href="/who/sam-surgeon.html">Sam (Tree Surgeon)</a></li> <li><a href="/who/marin-leafer.html">Marin (Leafer)</a></li> </ul> </main> </body> </html>

对比/index.html/who/index.html可以看出:除了页面标题和<main>内容,几乎所有部分都重复。这正是提取Layout 组件的时机。

Layout 组件:消除重复结构

Gatsby 中构建与样式化页面的基础构建块是<Layout>组件(详见 Layout Components 文档)。它包裹页面内容,提供所有页面共有的结构。

src下、与src/pages平级创建components文件夹,并在其中新建layout.js

import React from "react" export default function Layout({ children }) { return ( <> <header></header> <main>{children}</main> </> ) }

src/pages/index.js一样,该文件导出一个返回 JSX 结构的函数,但这次函数接收参数:组件函数的第一个参数永远是 props 对象,组件的内容(children)可从 props 上取到;JSX 中的花括号包裹一个 JavaScript 表达式,其结果会被放置到该位置,这里即children变量的值。

/index.html/who/index.html的公共部分从src/index.js复制进<Layout>

import React from "react" import { Link } from "gatsby" import "../styles/normalize.css" import "../styles/style.css" export default function Layout({ children }) { return ( <> <header> <Link to="/" className="brand-color logo-text"> Taylor's Tidy Trees </Link> <nav> <ul> <li> <Link to="/about.html">About</Link> </li> <li> <Link to="/services/index.html">Services</Link> </li> <li> <Link to="/who/index.html">Who We Are</Link> </li> <li> <Link to="/contact.html">Contact</Link> </li> </ul> </nav> </header> <main>{children}</main> </> ) }

注意:Layout 中的链接暂时保留了.html后缀,待各页面迁移到 Gatsby 的文件系统路由后,应改为不含扩展名的路径(如/about/who/index.html/who)。

现在用<Layout>创建src/who/index.js

import React from "react" import { Link } from "gatsby" import Layout from "../components/layout" export default function Who() { return ( <Layout> <h1>Who We Are</h1> <h2>These are our staff:</h2> <ul> <li> <Link to="/who/ella-arborist">Ella (Arborist)</Link> </li> <li> <Link to="/who/sam-surgeon">Sam (Tree Surgeon)</Link> </li> <li> <Link to="/who/marin-leafer">Marin (Leafer)</Link> </li> </ul> </Layout> ) } export const Head = () => ( <> <title>Taylor's Tidy Trees - Who We Are</title> <link href="/favicon.ico" rel="shortcut icon" type="image/x-icon" /> </> )

此时index.js中的 "Who We Are" 链接应该可以工作了!再把index.js页也改用<Layout>

import React from "react" import Layout from "../components/layout" // highlight-line export default function Home() { return ( {/* highlight-start */} <Layout> <h1>Welcome To Taylor's Tidy Trees</h1> <h2>We care about trees of all kinds!</h2> </Layout> {/* highlight-end */} ); }

检查 "Who We Are" 链接是否仍正常工作;若不正常,确认内容是否如上所示被<Layout>正确包裹。

从源码角度看 Layout 的价值在于:Gatsby默认不会自动为页面包裹布局,而是遵循 React 的组合模型,由页面显式 import 并包裹。这样既能实现多层级布局(全局 header/footer + 某些页面的侧边栏),也能在布局与页面间传递数据。若担心页面切换时导航组件被卸载重挂(破坏 CSS 过渡或组件状态),可使用wrapPageElement(gatsby-browser 与 gatsby-ssr API)或 gatsby-plugin-layout。

移植其他页面

复用<Layout>组件、复制<main>内容即可快速移植页面,别忘了classclassName。Ella 的页面:

import React from "react" import { Link } from "gatsby" import Layout from "../components/layout" export default function EllaArborist() { return ( <Layout> <h1>Ella - Arborist</h1> <h2>Ella is an excellent Arborist. We guarantee it.</h2> <div className="bio-card"> <img alt="Comically crude stick person sketch" src="/person.png" /> <p>Ella</p> </div> </Layout> ); } export const Head = () => ( <> <title>Taylor's Tidy Trees - Who We Are - Ella</title> <link href="/favicon.ico" rel="shortcut icon" type="image/x-icon" /> </> )

Marin 与 Sam 的页面结构类似,你甚至可以为 Bio Card 再抽一个组件。等services与根级页面都迁移完成后,完整的 Gatsby 项目结构如下:

gatsby-site ├── static │ ├── favicon.ico │ └── person.png ├── src │ ├── styles │ │ ├── normalize.css │ │ └── style.css │ ├── components │ │ └── Layout.js │ └── pages │ ├── index.js │ ├── about.js │ ├── contact.js │ ├── 404.js │ ├── who │ │ ├── index.js │ │ ├── ellla-arborist.js │ │ ├── marin-leafer.js │ │ └── sam-surgeon.js │ └── services │ ├── index.js │ ├── growing.js │ ├── cleaning.js │ └── shrinking.js ├── node_modules ├── .gitignore ├── .prettierignore ├── .prettierrc ├── gatsby-config.js ├── LICENSE ├── package-lock.json ├── package.json └── README.md

构建与部署:Building & Deploying

Gatsby 构建步骤

所有页面迁移完成后,站点已能完整镜像原 HTML 站点。停掉开发服务器,运行生产构建:

gatsby build

构建完成后,编译产物位于public目录。

托管新网站

构建产物public目录的内容可直接托管在域名根路径(/)下,部署方式与你现有 HTML 站点类似。迁移网站的一部分?同样可行——Gatsby 的构建输出可以与现有文件混合部署。

如果 Gatsby 站点托管在非根路径(如example.com/blog),需要告知 Gatsby,使构建产物中的页面与资源链接带上路径前缀,在gatsby-config.js中配置即可(详见 Path Prefix 文档):

module.exports = { pathPrefix: `/blog`, }

这也是"迁移站点一部分"场景的核心配置:只有路径前缀正确,<Link>生成的链接与静态资源 URL 才会指向正确位置。

新网站文件结构

Gatsby 构建输出中 HTML 与非 JavaScript 资源文件的结构如下(注意每个路由都被生成为xxx/index.html,配合服务器即可得到无扩展名的美观 URL):

website-domain ├── favicon.ico ├── person.png ├── index.html ├── 404 │ └── index.html ├── about │ └── index.html ├── contact │ └── index.html ├── services │ ├── index.html │ ├── growing │ │ └── index.html │ ├── cleaning │ │ └── index.html │ ├── shrinking │ │ └── index.html └── who ├── index.html ├── ellla-arborist │ └── index.html ├── marin-leafer │ └── index.html └── sam-surgeon └── index.html

下一步:Next steps

  • 图片与资源:Gatsby 支持在页面与组件文件中直接 import 图片等资源(见 Importing Assets Into Files),并可用 gatsby-plugin-image 获得更深度的优化(响应式、占位图、懒加载等)。资源交给 Gatsby 管理后,就能用插件优化其处理与交付。
  • 组件架构:Building With Components 解释了 Gatsby 采用 React 组件架构的原因及组件如何融入应用。
  • 内容与数据:Sourcing Content and Data 是下一步的理想选择——比如用 GraphQL 从gatsby-config.jssiteMetadata中读取站点标题(gatsby-config的配置方式见 Gatsby Config API),并用 Markdown 编写内容。
  • 路由进阶:当页面数量变多或需要从数据批量生成页面时,可使用 File System Route API(如src/pages/products/{Product.name}.js)或gatsby-node.js中的 createPages API。

【免费下载链接】gatsbyReact-based framework with performance, scalability, and security built in.项目地址: https://gitcode.com/gh_mirrors/ga/gatsby

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 17:52:09

易灵思Ti60F100外挂HyperRAM实战:Native接口时序调试与性能优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 17:50:31

Demosaic算法全解析:从双线性插值到深度学习与FPGA实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 17:49:10

Hugo 站点数据访问指南:Site.Data 方法详解与 hugo.Data 迁移实践

开发工具前端CLI 【免费下载链接】hugo The world’s fastest framework for building websites. 项目地址&#xff1a; https://gitcode.com/gh_mirrors/hu/hugo 点击查看 免费下载 Site.Data 返回由 data 目录&#xff08;或挂载到 data 目录的任何目录&#xff09;中全部文…

作者头像 李华
网站建设 2026/9/19 17:49:01

Google AI Pro 订阅深度评测:$19.99 的 Gemini 与 Google 生态整合值不值

1. 这个订阅到底在卖什么&#xff1a;先看清 Google AI Pro 的真实定位$19.99 一个月&#xff0c;这个价格放在当下的 AI 订阅市场里&#xff0c;属于“中档偏上”的位置。比免费版强不少&#xff0c;但又没到企业级方案那种动辄按席位、按调用量计费的程度。很多人第一次看到 …

作者头像 李华