首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >活动页每次上线都卡:用 COS 静态托管加 EdgeOne 把首屏压进一秒

活动页每次上线都卡:用 COS 静态托管加 EdgeOne 把首屏压进一秒

原创
作者头像
用户7589560
发布于 2026-09-09 09:28:51
发布于 2026-09-09 09:28:51
1080
举报

商协会的活动页面有个很典型的流量曲线:上线之后一周没什么人看,活动开始前两天突然涨起来,结束之后又归零。这种脉冲式访问对首屏要求很高——用户多半是从群里点链接进来的,网络环境不定,三秒还没看到内容人就走了。

我们之前的部署方式是一台轻量应用服务器上跑 Nginx,页面丢在目录里。问题有几个:带宽小,并发一上来就堵;没有 CDN,外地会员打开慢;每次发版手动传文件,容易漏。改造之后换成 COS 静态托管加 EdgeOne 加速,首屏从三秒多降到一秒出头。

先说构建。要做长缓存,前提是文件名跟内容绑定,内容变了文件名就变。Vite 默认带 hash,但别用 query 串版本号,那种方式在部分 CDN 上不会被当作不同资源。同时把 chunk 拆细,业务代码和第三方依赖分开,改业务的时候依赖包的缓存不失效。构建插件里挂上图片压缩,输出 AVIF 并保留 WebP 兜底,用 picture 标签让浏览器自己选。

图片是这类页面的大头。活动页通常有几张大图做头图,原图动辄两三兆,压完能省七八成。兼容上不用太纠结,近两年的移动浏览器基本都支持,不支持的回落到 WebP 也不会差太多。

上传环节写了个增量脚本。每次全量传一遍既慢又费请求,先拿 COS 的文件列表和本地构建产物做比对,只传哈希不一致的。

```js

const COS = require('cos-nodejs-sdk-v5');

const fs = require('fs');

const path = require('path');

const crypto = require('crypto');

const cos = new COS({

SecretId: process.env.TENCENTCLOUD_SECRET_ID,

SecretKey: process.env.TENCENTCLOUD_SECRET_KEY

});

const Bucket = process.env.COS_BUCKET;

const Region = process.env.COS_REGION;

const DIST = path.resolve(__dirname, '../dist');

const walk = (dir, base = '') =>

fs.readdirSync(dir, { withFileTypes: true }).flatMap((d) =>

d.isDirectory() ? walk(path.join(dir, d.name), `${base}${d.name}/`) : [`${base}${d.name}`]

);

const md5 = (p) => crypto.createHash('md5').update(fs.readFileSync(p)).digest('hex');

async function listRemote() {

const remote = new Map();

let marker;

do {

const res = await cos.getBucket({ Bucket, Region, Marker: marker, MaxKeys: 1000 });

res.Contents.forEach((o) => remote.set(o.Key, o.ETag.replace(/"/g, '')));

marker = res.IsTruncated === 'true' ? res.NextMarker : undefined;

} while (marker);

return remote;

}

async function upload() {

const remote = await listRemote();

const files = walk(DIST);

let uploaded = 0;

for (const key of files) {

const file = path.join(DIST, key);

if (remote.get(key) === md5(file)) continue;

await cos.putObject({

Bucket,

Region,

Key: key,

Body: fs.createReadStream(file),

ContentType: mime(key),

// 带哈希的资源长缓存,HTML 必须不缓存

CacheControl: key.endsWith('.html') ? 'no-cache' : 'public, max-age=31536000, immutable'

});

uploaded += 1;

}

console.log(`上传 ${uploaded} 个,跳过 ${files.length - uploaded} 个`);

}

upload();

```

这段脚本里有个必须做对的点:HTML 文件不能设长缓存。带哈希的 JS、CSS、图片可以缓存一年,但入口 HTML 每次都要回源,否则用户拿到旧入口,引用的还是旧资源,发版等于没生效。给 HTML 设 no-cache,配合 ETag 做协商缓存,既保证及时更新又不用每次传全量。

EdgeOne 这层的配置思路是:静态资源路径按后缀设长缓存并在边缘节点缓存,HTML 路径设不缓存或短缓存,回源到 COS 的静态网站域名。另外把 Brotli 压缩打开,文本类资源能再省两成体积。

发版之后还有一步容易忘——缓存刷新。带哈希的资源不需要刷,但 HTML 入口和不带哈希的老资源要主动刷新,否则边缘节点可能还存着旧内容。做法是调用 EdgeOne 的刷新接口,按 URL 提交刷新任务,只对入口路径提交即可,不用整站刷。

首屏还有一块是数据请求。活动页打开要请求活动详情,如果等 JS 加载完再发请求,链路是 HTML 到 JS 到接口再到渲染。我们改成在构建时把活动的基本信息预渲染进 HTML,首屏直接有内容,JS 加载完再接管。这一步对首屏的贡献比 CDN 还大。

改造完实测:首屏可见时间从三秒二降到一秒一,LCP 从接近四秒降到一秒五左右,活动页的报名转化率提升了一成多。带宽成本反而降了,因为大部分请求打在边缘节点上。

有几点经验值得留下。一是别把所有东西都塞进 CDN,带用户态的接口请求不能缓存,要单独走 API 域名,缓存规则里必须排除掉。二是监控要跟上,EdgeOne 的访问日志接到 CLS 之后,能看到各地区的首屏耗时分布,哪里的会员打开慢一目了然。三是回源鉴权要开,COS 开启私有读写之后只允许 EdgeOne 回源,避免源站被直接打。

我们这边在跑的商会管理系统叫未来漫城·商会互联平台,活动的报名页和邀请函都是这套部署方式,活动前临时加服务器这件事已经很久没做过了。

整体看下来,这套方案的技术含量不算高,核心价值是把"每次活动都要为性能操心"变成一次性投入。对商协会这种活动驱动、流量不平均的场景,这个投入是划算的。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档