UzumakiItachi
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

UzumakiItachi

起风了,唯有努力生存。
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • WebPack

  • 浏览器相关

  • 工程化相关

  • 工作中遇到的问题以及解决方案

  • Git

  • Vite

  • 一些小工具

  • 算法

  • 服务器

  • HTTP

  • 一些新东西

    • 简单易上手的SSG,SSR框架之Astro
      • 前言
      • 应用架构概念介绍
      • 渲染模式
        • 三种渲染模式对比
      • Astro
      • 创建第一个Astro项目
        • ssr能力
  • 技术
  • 一些新东西
hanhanbuku
2025-12-18
目录

简单易上手的SSG,SSR框架之Astro

# 前言

在大前端的发展历程中,为适应各使用场景逐渐衍生出了许许多多的工程化方案,框架,渲染模式等一些列概念。SPA,MPA,SSG,SSR,CSR等等一系列的专业名词数不胜数,本文将给大家带来一个全新的框架,让大家在技术选型的时候多一个选择。

# 应用架构概念介绍

首先从应用架构模式来说,我们需要了解的是SPA,MPA。

下面我将罗列一下他们各自的概念以及优缺点

  • SPA:Single Page Application,单页面应用
    • 这是一种只加载一个html页面,通过js动态更新页面内容的web应用架构,用户在操作时不会触发整页的刷新,而是通过ajax或者fetch api于服务器通信,异步加载数据并更新dom
    • 优点:
      • 交互体验好
      • 减少服务器压力
      • 前端路由优化
      • 开发体验好
    • 缺点:
      • 天然seo不友好
      • 首屏加载慢
      • js文件体积大
      • 开发体验虽然好,但复杂度较高
  • MPA:Multi-Page Application,多页面应用
    • 这是传统web架构,每次用户请求新页面时,浏览器都会向服务器请求一个新的html页面,服务端处理逻辑并返回完整的html代码,简单来说就是会有多个html文件,每个文件之间是独立的
    • 优点:
      • 首屏加载快
      • seo良好
      • 开发门槛低,会三大件就行
    • 缺点:
      • 页面切换慢,每次都要请求新的html
      • 服务器开销大
      • 前后端耦合度高
      • 开发效率低

# 渲染模式

上面介绍完了应用架构,接下来就带大家了解了解前端的三大渲染模式

  • CSR(客户端渲染)
    • 顾名思义,就是把渲染的工作放在客户端去做,SPA架构下大多数都是采用的CSR的渲染模式,服务器返回一个只有基础容器的html,和js文件,由这个js文件在客户端完成渲染
    • 优点:
      • 页面切换流畅
      • 前后端完全分离
      • 服务器压力小
      • 开发效率高
    • 缺点
      • 首屏加载慢
      • seo不友好
      • 内容依赖js
    • 技术栈代表:
      • Vue/React/Angular等
      • WebPack/Vite等
  • SSR(服务端渲染)
    • 浏览器请求url,nodejs在服务器上执行前端代码,调用后端api,然后渲染出完整的html字符串返回给客户端,对于客户端来说拿到的就是一个完整的html页面
    • 优点:
      • 首屏加载快
      • 支持seo
      • 低端设备友好
    • 缺点:
      • 服务器压力大,需要实时渲染
      • 开发复杂度高
      • 需要服务器上长期运行一个nodejs服务
    • 技术栈代表:
      • Next/Nuxt
  • SSG(静态站点生成)
    • 在构建时就生成所有完整的html,最终部署到服务器上的时纯静态的html/css/js
    • 优点:
      • 极致的性能(回归原始)
      • 顶级安全性(无服务器运行时)
      • 完美的seo支持
      • 服务器成本低,可以托管在任意地方
    • 缺点:
      • 大部分都是静态内容,不适合高度动态的内容
      • 实时数据依然需要客户端补充
    • 技术栈代表:
      • Nuxt/VitePress/Vite-SSG

# 三种渲染模式对比

