ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Wiki.js 首屏优化实战指南:2.8s 干到 0.9s

2026/8/22 16:34:36 拓冰建站 浏览量
Wiki.js 首屏优化实战指南:2.8s 干到 0.9s Wiki.js 首屏优化实战指南2.8s 干到 0.9s【免费下载链接】wiki-Wiki.js | A modern and powerful wiki app built on Node.js项目地址: https://gitcode.com/GitHub_Trending/wiki78/wiki-Wiki.js 升级之后首页首屏从 1.5 秒拖到了 2.8 秒截图吐槽塞满了群聊。一周性能优化下来我们把平均首屏压回了 0.9 秒编辑保存后的转圈也基本消失。摸清时间都花在哪了动手之前别猜先收集证据。我们翻 Network 面板、服务端日志和数据库监控拿到三条结论Network 面板里JS 和 CSS 动辄几 MB每次打开页面都像第一次一样重新下载服务端日志显示每个匿名请求都在完整跑一遍渲染管线Markdown 从头渲染、目录重新解析数据库连接数长期贴着低位高峰期请求开始排队而配置里连接池默认值整段是被注释掉的。机器没毛病卡住的全是重复劳动。下面按动配置 → 动缓存/反代 → 动构建/进程的顺序把改过的东西交代清楚。动配置让浏览器和数据库各干各的活把连接池拧到 min 2、max 8配置文件里pool段的 min/max 都被注释掉了底下的 Knex 只能退回一套很保守的默认值。连接池就像高速路口的收费站车道车道开少了高峰全堵在入口处开多了又白占资源。我们按实例规模把它拧开pool: min: 2 max: 8改完重启服务高峰时段接口的排队肉眼可见地少了这是整轮优化里投入产出比最高的一处。给 /_assets/ 上一年的 immutable 缓存前端产物的文件名都拼了时间戳生产 webpack 配置里output.filename的${now}等于文件名变了才是新版。这正是长缓存的前提。反代上只加两件事文本类资源走 Gzip/_assets/前缀给一年gzip on; gzip_comp_level 5; gzip_types text/css application/javascript application/json image/svgxml; location /_assets/ { expires 1y; add_header Cache-Control public, immutable; }改完 reload Nginx静态资源体积缩了约七成老用户再访问时资源请求基本归零。动缓存与反代重复渲染变一次计算给 NodeCache 拧上 10 分钟闹钟内存缓存入口只有一行无参的new NodeCache()键永不过期——服务一长跑进程里就攒满了早没人看的旧页面 HTML内存只进不出。给它拧上默认过期时间和定期清理module.exports { init() { return new NodeCache({ stdTTL: 600, checkperiod: 120 }) } }改完重启键 10 分钟后自动失效NodeCache 每 2 分钟清一次过期项。再讲究点可以分层字典类配置给长 TTL热点页面给短 TTL免得内容改了用户还看旧版。让 Nginx 直接吐出匿名页面 HTML内存缓存就算全部命中页面 HTML 仍要进服务端渲染一次render-page.js里那一长串 pipeline 加 cheerio 解析的开销不小。匿名读者占大头的知识库这部分基本是白算的。我们的做法是在反代加一层页面缓存只放进匿名 GET命中就直接吐 HTMLproxy_cache_path /var/cache/nginx/wiki levels1:2 keys_zonewiki:10m max_size1g inactive60m; location / { proxy_cache wiki; proxy_cache_key $scheme$request_method$host$request_uri; proxy_cache_valid 200 5m; }改完上线后匿名读者的首屏掉到了 1 秒以内。但有一条红线必须单独立起来登录用户的页面绝对不允许进这层缓存——登录态内容随权限不同而变化一旦串掉就是越权事故。只对匿名请求启用或用Vary头区分响应并让缓存键携带鉴权信息。打开 sqllog 让慢查询现原形猜是猜不出 N1 的。把 core/config.js 预留的开关打开默认值false见server/app/data.ymlflags: sqllog: true改完服务端会把每条 SQL 都打出来配合数据库的 EXPLAIN 过一遍N1 和缺索引的语句会自己跳出来。这份日志非常吵定位完记得关回去。动构建与进程主包瘦身、重活分家把第三方库单独打成 vendor chunkwebpack.prod.js里原有的splitChunks只有name: vendor, minChunks: 2偏保守。我们把 node_modules 显式归进独立 chunk并打开chunks: all让主包只留自家代码optimization: { splitChunks: { chunks: all, cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: vendor, priority: 10 } } } }改完重新构建vendor 块几乎不再变动浏览器长缓存命中率稳定应用代码更新时只需重下小块 diff主包体积缩了约四成。让编辑器点击时才加载编辑器是重型组件绝大多数用户打开页面根本用不上不该占首屏const Editor () import(/* webpackChunkName: editor */ ./components/editor.vue)改完之后编辑器 chunk 只在按下编辑按钮那一刻才下载首屏体积又砍掉一截。还有两个细节其一cache-loader与 babel 的cacheDirectory已经配好缓存在.webpack-cache/下别顺手清掉它增量构建快很多其二MomentTimezoneDataPlugin默认打包了 2017 到当前年份加 5 的全量时区数据用户集中在少数时区的话收紧startYear/endYear甚至直接裁掉时区表体积当场省出一块。进程层面Node 单线程事件循环怕同步 CPU 密集任务生产环境把渲染这类重活交给独立 workerrender-page本身就是独立 job再上多实例吃满多核按机器内存给堆设上限一般取内存的四分之一GC 抖动拖慢响应的问题就稳了。试过又撤掉的东西Gzip 全量压连图片都过了一遍压缩CPU 蹿高、收益为零压缩只对文本类资源有意义。页面缓存 TTL 拉太长更新后用户长时间看旧版短 TTL 加发布时主动失效更稳。chunk 拆得太碎HTTP/1.1 下小文件请求数爆炸反而更慢要么合并要么先升 HTTP/2。登录态页面进了反代缓存就是上面那条红线发现即回滚。Docker 里无脑复制实例四个实例共用一套库内存缓存和连接池各是各的问题一点没少先看慢查询再谈扩容。多实例不做失效同步配置里的ha标志不是摆设多实例共享数据库时发布流程必须统一失效缓存否则各实例看到的内容不一致。最终账单与三件事改了什么省了多少要动几行配置连接池 min 2 / max 8高峰排队减少接口 p95 明显下降2 行 yaml/_assets/ Gzip 一年 immutable资源体积降约七成重复访问零下载约 8 行 nginxNodeCache 加 TTL 匿名页面缓存首屏 2.8s → 0.9s1 行 js 约 7 行 nginx构建瘦身 路由懒加载主包缩小约四成构建时间减半约 11 行 webpack只做三件事在反代配置里写入/_assets/的 gzip 与expires 1y段落reload Nginx打开server/core/cache.js把new NodeCache()改成new NodeCache({ stdTTL: 600, checkperiod: 120 })重启服务启用flags.sqllog跑一天按耗时排序的 SQL 逐条过 EXPLAIN定位完再关回去。性能优化是一场持续的小步快跑先吃下配置层的正反馈再逐步深入缓存和构建用日志定位、用数据说话。守住安全与一致性两条底线Wiki.js 就能从能用走到好用。【免费下载链接】wiki-Wiki.js | A modern and powerful wiki app built on Node.js项目地址: https://gitcode.com/GitHub_Trending/wiki78/wiki-创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考