Skip to content

将 jaimebuilds.com 升级到 Astro 7、Tailwind 4 与 TypeScript 6

把作品集发布在 Astro 栈的最新版本上。迁移过程中坏了五件事。页面体积从约 100 KB 降到 13.6 KB。下面是 changelog 与修复方案。

7分钟阅读
#astro#tailwind#迁移#性能#构建#vercel

这个站点现在运行在 Astro 7.0.3Tailwind 4.3.1TypeScript 6.0.3 上。重设计分支今天早些时候上线,主页的 live proof 条最终稳定在 13.6 KB 的总体积和 313 ms 的桌面浏览器加载时间。

升级用了一个下午。有意思的不是把 package.json 里的版本号往上调。而是版本跳跃带来的五个小型破坏性改动。我先读的 release notes 里一个都没提。它们都以构建错误或者静默坏掉的布局形式冒出来。

把 changelog 分享出来,因为肯定有人正要做同样的升级。

起点

迁移之前这个站点跑在:

  • Astro 5.16.6,output: 'server',部署在 Vercel
  • Tailwind 3.4,带 @astrojs/tailwind 集成和 tailwind.config.ts
  • React 19.2.3 islands
  • TinaCMS 用于内容编辑
  • lenis 平滑滚动、motion 动画、深浅色切换、astro-iconastro-i18next

同时刚在另一个分支上完成了一次彻底的重设计(瑞士编辑风格布局、暖奶油色调、Shopify Plus 导向的文案)。所以升级和重设计一起上线。

1. LegacyContentConfigError

版本升级后第一个构建失败:

[LegacyContentConfigError] Found legacy content config file in
"src/content/config.ts". Please move this file to "src/content.config.ts"
and ensure each collection has a loader defined.

Astro 6 把 content collections 移出了 src/content/config.ts,并要求每个集合显式声明 loader。新位置是 src/content.config.ts(路径里没有 content/ 目录)。新的形态用从 astro/loaders 导入的 loader 替换了 type: 'content'

之前:

import { defineCollection, z } from 'astro:content';

const blog = defineCollection({
  type: 'content',
  schema: z.object({ title: z.string(), date: z.coerce.date() }),
});

export const collections = { blog };

之后:

import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
  schema: z.object({ title: z.string(), date: z.coerce.date() }),
});

export const collections = { blog };

站点有九个集合(blog、work、lab 各三种语言),所以是九行 loader: glob(...) 加一次文件重命名。

2. post.render() 消失了

[slug].astro 里,旧 API 是:

const { post } = Astro.props;
const { Content } = await post.render();

post.render() 在 Astro 6 被移除。替代方案是顶层的 render 函数:

import { getCollection, render } from 'astro:content';

const { post } = Astro.props;
const { Content } = await render(post);

行为相同,导入不同。九个 [slug].astro 都要改。

3. post.slug 现在叫 post.id

同一次迁移。当你用新的 loader API 读取条目时,原来的 post.slug 现在叫 post.id。形态变了,因为 loaders 可以产出不来自文件的条目,那种条目谈 “slug” 没意义。

原来写:

<a href={`/blog/${post.slug}/`}>{post.data.title}</a>

现在写:

<a href={`/blog/${post.id}/`}>{post.data.title}</a>

对于 glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' })id 就是文件相对 base 的路径去掉扩展名,和旧的 slug 一模一样。URL 不变,仅仅是一次重命名。

4. @astrojs/tailwind 不支持 Astro 6 与 7

@astrojs/tailwind 集成的 peer dependency 卡在 Astro 5。Astro 6+ 官方的路径是 Tailwind 4 的 Vite 插件:@tailwindcss/vite

具体写法:

// astro.config.mjs(之前)
import tailwind from '@astrojs/tailwind';
export default defineConfig({
  integrations: [tailwind({ applyBaseStyles: false })],
});

