前端 SSG:别让你的网站加载速度慢得像蜗牛

前端 SSG:别让你的网站加载速度慢得像蜗牛

毒舌时刻

这网站加载速度慢得能让我泡杯咖啡回来还没好。

各位前端同行,咱们今天聊聊前端 SSG(静态站点生成)。别告诉我你还在使用纯客户端渲染,那感觉就像在没有预加载的情况下开车——能开,但起步慢得要命。

为什么你需要 SSG

最近看到一个项目,每次加载都要重新获取数据,用户体验差。我就想问:你是在做网站还是在做实时应用?

反面教材

// 反面教材:纯客户端渲染 // App.jsx import React, { useState, useEffect } from 'react'; function App() { const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { async function fetchPosts() { setLoading(true); try { const response = await fetch('https://api.example.com/posts'); const data = await response.json(); setPosts(data); } catch (error) { console.error('Error fetching posts:', error); } finally { setLoading(false); } } fetchPosts(); }, []); return ( <div> <h1>我的博客</h1> {loading ? <div>加载中...</div> : ( <div> {posts.map(post => ( <div key={post.id}> <h2>{post.title}</h2> <p>{post.content}</p> </div> ))} </div> )} </div> ); } export default App; 

毒舌点评:这代码,就像在每次启动时都要重新启动发动机,慢得要命。

正确姿势

1. Next.js SSG

// 正确姿势:Next.js SSG // 1. 安装依赖 // npx create-next-app@latest // 2. 页面组件 // pages/index.js import React from 'react'; export async function getStaticProps() { // 构建时获取数据 const res = await fetch('https://api.example.com/posts'); const posts = await res.json(); return { props: { posts }, revalidate: 60 // 每 60 秒重新生成 }; } export default function Home({ posts }) { return ( <div> <h1>我的博客</h1> <div> {posts.map(post => ( <div key={post.id}> <h2>{post.title}</h2> <p>{post.content}</p> </div> ))} </div> </div> ); } // 3. 动态路由 // pages/posts/[id].js import React from 'react'; export async function getStaticPaths() { // 构建时生成所有可能的路径 const res = await fetch('https://api.example.com/posts'); const posts = await res.json(); const paths = posts.map(post => ({ params: { id: post.id.toString() } })); return { paths, fallback: false }; } export async function getStaticProps({ params }) { // 构建时获取数据 const res = await fetch(`https://api.example.com/posts/${params.id}`); const post = await res.json(); return { props: { post } }; } export default function Post({ post }) { return ( <div> <h1>{post.title}</h1> <p>{post.content}</p> </div> ); } 

2. Astro SSG

// 正确姿势:Astro SSG // 1. 安装依赖 // npm create astro@latest // 2. 页面组件 // src/pages/index.astro --- // 构建时获取数据 const res = await fetch('https://api.example.com/posts'); const posts = await res.json(); --- <html> <head> <title>我的博客</title> </head> <body> <h1>我的博客</h1> <div> {posts.map(post => ( <div key={post.id}> <h2>{post.title}</h2> <p>{post.content}</p> </div> ))} </div> </body> </html> // 3. 动态路由 // src/pages/posts/[id].astro --- // 构建时获取数据 const { id } = Astro.params; const res = await fetch(`https://api.example.com/posts/${id}`); const post = await res.json(); --- <html> <head> <title>{post.title}</title> </head> <body> <h1>{post.title}</h1> <p>{post.content}</p> </body> </html> 

3. Gatsby SSG

// 正确姿势:Gatsby SSG // 1. 安装依赖 // npm install -g gatsby-cli // gatsby new my-site // 2. 配置数据源 // gatsby-config.js module.exports = { plugins: [ { resolve: 'gatsby-source-rest-api', options: { endpoints: ['https://api.example.com/posts'] } } ] }; // 3. 页面组件 // src/pages/index.js import React from 'react'; import { graphql } from 'gatsby'; export const query = graphql` query { allRestApiPosts { edges { node { id title content } } } } `; export default function Home({ data }) { const posts = data.allRestApiPosts.edges.map(edge => edge.node); return ( <div> <h1>我的博客</h1> <div> {posts.map(post => ( <div key={post.id}> <h2>{post.title}</h2> <p>{post.content}</p> </div> ))} </div> </div> ); } // 4. 动态页面 // src/templates/post.js import React from 'react'; import { graphql } from 'gatsby'; export const query = graphql` query($id: String!) { restApiPosts(id: { eq: $id }) { title content } } `; export default function Post({ data }) { const post = data.restApiPosts; return ( <div> <h1>{post.title}</h1> <p>{post.content}</p> </div> ); } // 5. 创建动态页面 // gatsby-node.js exports.createPages = async ({ graphql, actions }) => { const { createPage } = actions; const result = await graphql(` query { allRestApiPosts { edges { node { id } } } } `); result.data.allRestApiPosts.edges.forEach(edge => { createPage({ path: `/posts/${edge.node.id}`, component: path.resolve('./src/templates/post.js'), context: { id: edge.node.id } }); }); }; 