特性 CSR SSR SSG
渲染地点 客户端 🌐 服务器端 ☁️ 构建时 ⚙️
首屏速度 慢 ⏳ 快 🚀 极快 ⚡
SEO 友好 一般 ⚠️ 优秀 ✅ 优秀 ✅
开发复杂度 简单 😊 复杂 😅 中等 😐
服务器负载 低 😌 高 😓 极低 🥳
实时数据 优秀 ✅ 优秀 ✅ 差 ❌
适用场景 后台系统/应用类 内容网站/电商 博客/文档/宣传页
技术成本 低 💰 高 💰💰 极低 🆓
安全系数 中 🛡️ 中 🛡️ 高 🛡️🛡️🛡️

有了这些基础知识,接下来就可以隆重介绍我们的主人公Astro了。首先引用一段官网 (opens new window)的介绍

# Astro

提示

Astro 是最适合构建像博客、营销网站、电子商务网站这样的以内容驱动的网站的 Web 框架。Astro 以开创了一种新的前端架构而闻名,与其他框架相比它减少了 JavaScript 的开销和复杂性。如果你需要一个加载速度快、具有良好 SEO 的网站,那么 Astro 就是你的选择。

这是一个集多功能于一体的web框架,他的亮点如下

  • 群岛:一种基于组件的针对内容驱动的网站进行优化的 Web 架构。
  • UI 无关:支持 React、Preact、Svelte、Vue、Solid、HTMX、Web 组件等等。
  • 服务器优先:将沉重的渲染移出访问者的设备。
  • 默认无 JS:让客户端更少的执行 JS ,以提升网站速度。
  • 内容集合:为你的 Markdown 内容,提供组织、校验并保证 TypeScript 类型安全。
  • 可定制:Partytown、MDX 和数百个集成可供选择。

通过上面这些简介我们就可以了解到这是一个实现SSR或者SSG的极佳框架,他允许你使用任意的ui框架,并且同时支持SSR和SSG混用,你的项目里可以A文件使用vue,B文件使用react,甚至一个文件里可以同时存在多个框架的组件。接下来就带大家从0到1体验一下这个框架!

# 创建第一个Astro项目

这里官方推荐使用Node版本v18.20.8 或 v20.3.0、v22.0.0 或更高版本。(v19 和 v21均不支持。)

# 使用 pnpm 创建一个新项目
pnpm create astro@latest

# 使用 npm 创建一个新项目
npm create astro@latest

# 使用 yarn 创建一个新项目
yarn create astro
1
2
3
4
5
6
7
8

这里我用的是pnpm,安装好依赖之后,你就可以通过dev运行项目啦

# 安装依赖
pnpm i
# 运行项目
pnpm dev
1
2
3
4

astro采用约定式路由,也就是说src下必须有一个pages目录,这个目录下的文件就是你的网页路径

如上图所示,最终会构建出index.html和ssrPage.html这两个文件。

astro默认就是以SSG模式去构建。

进入.astro后缀的文件内,里面的结构如下

---
        astro的相关变量以及组件的导入需要写在这里,如创建变量,导入文件,导入组件等
---
        
这里是写模板的地方,可以写原生html标签,可以渲染vue组件,react组件等等      

1
2
3
4
5
6

可以看到整体的语法上来说 还是相对很自由的。

想要用对应的ui框架需要安装对应的继承,这里用vue和react来做个示例

pnpm add @astrojs/vue

pnpm add @astrojs/react

1
2
3
4

如果在启动Astro时看到 Cannot find package 'react'(或类似)的警告,你需要安装react 和 react-dom:

pnpm add react react-dom @types/react @types/react-dom
1

vue同理

pnpm add vue
1

接下来需要在astro.config.*文件中添加这两个继承

// @ts-check
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
import react from '@astrojs/react';

// https://astro.build/config
export default defineConfig({
    integrations:[
        vue(),
        react()
    ]
});

1
2
3
4
5
6
7
8
9
10
11
12
13

下面我们来创建一下这两个组件

// testReact.jsx
const App = ()=>{
  return (
          <>
            <div>我是React组件</div>
          </>
  )
}
export default App
1
2
3
4
5
6
7
8
9
// testVue.vue

<template>
  <div>
    测试一下vue组件
  </div>
  <div>
    Astor组件传入的props-name:{{options.name}}
  </div>