// astro.config.mjs(之后)
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
  vite: { plugins: [tailwindcss()] },
});

Tailwind 4 也放弃了 JavaScript 配置文件,改为 CSS 优先的配置方式。tailwind.config.ts 不再存在。设计 token 现在写在主 CSS 文件的 @theme 块里,Tailwind 会自动生成对应的 utility:

@import "tailwindcss";

@plugin "@tailwindcss/forms";
@plugin "@tailwindcss/typography";

@theme {
  --color-v2-bg: #f4f1ea;
  --color-v2-ink: #161310;
  --color-v2-accent: #8a2417;

  --font-v2-sans: "Geist", -apple-system, sans-serif;
  --font-v2-serif: "Newsreader", Georgia, serif;
}

--color-v2-bg: #f4f1ea 自动生成 bg-v2-bgtext-v2-bgborder-v2-bg 以及对应的变体。--font-v2-sans 生成 font-v2-sans。插件用同一文件中的 @plugin 指令引入。整个 JS 配置文件都没了。

5. 差点把坏掉的站点发上线那个

构建变绿、部署上线之后,我打开预览 URL,首页只有一半样式。颜色和字体生效了。栅格没生效。容器没有 max-width。导航菜单不可见。CTA 按钮上下堆叠而不是并排。

罪魁是 astro-compress。它在 Vite 之后跑第二轮 CSS 压缩。Tailwind 3 时它无害。Tailwind 4 时它似乎会丢弃或破坏它没识别为被引用的 utility,特别是那些 arbitrary value(max-w-[1280px]lg:grid-cols-[1.4fr_1fr])和响应式变体(hidden md:flex)。font-mediumborder 这种简单 utility 活下来了。复杂的没活。

修复:

compress({
  CSS: false,
  HTML: true,
  Image: true,
  JavaScript: true,
  SVG: true,
}),

Vite 本身已经正确地压缩了 CSS bundle。第二轮是多余的。把 CSS: false 一关,布局就回来了。

值得标注:构建是绿的。部署是上线状态。任何日志里都没有错误信号。唯一的提示是预览里的一张截图。

顺带掉出来的东西

升级正好是一次大扫除的时机。我移除了九个重设计已经不再需要的依赖:

  • lenismotion:没有平滑滚动,没有动画库
  • astro-icon@iconify-json/mdi:只用内联 SVG
  • astro-i18nexti18next:重设计用硬编码语言路径
  • @astrojs/tailwind:由 @tailwindcss/vite 替代
  • 深色模式 CSS 与主题切换:重设计只走亮色

还删掉了十七个新布局已经让其过时的 V1 组件。

结果:主页 live proof 条测量到 13.6 KB 的总传输字节(HTML + CSS + JS),桌面浏览器的 domInteractive313 ms。之前的站点在 80 到 120 KB 之间。差不多 7 到 9 倍的减重,而且重设计视觉上比被它替代的版本更厚重。

一行五点版的迁移

  1. src/content/config.ts 重命名为 src/content.config.ts,并为每个集合加上 loader: glob({...})
  2. await post.render() 替换为 await render(post),从 'astro:content' 导入 render
  3. post.slug 改成 post.id,凡是集合条目都要改(URL 的 params: { slug } 不变)。
  4. 卸掉 @astrojs/tailwind,装 @tailwindcss/vite,删 tailwind.config.ts,把 token 搬到 CSS 的 @theme 块。
  5. astro-compressCSS 设为 false。信任 Vite。

如果你正在做同样的升级,这个顺序是行得通的。整条分支是公开的,想看 diff 可以去:github.com/jaimesolis/jaimebuilds


剩下的笔记(以及偶尔关于 Shopify Plus 主题构建的吐槽)我都发在 X 上。如果这种调试笔记对你有用,欢迎关注:@jaimesolis。如果你正在 Astro 升级或 Hydrogen 迁移中卡住,想多一双眼睛看一下,联系表单点两下就到。