毒舌点评:这才叫前端 SSG,构建时生成静态页面,加载速度快,用户体验好,再也不用担心页面加载慢的问题了。

Read more

前端老哥必看:window.print只打半截?一招搞定HTML实际高度打印不踩坑

前端老哥必看:window.print只打半截?一招搞定HTML实际高度打印不踩坑

前端老哥必看:window.print只打半截?一招搞定HTML实际高度打印不踩坑 * 前端老哥必看:window.print只打半截?一招搞定HTML实际高度打印不踩坑 * 别整那些虚的,咱们直接开唠 * 这玩意儿到底是个啥妖魔鬼怪 * 浏览器打印机制那点不为人知的秘密 * CSS里的print媒体查询,是救星还是坑货? * 深挖底层逻辑,把打印机按在地上摩擦 * height: auto失效?布局塌陷的锅谁来背 * 强制分页符的正确打开方式 * 动态内容高度计算,别让JS骗了打印机 * 隐藏的overflow: hidden和fixed定位 * 这招好用是好用,但也有翻车的时候 * 优点当然是爽啊 * 缺点也得认,有些坑真的躲不掉 * 实战场景大乱斗 * 电商后台订单详情打印 * 财务报表长表格打印 * 简历生成器实战 * 电子发票和物流面单 * 遇到报错别慌,老司机的排查套路 * 打印出来是空白?

B站PC端web自动开启字幕脚本(2026新版适配)

B站自动字幕用户脚本:快捷键开关 + 自动开启字幕(2026新版适配) 作者:Apixus 更新日期:2026年3月5日 项目地址:GitHub仓库 一、脚本介绍 你是否经常在B站看视频时反复手动开启字幕?是否希望切换视频时字幕能自动开启? 这个用户脚本就是为了解决这些问题而开发的。 B站自动字幕脚本 提供了以下功能: * 🎯 快捷键控制:按 C 键快速开启或关闭字幕 * 🔄 自动开启:切换分P、点击推荐视频时自动打开字幕 * 🆕  2026新版适配:专为B站最新版播放器优化 * ⚡ 性能优化:智能监听,告别卡顿轮询 * 🛡️ 防冲突:自动识别输入框,避免误触 二、适用页面 * 普通视频页:https://www.bilibili.com/video/* * 播放列表页:https://www.bilibili.com/list/* 支持普通视频页、番剧页、播放列表页等常见场景。 三、

论文AI率从60%降到20%以内的完整攻略:亲测有效的3步法

论文AI率从60%降到20%以内的完整攻略:亲测有效的3步法

论文AI率从60%降到20%以内的完整攻略:亲测有效的3步法 上周一个学弟在群里发了一张截图,检测报告上面的AI率是63.4%,他已经修改了两遍,依然没降下来。他当时快崩溃了,说答辩在15天后,导师要求AI率必须低于20%,感觉毫无希望。 这种情况其实很多人都经历过。AI写的论文痕迹很重,换几个词、调整几句语序,根本没用,检测系统识别的是深层语言特征,不是表面文字。今天这篇文章,把我试验过的3步攻略完整写出来,60%能不能降到20%以内,看完你自己判断。 先搞清楚:为什么AI率这么难降 很多人修改了半天还是降不下来,根本原因是搞错了方向。 AIGC检测系统(知网、万方、大雅这些)识别的不是具体的词汇,而是语言模式。AI生成的文本有几个典型特征:句式结构高度规整、逻辑过渡词密集("首先、其次、再次、最后"这套)、段落长度均匀、主动被动句比例异常……这些特征是一个整体,你改几个词根本动不了这个模式。

浏览器 XPath 深度解析:为什么 90% 的前端高手都在用它?

浏览器 XPath 深度解析:为什么 90% 的前端高手都在用它?

浏览器 XPath 深度解析:为什么 90% 的前端高手都在用它? 你是否遇到过这些崩溃时刻:动态 ID 每次刷新都变、元素藏得比忍者还深、CSS 选择器写到怀疑人生?XPath 可能就是你的救命稻草。 文章目录 * 浏览器 XPath 深度解析:为什么 90% 的前端高手都在用它? * 一、什么是 XPath?为什么它能成为元素定位的"瑞士军刀"? * 1.1 XPath 的本质定义 * 1.2 XPath vs CSS 选择器:谁才是你的真命天子? * 二、为什么必须用 XPath?这些场景让你"不得不服" * 2.1 场景一: