Table of contents
说明
Basic use
bun create astro@latest -- --template basics
bun astro add react
bun astro add tailwind
pnpm dlx shadcn@latest init
# Upgrade to the latest version of Astro, and upgrade all integrations to their latest versions by running the following commands in your terminal:
pnpm dlx @astrojs/upgrade
pnpm exec vercel env pull .env.development.local # 拉取环境变量
# 远程连接使用Astro DB
# Build with a remote connection
astro build --remote
# Develop with a remote connection
astro dev --remote
# Pushing table schemas
pnpm astro db push --remote
pnpm astro db push --remote --force-reset
项目相关
- 项目以Astro为主,React为辅。
- 使用了workflows,每次commit之后pull一次,保持和远程一致,要不然报错。
- 虽然vercel部署的站点访问不稳定,但是netlify部署站点每月有次数限制,所以仍然以vercel为主,不过会不定期更新netlify平台网站,注意vercel平台必须先
build文件再提交。 - 目前数据库中管理员账户自己手动在数据库中添加,规避风险。
[...404].astro写法是为了解决vercel报404错误。url地址中不能出现日语,编码不同会导致找不到资源。astro.config.ts是node环境,获取环境变量需要使用process.env,而不是import.meta.env。literature使用slugifyIgnoreCase,这不会将大写字母转换成小写字母,兼容字母语言,post全部使用slugify。pages目录下的文件不要删除,url路径不带locale的时候会用到。- markdown文件中的
title为必填且唯一,因为我才用了此项作为url路径。 - markdown文件中使用
layout属性情况下的markdown文件必须在pages目录下,否则模版文件中无法获取frontmatter。 - markdown文件中使用
layout属性情况下的markdown文件在vercel平台上部署页面会出现错误,但是netlify平台上部署页面不会出现错误。目前无法解决错误,所以不再采用文件中使用layout属性来生成页面。 dev模式下的图片文件获取需要配置,当vercel的devImageService配置设置为"sharp"时,获取图片文件时会报未找到错误,需要将其修改为"astro/assets/services/sharp"。- 外部js无法控制react组件内部
dom节点的时候,可以考虑使用createPortal方法把目标dom挂载到外部dom上面,项目中的语言选择条即采用此方法实现。 fallback修复其设置了会导致重定向太多错误,页面无法显示,不使用此设置。- 使用@astrojs/db,vercel会产生找不到的错误,无法采取之前的方式解决,底层系统编码问题,目前不使用,待新版本解决后再确定。
- remark-toc的
prefix配置只给目录元素的id加上了前缀,但是没有给具体内容元素的id加上,此配置暂时不使用。 - rehype-sanitize插件会移除remark-collapse生成的目录折叠功能代码,暂不使用。
- 在中间件中获取
cookie为undefined,不使用js-cookie了。(原因其实是因为middleware是在服务器端运行,无法获取客户端cookie) - 不再使用react-astro-i18next, astro-react-i18n等第三方库,因为会产生混乱且错误的
route,导致vercel报错。 LanguageSwitcher语言切换出现问题,目前Astro没有提供比较好的传递函数到脚本中的方法,暂时使用React组件来实现语言切换功能,待版本更新后有较好的方式再使用Astro组件进行替换。(不再考虑更换了,架构改动十分巨大,时间成本太高)astro.config.js中导入的文件中不能有导入astro:content模块的内容,否则会报错。astro的actions中无法返回自定义类型,不要使用自定义错误等自定义类型,只能在里面返回字面量。astro的actions中的context无法给ctx.request.signal.onabort绑定事件,只有采用fetch的方式传入AbortController才可以手动取消请求,但是由于actions中也无法获取手动创建的AbortController的signal的状态值(非ctx.request.signal自带的),所以无法确认后续发出的数据库操作是否能被取消。此功能暂时搁置。experimental_AstroContainer(注意它是server api无法在客户端使用,可以在frontmatter中使用)创建的组件的子组件在打包时路径会变成从项目根路径开始,这导致在netlify平台部署路径变为*/opt/build/repo/src/components/*.astro?astro&type=script&index=0&lang.ts这种格式,而且返回的Content-Type为text/html导致报错,另外此路径下的文件在打包之后会清理,也导致文件无法找到从而报错。- 由于
Astro只有页面级别的组件及其子组件(非子孙组件)能从params中获取路径参数(本项目中的locale,slug等),若想在middleware中往locals中注入相关变量来达到非页面级别组件获取相关变量的能力,需注意是否依赖路径,因为组件请求也有自己的路径,比如其路径不带locale那么就无法获取。 - 当地址为
https://xx.netlify/zh-CN/500时,trailingSlash分别为always、never时,context.url.pathname分别为https://xx.netlify/zh-CN/和https://xx.netlify/zh-CN/500。其他情况视情况而定。
代码相关
- 引入环境变量需要区分是cleint还是server,如下:
import { PUBLIC_API_URL } from "astro:env/client";
import { PORT, API_SECRET } from "astro:env/server";
css module的:gloabl能穿透子组件和节点,但是不能嵌套使用,如下:
/* example.module.css */
.content {
background-color: blue;
}
/* 推荐使用这种写法,这种写法每行可以有多个选择器,可以写复合选择器,但是不能嵌套 */
:global(.gg),
:global(.hh),
:global(.gg.hh),
:global(.gg .hh) { /* 正确 */
background-color: green;
}
:global(.gg) { /* 正确 */
background-color: green;
:gloabl(.hh){ /* 错误 */
background-color: green;
}
}
/* 下面这种写法每行只能有一个选择器,可以写复合选择器,并且也不能嵌套 */
:gloabl .kk .jj { /* 正确 */
background-color: yellow;
}
:global .kk, :global .jj { /* 错误 */
background-color: yellow;
}
:global .kk { /* 正确 */
background-color: yellow;
.jj { /* 错误 */
background-color: aqua;
}
}
使用穿透的时候,直接写类名,不需要带前缀,如下:
// example.tsx
import styles from './example.module.css';
import ChildComponent from './ChildComponent';
export default function() {
return (
<div className={styles.root}>
<p className={styles.content}>1234</p> {/* scoped */}
<p className="gg">1234</p> {/* global */}
<ChildComponent />
</div>
);
}
// ChildComponent.tsx
export default function() {
return (
<div className="hh"></div>
);
}
- 加载图片示例:
import { Image } from "astro:assets";
import shape from "@/assets/images/shape.jpg";
<!-- 加载public目录中的图片 -->
<img src={`/${frontmatter.ogImage}`} class="w-200" title="OG Image" />
<Image
src={`/${frontmatter.ogImage}`}
alt="shape-from-public"
width={400}
height={200}
/>
<!-- 加载assets目录中被处理后的图片 -->
<img src={shape.src} class="w-200" title="Shape Image" />
<br />
<Image
src={shape}
alt="shape"
widths={[240, 540, 720, shape.width]}
sizes={`(max-width: 360px) 240px, (max-width: 720px) 540px, (max-width: 1600px) 720px, ${shape.width}px`}
/>
<!-- 注意如下引用在开发模式是正常的,但是如果设置了prerender=true的话会导致打包失败,而且也不会被打包,暂且不用 -->
src={import(`../../assets/${aboutMarkdown.frontmatter.avatar}`)}
// 自适应加载图片
// 图片显示尺寸固定为32px,在 (DPR=x)window.devicePixelRatio = x,的情况下会加载x * 32px的图片
<img
src="/images/site_icon.webp"
alt={t(SITE.title)}
title={t(SITE.title)}
class="size-8"
srcset="/images/site_icon-32.webp 32w, /images/site_icon-64.webp 64w"
sizes="32px"
/>
// 假定DPR=1,在视窗小于等于360px时加载xx-300.webp,在视窗大于360px小于等于720px时加载xx-600.webp,类推...,当视窗大于1600px时加载1600px的图片
<img
src="/images/site_icon.webp"
alt={t(SITE.title)}
title={t(SITE.title)}
class="size-8"
srcset="/images/xx-300.webp 300w, /images/xx-600.webp 600w, /images/xx-1200.webp 1200w, ..."
sizes="
(max-width: 360px) 300px,
(max-width: 720px) 600px,
(max-width: 1600px) 1200px,
1600px
"
/>
// fetch的GET和POST请求会有差异,这里更改为POST请求会缺少Cookie等信息,必须手动加上,否则apiRoute的函数无法得到相关信息
const res = await fetch(url, {
method: "POST",
headers: {
Cookie: Astro.request.headers.get("cookie") || "",
"Content-Type": "application/json",
},
body: JSON.stringify({
postSlug,
data: data,
}),
});