</template>
<script>
  export default {
    props:{
      options:{
        type:Object,
        default:()=>({})
      }
    },
    data(){
      return{
        name:'123123'
      }
    },
    methods:{

    }
  }
</script>
<style scoped>

</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

接下来我们在index.astro中引入这两个组件:

可以在页面上看到

可以看到 页面上已经渲染出了这两个组件,并且还可以接受astro组件的传值。

像大多数约定式路由一样,astro也支持动态约定路由

// src/pages/dogs/[dog].astro
---
export function getStaticPaths() {
  return [
    { params: { dog: "clifford" }},
    { params: { dog: "rover" }},
    { params: { dog: "spot" }},
  ];
}
const { dog } = Astro.params;
---

<div>Good dog, {dog}!</div>
1
2
3
4
5
6
7
8
9
10
11
12
13

这将生成三个页面: /dogs/clifford、/dogs/rover 和 /dogs/spot ,每个页面显示相应的狗名。

还有更复杂得嵌套路由可以自行前往官网查看

这里需要提一嘴,默认情况下astro会构建出文件夹/index.html得结构,如果想去除这一层级直接打包出html文件可以再配置文件中这样写

// @ts-check

import mdx from '@astrojs/mdx';
import sitemap from '@astrojs/sitemap';
import { defineConfig } from 'astro/config';

// https://astro.build/config
export default defineConfig({
  // site: 'https://example.com',
  // 使用相对路径以便直接在浏览器打开HTML文件
  integrations: [mdx(), sitemap()],
  build: {
    // 将页面构建为单独的HTML文件,而不是放在文件夹中
    format: 'file',
  },
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# ssr能力

astro支持ssr和ssg混用,即:可以某些页面使用ssr渲染,某些页面使用ssg渲染。

框架默认采用SSG,我们可以通过配置来修改

import { defineConfig } from 'astro/config';

export default defineConfig({
  output: 'static'// static|server
})
1
2
3
4
5

如果想修改某个页面得渲染模式可以在page文件中这样写

---
export const prerender = false  

---
1
2
3
4

prerender

  • 默认值: 静态模式下为 true(默认);配置 output: 'server' 后为 false

像Next,Nuxt这类SSR框架都提供了服务端钩子客户端钩子等一系列工具函数,在astro中如何在服务器上调用接口呢?

这里需要引入一个新的名词---服务器端点

所谓的服务器端点我们可以理解为他是一个我们自己写的后端服务,我们在调用,或者访问它的时候 它可以像正常的接口一样返回Response实例。它的定义方法也和写接口很类似:

在pages目录下的任意以.js或.ts结尾的文件都可以被作为服务器端点,而服务器端点需要暴露出以http请求方式为名的函数,例如下面这个例子

//src/pages/api/hello.ts

import type { APIRoute } from 'astro'

export const GET: APIRoute = () => {
  return new Response(
          JSON.stringify({
            greeting: 'Hello',
          }),
  )
}
// export const POST
// export const DELETE
// export const PUT
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在这个文件中我们暴露了一个GET方法,文件名为hello.ts。客户端就可以通过发送get请求或者直接调用这个GET方法访问到这个端点。

在这个端点中 我们可以自定义响应体,比如返回一个标准的response实例,亦或者直接返回json数据

这里提到了response实例就顺带介绍一下

Response实例 总共有以下属性

  • body: ReadableStream, // 响应体流
  • bodyUsed: false, // 是否已读取
  • headers: Headers {}, // 响应头
  • ok: true, // 请求是否成功
  • redirected: false, // 是否被重定向
  • status: 200, // 状态码
  • statusText: "OK", // 状态文本
  • type: "basic", // 响应类型
  • url: "https://..." // 请求 URL

而这里我们需要特别关注的是body,和前端打交道最多的就是响应体啦,我们需要在这里面读取各种各样的数据。但是这个对象是没法直接用的,还需要调用一些特定的方法去读取数据

比如:

  • .json() → 解析为 JSON
  • .text() → 读取为文本
  • .blob() → 读取为 Blob
  • .arrayBuffer() → 读取为 ArrayBuffer
  • .formData() → 读取为 FormData

在日常开发过程中一些请求库都帮我做了这件事,所以可以直接在.then里拿到响应体。但是在astro里我们就需要自行处理一下了

下面我们调用一下这个端点

// src/pages/index.astro
---
import { GET } from './api/hello.ts'

let response = await GET(Astro)
const data = await response.json()
---

<h1>{data.greeting} world!</h1>
1
2
3
4
5
6
7
8
9

接下来演示一下真实的调用服务端接口,这里我起了一个nodejs服务并且写了一个接口

const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
const {doc} = require('./data')
app.listen(port, () => {
  console.log(`express服务已启动,端口号: ${port}`);
  console.log('\x1b[36m%s\x1b[0m', `👉 点击这里访问: http://localhost:${port}/getReqText`); // 青色显示
  console.log('\x1b[32m%s\x1b[0m', `🌐 或者访问: http://127.0.0.1:${port}/getReqText`); // 绿色显示
})
app.get('/',(req,res)=>{
  res.send('<!DOCTYPE html>\n' +
          '<html lang="zh-CN">\n' +
          '<head>\n' +
          '    <meta charset="UTF-8">\n' +
          '    <meta name="viewport" content="width=device-width, initial-scale=1.0">\n' +
          '    <title>无脚小鸟</title></head>' +
          '<body>' +
          '<div>express服务,3000端口</div>' +
          '</body>' +
          '</html>')
})

app.post('/getUserInfo',(req,res)=>{
  console.log(req.headers,'请求头')
  res.status(200).json({
    status_code:1,
    data:{
      name:'小明',
      age:'18',
      sex:1
    }
  })
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

接下来在astro项目中写一个服务器端点调用一下这个接口

// 返回一个GET函数,这个函数里可以调用服务端接口
export async function GET({params,request}){
    console.log('GET', params,request)

    const res = await fetch('http://localhost:3000/getUserInfo',{
        method: 'POST',
        headers: {
            'Authorization': `Bearer ab87qwd78cq87we`,
        },
    })

    const data = await res.json()

    console.log(data,'接口响应')

    return new Response(JSON.stringify(data), {
        status: 200,
        headers: {
            'Content-Type': 'application/json'
        }
    });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

调用端点

---
import Welcome from '../components/Welcome.astro';
import Layout from '../layouts/Layout.astro';
import {GET as commonApi} from './Api/common'

const res = await commonApi({
	params:{
		name:'Welcome',
		Astro
	},
	request:{
		method:'POST',
	}
})
const users = await res.json();
console.log(users,123123123)

---

<Layout>
	{
		users.data.map((user,index)=>{
			return(
				<div style="padding-bottom: 50px" key={index}>
					<div>用户名:{user.name}</div>
					<div>年龄:{user.age}</div>
				</div>
			)
		})
	}
	<Welcome />
</Layout>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

运行项目后就可以看到一系列打印了

服务端成功接受到请求,并打印了一下请求头,可以看到调用时传入的Authorization字段也是成功拿到了

服务器端点这边也是成功拿到响应,并打印出来了(由于这一步动作是在服务器执行的 所以浏览器看不到,等调用完毕后浏览器拿到的已经是完整的html了)

浏览器上可以看到 响应的html里已经有我们服务器端点返回的数据了,这就完成了一次ssr渲染

在任何astro页面上,都可以去调用这个端点暴露的get方法。Astro还提供了丰富的全局对象,如Request,Responses,等来处理请求需要的一些配置,我们可以在这里获取/配置请求头响应头等等一些列操作。

由于涉及到的内容太多,大家可以自行前往官网查阅,整体来说 这是一个非常灵活,易上手的SSG/SSR框架。很值得一式!

编辑 (opens new window)
上次更新: 2025/12/20, 11:40:13
图解http

← 图解http

最近更新
01
React如何获取最新状态
12-03
02
react路由切换过度效果
11-08
03
小程序实现全局状态管理
07-09
更多文章>
Theme by Vdoing | Copyright © 2023-2025 UzumakiItachi | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式