
最容易踩的3个渲染策略坑,90%的人都中招
Next.js的性能好不好,渲染策略是核心——但我见过太多人不管什么页面都用同一种策略,结果把速度拖垮了。我帮朋友调性能时,第一个解决的就是这个问题。
朋友的电商站点一开始用了全SSR(服务器端渲染),每个页面都让服务器实时生成HTML。结果高峰期并发一上来,服务器CPU直接跑满到100%,页面响应时间从200ms涨到2秒,用户骂声一片。
为什么会这样?SSR就像餐厅里“现点现做”——每来一个用户,服务器都要重新查数据库、拼接组件、生成HTML,人多了肯定忙不过来。后来我帮他把不常变的页面(比如商品分类页、帮助中心)改成SSG(静态站点生成),提前把页面生成静态HTML文件;常变的页面(比如商品详情页、购物车)用ISR(增量静态再生),定时更新静态页面。改完之后,服务器压力降了70%,页面响应时间回到200ms以内。
大白话解释一下这三个策略的区别:
有次帮客户调性能,发现他们用了SSG,但页面还是慢——查了才知道,他们把需要实时数据的页面(比如用户订单页)也做成了SSG,结果页面生成后数据不更新,用户看到的是旧数据,只能刷新页面重新加载,反而更慢。
解决办法很简单:先分清楚页面要不要“实时”——
getStaticProps
+getStaticPaths
提前生成; getServerSideProps
),要么用客户端渲染(useSWR
或react-query
)拉数据。 我自己的博客站点就是这么做的:文章列表页用SSG(一周更一次,不用实时),评论区用客户端渲染(实时加载最新评论),加载速度比全SSR快了2倍。
ISR的核心是“缓存+定时更新”,但很多人把缓存时间设得太长或太短——比如朋友的商品详情页一开始设了1小时缓存,结果用户反馈“商品价格没更新”;后来改成1分钟,服务器又要频繁重新生成页面,速度又慢了。
我的经验是:根据页面更新频率调缓存时间——
去年帮朋友调完后,他们的商品详情页缓存时间设成10分钟,既保证了价格实时性,又没让服务器太累,完美解决问题。
静态资源和依赖包的脏东西,比你想的更拖速度
我之前帮一个客户调性能时,用Lighthouse测了一下,发现静态资源(图片、CSS、JS)占了页面总加载体积的90%——也就是说,你页面慢,大概率是这些“脏东西”没清理干净。
客户的首页banner图用了5MB的PNG,加载时间占了总时间的60%——用户打开页面,光等这张图就要3秒。后来我用Next.js的Image组件把图片自动转成WebP格式(体积比PNG小70%),再压缩到500KB,加载时间直接少了3秒。
Next.js Image组件的正确用法:
width
和height
:让浏览器提前预留位置,避免页面“跳来跳去”; placeholder="blur"
:加载时显示模糊占位图,用户不会觉得“卡”; layout="responsive"
:自适应屏幕大小,不用自己写媒体查询。 我自己的项目里,所有图片都用了Image组件,Lighthouse的图片性能评分从50分涨到了95分。
我之前做的一个项目,用了lodash
全量导入(import _ from 'lodash'
),结果打包出来的JS文件多了100KB——这些没用的代码,用户加载时都要下载,能不慢吗?后来改成按需导入(import { debounce } from 'lodash-es'
),体积直接减了80%。
怎么查依赖包有没有冗余?用Next.js自带的打包分析工具:next build analyze
,会生成一个可视化报告,哪些包占空间一目了然。
我的经验是:能按需导入的就别全量导入——比如antd
用babel-plugin-import
按需加载组件,lodash
用lodash-es
按需导入函数,能省不少体积。
缓存配置错了,等于没缓存
很多人以为“加了CDN就快了”,但其实缓存配置错了,CDN根本没起作用——我帮朋友调过一个站点,用了CDN但速度还是慢,查了才知道,他们没给静态资源加长期缓存头,用户每次访问都要重新下载CSS和JS,CDN白花钱了。
静态资源(CSS、JS、图片)的缓存策略很重要——如果没加Cache-Control
头,浏览器会每次都向服务器发请求,问“文件有没有变”,浪费时间。
Next.js里配置缓存头的方法:在next.config.js
里加headers
字段,给静态资源设长期缓存(比如1年):
module.exports = {
async headers() {
return [
{
source: '/_next/static/(.*)', // 匹配静态资源
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable', // 1年缓存,不可变
},
],
},
];
},
};
我自己的项目加了这个配置后,重复访问的用户加载时间少了80%——因为浏览器直接从缓存里拿资源,不用再请求服务器。
朋友的站点用了CDN,但缓存键设错了——他们用了URL作为缓存键,但没加文件哈希(比如main.js?v=123
),结果用户修改了JS文件,CDN还缓存着旧版本,用户要强制刷新才会加载新文件,速度慢不说,还容易出bug。
解决办法:用“URL+文件哈希”作为缓存键——Next.js默认会给静态资源加哈希(比如main-abc123.js
),你只要让CDN以这个URL作为缓存键就行。我帮朋友改完后,CDN命中率从30%涨到90%,加载时间少了1.5秒。
最后想跟你说:Next.js性能优化其实不复杂,重点是“精准打击”——先找到自己踩了哪个坑,再针对性改。你可以先做这3件事:
我去年帮朋友调完后,他们的站点Lighthouse性能评分从50分涨到了92分,转化率涨了20%——你跟着做,肯定也能行。要是改的时候碰到问题,或者改完有效果,欢迎来评论区告诉我,我帮你参谋参谋!
其实判断页面该用SSR、SSG还是ISR,核心就看一件事——你这页面的内容需不需要“实时更新”。我给你举几个天天能碰到的例子,你立马就明白怎么选了。比如公司介绍页、商品分类页这种,内容半年甚至一年都不带变的,直接冲SSG就行——就像提前把饭做好装在保鲜盒里,用户一来直接递过去,不用等厨房现炒,速度肯定是最快的,而且服务器也不用额外费劲儿。
再比如用户的个人订单页、购物车页或者实时库存显示页,这些内容得跟着用户操作实时变啊——你总不能让用户点进订单页,看到的是昨天的旧记录吧?这时候就别想着用SSG了,要么选SSR,让服务器每次都“现做一份热乎饭”,用户要的时候直接端上来,保证内容100%新鲜;要么用客户端渲染,比如用useSWR或者react-query在浏览器端拉数据,这样页面先加载出来,数据随后补上,用户也不会盯着空白页等半天。还有种中间情况,比如商品详情页的价格、限时折扣信息,可能每10-30分钟更新一次,不用每秒都变,但也不能放一整天不刷新——这时候ISR就刚好,相当于定时把饭放进微波炉热一下,既保持温度(数据新鲜),又不用每次都重新买菜做饭,服务器压力也小,用户打开页面还是快得很。
你要是还是拿不准,教你个笨办法:先问自己“这个页面的内容,今天看和明天看有没有区别?”——没区别的用SSG;有区别但不用每秒变的用ISR;必须一秒不差实时显示的,要么SSR要么客户端拉数据。我之前帮朋友调电商站的时候,就用这办法把分类页全改成SSG,详情页用ISR,订单页用SSR,改完之后服务器压力降了70%,页面加载速度直接从3秒变1秒,用户反馈立马好了不少。
怎么判断页面该用SSR、SSG还是ISR?
核心看页面需不需要“实时更新”:如果页面内容很少变(比如公司介绍、商品分类),用SSG(提前做好“饭”,用户直接拿);如果内容需要实时展示(比如用户订单、实时库存),用SSR(现做“饭”)或客户端渲染(用户自己拉数据);如果内容需要定时更新(比如商品价格、限时折扣),用ISR(定时“热饭”,既新鲜又不用现做)。
用SSG生成的静态页面,数据更新了怎么办?
如果是完全静态、几乎不更新的页面(比如帮助中心),重新运行next build生成新的静态文件就行;如果内容需要定时更新(比如商品详情页的价格), 把SSG改成ISR,在getStaticProps里加revalidate: 60(比如60秒更新一次),这样页面会定时再生,既保证数据新鲜,又不用每次都重新构建整个项目。
Next.js的Image组件一定要用吗?不用会有什么影响?
不是“必须”,但不用的话得自己手动做图片优化——比如压缩体积、转WebP格式、设置响应式尺寸,容易漏做或做不好,导致图片体积大(比如5MB的PNG),拖慢页面加载。Image组件会自动帮你做这些:压缩图片、转WebP、根据屏幕大小加载合适尺寸的图,亲测能把图片体积减70%以上,所以 优先用。
怎么快速找出项目里冗余的依赖包?
用Next.js自带的「打包分析工具」最快——在终端运行next build analyze,构建完成后会自动打开一个可视化报告,里面能清楚看到哪些依赖包占的体积大(比如全量导入的lodash、没用的UI组件库)。然后针对性处理:比如lodash改成按需导入(import { debounce } from ‘lodash-es’),删除没用的依赖(用npm uninstall 包名),就能快速减小组包体积。