Astro v6 Node Adapter后端的环境变量设置(v5 到 v6 的一个 breaking change)

这其实是Astro v6的一个改动,Changed: import.meta.env values are always inlined

In Astro 5.13, the experimental.staticImportMetaEnv flag was introduced to update the behavior when accessing import.meta.env directly to align with Vite’s handling of environment variables and ensures that import.meta.env values are always inlined.

In Astro 5.x, non-public environment variables were replaced by a reference to process.env. Additionally, Astro could also convert the value type of your environment variables used through import.meta.env, which could prevent access to some values such as the strings “true” (which was converted to a boolean value), and “1” (which was converted to a number).

Astro 6 removes this experimental flag and makes this the new default behavior in Astro: import.meta.env values are always inlined and never coerced.

所以下面这篇文章讨论的前提,都是Astro v6。

Astro官方文档里有关于环境变量的章节,Using environment variables,里面有个例子:

1
2
3
4
5
// When import.meta.env.SSR === true
const data = await db(import.meta.env.DB_PASSWORD);

// When import.meta.env.SSR === false
const data = fetch(`${import.meta.env.PUBLIC_POKEAPI}/pokemon/squirtle`);

它主要使用Vite的环境变量,Env Variables and Modes,但这里其实就有个坑点,那就是这种方式的值会在build的时候被替换成实际的值,而不是在运行时动态获取,这意味着:

  • 在build的时候就需要提供这个环境变量,不然无法替换;
  • 在运行时无法改变这个变量,因为他已经被写入了构造物里。

假设你在代码里写了:

1
const db = drizzle(import.meta.env.DATABASE_URL);

那么在build时提供了系统环境变量或者.env的时候,且值是 mysql://user:password@localhost:3306/db,它最终的dist会变成(也就是直接inline替换了):

1
const db = drizzle("mysql://user:password@localhost:3306/db");

Read More

谷歌GenAI Java SDK tts示例 (gemini-3.1-flash-tts-preview)

谷歌官方文档是: https://ai.google.dev/gemini-api/docs/speech-generation

但是很尴尬,他没有java sdk的示例,而且java sdk的example里也没有对应的例子,https://github.com/googleapis/java-genai/tree/main/examples.
这两个和文档不一致examples/src/main/java/com/google/genai/examples/InteractionMultimodalResponseAudio.java, examples/src/main/java/com/google/genai/examples/InteractionMultimodalResponseAudioWithGenerateContent.java.

但其实只需要用他统一的generateContent方法就可以了.

调用方式其实和文档里的python一样,只要知道对应的类即可.

Read More

Vercel AI SDK 的工具审批(Approval)流程详解

概述

在 AI 应用中,当 AI 助手需要调用本地文件系统或其他敏感工具时,出于安全考虑,需要在执行前获得用户的明确批准。Vercel AI SDK(文中使用 v6 版本,这狗东西 API 一直变来变去的)提供了工具审批(Tool Approval)机制,允许开发者在 AI 执行工具前拦截并请求用户确认。

注意这个参数主要是为了配合stopWhen(也就是工具自动调用),也就是:

1
2
3
4
5
6
const result = streamText({
model: newapi.chat(config.model.model),
messages,
tools,
stopWhen: stepCountIs(5), // 这里 自动工具调用
});

同时也注意这东西的调用方式灵活的很,毕竟他不是应用级别的封装,不用这套自己也可以搓一个出来。

同时在 AI SDK UI 中的调用流程是完全不一样的,这里只针对 Core 的 API。(但是其实也八九不离十了)

Read More

Astro 项目集成 Better Auth 指南(使用drizzle+PG)

本文档说明如何在 Astro 项目中集成 Better Auth,使用 Drizzle 作为数据库适配器,PostgreSQL 作为数据库。

emmmm,文档是在实践完之后写的,不是边实践边写的,可能会有所遗漏,但是扫了几遍源码,应该八九不离十。

注意这个需要 server 支持,所以需要 server adapter,比如 node 的@astrojs/node

我使用的是默认 static,需要后端运行时渲染的地方用export const prerender = false;,如果你的 output 设置为了 server,那么就不需要 prerender=false。

📖 官方文档参考


步骤 1:安装依赖

首先,安装 Better Auth 及相关依赖:

1
2
3
4
5
# 安装核心依赖
pnpm add better-auth drizzle-orm pg dotenv

# 安装开发依赖
pnpm add -D drizzle-kit tsx @types/pg

依赖说明

  • better-auth: 认证核心库
  • drizzle-orm: ORM 库,用于数据库操作
  • pg: PostgreSQL 驱动
  • drizzle-kit: Drizzle 开发工具(用于数据库迁移)
  • dotenv: 环境变量加载(drizzle.config.ts 需要

Read More

字符编码详解:UTF-8、Unicode、GBK 等

编辑说明:本文初稿完成后,通过 Claude Code 进行了技术审阅和优化润色,修正了 UTF-16 编码方式的描述,补充了 Unicode 平面结构、代理对机制、MySQL utf8mb4 等深度内容,使文章在保持通俗易懂的同时更加准确严谨。


字符编码详解:UTF-8、Unicode、GBK 等

一、核心概念:Unicode vs UTF-8

用快递系统来理解

Unicode(统一字符集)

  • 相当于:全国统一的地址编号系统
  • 作用:给世界上每个文字、符号分配唯一编号
  • 例子:
    • ‘A’ → U+0041(编号 65)
    • ‘中’ → U+4E2D(编号 20013)
    • ‘😊’ → U+1F60A(编号 128522)
  • 特点:只定义编号,不管怎么存储

UTF-8(编码方式)

UTF-8 根据 Unicode 码点的大小,使用不同长度的字节:

Unicode 范围 字节数 UTF-8 格式
U+0000 ~ U+007F 1 字节 0xxxxxxx
U+0080 ~ U+07FF 2 字节 110xxxxx 10xxxxxx
U+0800 ~ U+FFFF 3 字节 1110xxxx 10xxxxxx 10xxxxxx
U+10000 ~ U+10FFFF 4 字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  • 相当于:快递打包和运输方式
  • 作用:把 Unicode 编号转换成实际的字节数据
  • 例子:
    • ‘A’ (U+0041) → 01000001(1 字节)
    • ‘中’ (U+4E2D) → 11100100 10111000 10101101(3 字节)
    • ‘😊’ (U+1F60A) → 11110000 10011111 10011000 10001010(4 字节)
  • 特点:变长编码,节省空间

Read More

深入理解 SSR/CSR 与 SPA/MPA:现代前端渲染模式详解

前言

在学习现代前端框架时,你可能会发现它们的 SSR(服务端渲染)与传统后端框架的 SSR 似乎不太一样。这篇文章将详细解释它们的本质区别,以及 SSR/CSR 和 SPA/MPA 这两个维度的概念关系。

传统后端 SSR vs 现代前端 SSR

传统后端 SSR(PHP、JSP、Rails)

工作流程:

1
2
3
浏览器 → 请求页面 → 服务器生成完整HTML → 返回HTML → 浏览器显示

每次交互都重复这个过程

特点:

  • 纯服务器渲染,返回的是完整的静态 HTML
  • 每次交互都需要刷新整个页面
  • JavaScript 只是点缀(表单验证、简单动效)
  • 模板引擎在服务器端运行(如 PHP、EJS、Jinja2)

现代前端框架 SSR(Next.js、Nuxt、Remix)

工作流程:

1
2
3
4
5
首次访问:
浏览器 → 服务器SSR → 返回HTML + JS Bundle → Hydration → 变成SPA

后续导航:
客户端路由 → 仅获取数据 → 客户端渲染 → 无刷新更新

Read More

6GB显存跑Z-Image-Turbo完全指南

前言

我的拖拉机又开动了(指显卡风扇的噪音)。

这次是阿里开源的 Z-Image-Turbo,6GB 显存也能跑,而且效果还挺好。这篇博客就是纯纯的配置教程,不整那些虚的,直接告诉你怎么让小显存的卡也能愉快生图。

准备工作

首先,你需要:

  • ComfyUI(应该已经装好了吧,注意需要更新到最新版本,显卡驱动也要最新)
  • 6-12GB 显存(我用的 2060 6GB)
  • 16 以上内存(我用的是 16GB 内存,其实已经 swap 了)
  • 足够的硬盘空间下模型(量化版本所有东西最好保证 15GB 左右的空余空间)

第一步:拿官方工作流

官方已经给你做好工作流了,直接拖进 ComfyUI 的 web 界面就能用:

👉 官方工作流https://comfyanonymous.github.io/ComfyUI_examples/z_image/

拖进去之后你会发现缺模型,别慌,继续往下看。

Read More

自行部署 Next.js Docker 的挑战:臃肿、平台绑定与隐藏的“坑”

Next.js 作为目前最受欢迎的 React 全栈框架之一,凭借其强大的功能和“开箱即用”的体验赢得了大量开发者。然而,这种便利性很大程度上依赖于其创造者 Vercel 提供的原生托管平台。但是一旦开发者选择通过 Docker 自行部署,便会发现这条路并非坦途,充满了各种不易察觉的“坑”,并且会逐渐感受到框架本身的臃肿与对特定平台的深度绑定。

这里就总结了一下我这一年实践遇到的问题,建议没有 SEO 需求还是不要碰他了。

路由与请求类型的“黑盒”

在自行部署的环境中,Next.js 的一些内部机制会变得异常棘手。以 React Server Components (RSC) 的请求为例,它与页面的请求路径一致,但会附加一个 ?_rsc=... 参数以区分(也会增加一些 header)。问题在于,这个参数(以及额外的 header)在到达我们自己的应用逻辑之前,往往已被 Next.js 框架内部“消化”。这意味着,proxy.ts(16 之前的middleware.ts),无法捕获到 _rsc 这个参数。因此,从代码层面来看,开发者无法轻易判断一个传入的请求究竟是完整的页面加载请求,还是一个用于更新部分 UI 的 RSC 请求。

这在一些重定向场景中会造成一些问题。

Read More

Next.js 16 Cache Components 完全指南

基于 Next.js 16.0.1 官方文档整理

目录

  1. 什么是 Cache Components
  2. 核心工作原理
  3. 使用 Suspense 边界
  4. 使用 use cache
  5. 启用 Cache Components
  6. 从旧版本迁移
  7. 实战示例
  8. 最佳实践
  9. 和 Next-intl 结合
  10. 常见错误与解决方案
  11. FAQ
  12. 参考资料

这边文档主要是 AI 总结+我补充实际遇到的问题,大部分是 AI 写的。

注意,文档里不包含use cache: private的内容,在写的时候本来官方文档里说依赖unstable_prefetch,但是后来一看这个内容又被移除了,不知道后续会不会再改,先不写了,以官方文档为准。API Reference > Directives > use cache: private

注意,现在的 16.0 还在不断变化中,还是等 16.1,16.2 再用吧。这东西太不稳了。

Cache components除了方便的 PPR+显式缓存,另外一个就是在框架层面(主要是 dev 和构建的时候),防止用户写出动态内容卡住整个页面加载的事,这在之前很容易写出来,网上也有很多批评的文章和视频,一看连loading.tsxSuspense都不会用。😂
现在官方强制了,也是件好事吧。

什么是 Cache Components

Cache Components 是 Next.js 16 中一种新的渲染和缓存方法,通过 Partial Prerendering (PPR) 提供细粒度的缓存控制,同时确保出色的用户体验。

核心关系

1
Cache Components = PPR + use cache
  • PPR 提供静态外壳和流式传输基础设施
  • use cache 让你在外壳中包含优化的动态输出

解决的问题

在开发动态应用时,你需要在两种方式之间权衡:

  • 完全静态页面:加载快,但无法显示个性化或实时数据
  • 完全动态页面:可显示最新数据,但每次请求都需要渲染所有内容,导致初始加载慢

Read More