Next.js 真让人抓狂
以为我在开玩笑?数百个问题被点赞无数却多年无人回应。等到终于收到回复,对方只会告知你的做法有误,并承诺真正的解决方案即将推出——然后这个“解决方案”就在测试环境里无限期滞留。
嘿,终于要写博客了。既然你正在读这篇文章,说明我已经写完了。我早就想这么做了,却始终找不到开始的动力。不过俗话说得好:愤怒是最好的动力。这话没错吧?
背景故事
你我即将踏上旅程,但先得铺垫场景。想象我们供职于$COMPANY,某项Next.js服务出了岔子。由于Next.js默认仅在开发环境启用日志记录,我们自然无从知晓具体问题所在。
我们的任务是配置生产环境就绪的日志系统。这绝非易事——不过世上本无易事。
中间件?荒郊野岭!
旅程的第一步是中间件。官方文档明确指出:
中间件在路由渲染前执行,特别适用于实现自定义服务器端逻辑,如身份验证、日志记录或重定向处理。
看起来够简单。该选个日志库了。我选了pino,毕竟之前用过。反正比console.log强多了。午饭前搞定。
来配置个基础中间件:
// middleware.ts
import { NextResponse, NextRequest } from "next/server";
export async function middleware(request: NextRequest) {
return new NextResponse.next({
request: request,
headers: request.headers,
// status: 200,
// statusText: 'OK'
});
}
export const config = {
matcher: "/:path*",
};
我觉得问题已经显现了。中间件最多只能传递4个参数,而实际影响路由调用的只有headers。更别提无法使用多个中间件或进行链式调用。这设计怎么能这么糟糕?中间件功能早在2010年代初Express出现时就已存在。
不过没关系,我们很聪明,现代 Node.js 有不少精妙的工具。来试试 AsyncLocalStorage 吧。
// app/logger.ts
import { AsyncLocalStorage } from "async_hooks";
import { Logger, pino } from "pino";
const loggerInstance = pino({
// Whatever config we need
level: process.env.LOG_LEVEL ?? "trace",
});
export const LoggerStorage = new AsyncLocalStorage<Logger>();
export function logger(): Logger | null {
return LoggerStorage.getStore() ?? null;
}
export function requestLogger(): Logger {
return loggerInstance.child({ requestId: crypto.randomUUID() });
}
// middleware.ts
export async function middleware(request: NextRequest) {
LoggerStorage.enterWith(requestLogger());
logger()?.debug({ url: request.url }, "Started processing request!");
return NextResponse.next();
}
呼,辛苦的工作完成了。来测试一下。访问 localhost:3000,我们看到这样的结果:
{ requestId: 'ec7718fa-b1a2-473e-b2e2-8f51188efa8f' } { url: 'http://localhost:3000/' } 'Started processing request!'
GET / 200 in 71ms
{ requestId: '09b526b1-68f4-4e90-971f-b0bc52ad167c' } { url: 'http://localhost:3000/next.svg' } 'Started processing request!'
{ requestId: '481dd2ff-e900-4985-ae15-0b0a1eb5923f' } { url: 'http://localhost:3000/vercel.svg' } 'Started processing request!'
{ requestId: 'e7b29301-171c-4c91-af25-771471502ee4' } { url: 'http://localhost:3000/file.svg' } 'Started processing request!'
{ requestId: '13766de3-dd00-42ce-808a-ac072dcfd4c6' } { url: 'http://localhost:3000/window.svg' } 'Started processing request!'
{ requestId: '317e054c-1a9a-4dd8-ba21-4c0201fbeada' } { url: 'http://localhost:3000/globe.svg' } 'Started processing request!'
不知道你是否用过pino,但这显然不对。你能找出原因吗?
与Next.js不同,我不会让你悬着。这是浏览器输出结果。为什么?因为Next.js默认的中间件运行时是edge。当然我们可以切换到nodejs运行时,理论上应该可行。但实际情况可能并非如此。
我在全新Next.js项目中测试过确实可行,但在实际项目中却失败了。我发誓自己没疯。不过这并非核心问题,我们正逐步接近真相。
翻阅本地精神病院档案
中间件日志固然不错,但真正的魔力在于页面和布局层。现在就来实践。
// app/page.tsx
export default function Home() {
logger()?.info("Logging from the page!");
return <div>Real simple website!</div>
}
刷新页面后得到:
✓ Compiled / in 16ms
GET / 200 in 142ms
就这?没错。一片空白。空空如也。
为了后世留存,正确效果应是这样:
✓ Compiled / in 2.2s
[11:38:59.259] INFO (12599): Logging from the page!
requestId: "2ddef9cf-6fee-4d1d-8b1e-6bb16a3e636b"
GET / 200 in 2520ms
好吧,篇幅有点长了,我直奔主题。logger函数返回null。为什么?我不太确定,但似乎渲染操作并未在与中间件相同的异步上下文中执行。
那解决方案是什么?你绝对想不到。还记得中间件唯一能传递的值是headers吗?没错,这就是我们要用的。
以下内容适合胃口够大的读者:
// app/log/serverLogger.ts
import { pino } from "pino";
export const loggerInstance = pino({
// Whatever config we need
level: process.env.LOG_LEVEL ?? "info",
});
// app/log/middleware.ts
// Yes, we need to split up the loggers ...
// Mostly the same as before
import { loggerInstance } from "./serverLogger";
export function requestLogger(requestId: string): Logger {
return loggerInstance.child({ requestId });
}
// app/log/server.ts
import { headers } from "next/headers";
import { loggerInstance } from "./serverLogger";
import { Logger } from "pino";
import { NextRequest } from "next/server";
const REQUEST_ID_HEADER = "dominik-request-id";
export function requestHeaders(
request: NextRequest,
requestId: string,
): Headers {
const head = new Headers(request.headers);
head.set(REQUEST_ID_HEADER, requestId);
return head;
}
// Yeah, this has to be async ...
export async function logger(): Promise<Logger> {
const hdrs = await headers();
const requestId = hdrs.get(REQUEST_ID_HEADER);
return loggerInstance.child({ requestId });
}
// middleware.ts
import { logger, LoggerStorage, requestLogger } from "./app/log/middleware";
import { requestHeaders } from "./app/log/server";
export async function middleware(request: NextRequest) {
const requestId = crypto.randomUUID();
LoggerStorage.enterWith(requestLogger(requestId));
logger()?.debug({ url: request.url }, "Started processing request!");
return NextResponse.next({ headers: requestHeaders(request, requestId) });
}
// app/page.tsx
export default async function Home() {
(await logger())?.info("Logging from the page!");
// ...
}
是不是很美妙?尤其欣赏现在能从服务器端导入中间件日志代码——当然这行不通。或者从中间件导入服务器日志代码——同样行不通。最好别搞砸。更别提客户端组件的日志了,尽管名字叫客户端,它们其实也在服务器上运行。没错,这是第三个分支。
恭喜,你正被温柔以待。请勿抗拒。
听着。我想道歉,因为是我把你引入这个陷阱。要知道,我自己早已深陷其中数次。设计得当的中间件系统确实很有用,而我想让你看看设计失败时的模样。这篇博文的创作动机,其实就源于此。
我想每个人都有过忍无可忍的时刻。对我而言,此刻就是转折点。去他的,直接用自定义服务器吧。
自定义 Next.js 服务器允许通过编程方式启动服务器以实现特定模式。多数情况下无需此方案,但当需要移除原生组件时可启用该功能。
让我们参考文档中的示例:
import { createServer } from 'http'
import { parse } from 'url'
import next from 'next'
const port = parseInt(process.env.PORT || '3000', 10)
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = parse(req.url!, true)
handle(req, res, parsedUrl)
}).listen(port)
console.log(
`> Server listening at http://localhost:${port} as ${
dev ? 'development' : process.env.NODE_ENV
}`
)
})
注意 handle 函数仍未接收任何参数,仅处理请求 URL 及原始请求/响应数据。
不过我们仍有 AsyncLocalStorage 可用,因此不必过多担忧。让我们稍作修改:
// app/logger.ts
// Reverted back to our AsyncLocalStorage variaton
import { pino, Logger } from "pino";
import { AsyncLocalStorage } from "async_hooks";
const loggerInstance = pino({
// Whatever config we need
level: process.env.LOG_LEVEL ?? "info",
});
export const LoggerStorage = new AsyncLocalStorage<Logger>();
export function logger(): Logger | null {
return LoggerStorage.getStore() ?? null;
}
export function requestLogger(): Logger {
return loggerInstance.child({ requestId: crypto.randomUUID() });
}
// server.ts
import { logger, LoggerStorage, requestLogger } from "./app/logger";
app.prepare().then(() => {
createServer(async (req, res) => {
// This is new
LoggerStorage.enterWith(requestLogger());
logger()?.info({}, "Logging from server!");
const parsedUrl = parse(req.url!, true);
await handle(req, res, parsedUrl);
}).listen(port);
});
// middleware.ts
import { logger } from "./app/logger";
export async function middleware(request: NextRequest) {
logger()?.info({}, "Logging from middleware!");
return NextResponse.next();
}
// app/page.tsx
import { logger } from "./logger";
export default async function Home() {
logger()?.info("Logging from the page!");
// ...
}
好,现在测试一下。刷新浏览器后…
> Server listening at http://localhost:3000 as development
[12:29:52.183] INFO (19938): Logging from server!
requestId: "2ffab9a2-7e15-4188-8959-a7822592108f"
✓ Compiled /middleware in 388ms (151 modules)
○ Compiling / ...
✓ Compiled / in 676ms (769 modules)
就这样?你他妈在开玩笑吧?搞什么鬼?
现在,你可能会认为这根本不是AsyncLocalStorage的工作方式。你或许是对的。但我想指出的是,headers()和cookies()正是通过AsyncLocalStorage实现的。这是Next.js开发者独有的能力,而我们无法企及。
据我所知,从中间件向页面传递信息仅有两种方式:
- 头部参数
- 通过
NextResponse.redirect/NextResponse.rewrite跳转至带额外参数的路由(例如 /[requestId]/page.tsx)
你可能已经注意到,这两种方式在此场景下都相当不便。
你们被宠坏了。Next.js开发者有他们的理念,要么接受要么滚蛋。请注意,如果问题仅限于中间件,我才不会浪费周末时间在这里抱怨React框架。信不信由你,我有更重要的事要做。这只是使用Next.js时每日遭遇的持续痛苦之一。
Vercel完全可以做得更好
最令人恼火的是,Vercel完全有能力做得更好。我不想过多赞美Svelte(Kit),因为对其近期发展方向存有疑虑,但它确实比Next.js优秀得多。看看他们的中间件文档:
handle – 该函数在 SvelteKit 服务器每次接收请求时执行 […] 可用于修改响应头或主体,甚至完全绕过 SvelteKit(例如实现程序化路由)。
目前看来相当不错。
locals – 若需向请求添加自定义数据(该数据将传递至
+server.js中的处理函数及服务器load函数),请按如下方式填充event.locals对象。
此刻我喜极而泣。你甚至能往里塞真正的对象/类,比如日志记录器。
可定义多个处理函数并按序执行。
这才是真正的工程设计。SvelteKit是Vercel的产品,其旗舰产品竟不如这个本质上是副业的项目,这算什么鬼?
科学家发现新超大质量黑洞
https://github.com/vercel/next.js/issues
虽无其他补充,但既然提及此处,不得不谈谈GitHub问题跟踪器。这堪称Next.js这堆垃圾中的皇冠明珠——希望与问题在此终结。其平均响应时间永远是零。我已将搜索问题跟踪器/讨论区作为消遣,专门寻找当前遇到的技术难题,并打赌需要多少年才能收到Next.js开发者的回复。
以为我在开玩笑?数百个问题被点赞无数却多年无人回应。等到终于收到回复,对方只会告知你的做法有误,并承诺真正的解决方案即将推出——然后这个“解决方案”就在测试环境里无限期滞留。
我一年前提交过两个问题报告。请注意:有效的错误报告必须附带复现步骤。
那么,花时间制作最小复现案例能得到什么?没错。完全的沉默。
我本想报告另外十几个遇到的问题,但经历此事后我放弃了。
说实话,我甚至不确定这些问题是否依然存在。
我们学到了什么?
我不知道。就我个人而言,我再也不想使用Next.js了。你或许认为这只是个别问题,是我反应过度。但漏洞和边界情况无处不在。他们究竟是如何让TypeScript的编译速度慢于Rust的?为何要区分客户端和服务器端的代码运行环境,却又不提供任何工具让我利用这种差异?为什么?为什么?为什么?
我自知影响力有限,难以推动团队彻底弃用Next.js。但若未来开发新应用,我定会提出异议。且看别处草是否更青。
本文文字及图片出自 Next.js Is Infuriating
我完全赞同。我也遇到过同样的问题,绝不会用Next.js做任何项目,还会鼓励公司每个团队都选用其他方案。
Next.js 堆砌了大量抽象层,而99.9999%的项目根本不需要这些。即使需要这些功能的项目,也更适合从底层组件构建定制化解决方案。
Next.js 毫无疑问是我用过最糟糕的技术。
幸亏不止我这么想。我曾用Next.js构建过一个中等复杂度的盈利级生产应用,最初托管在Vercel(和Google Firebase)上,后来转为自主托管并弃用Firebase,改用Pocketbase替代。
Pocketbase是这段经历中唯一值得称道之处。其余部分简直糟糕透顶。
无处不在的无限复杂性,持续不断的破坏性变更,遍地难以理解的文档。
简直糟糕透顶。如果能倒转过去五年的前端潮流,专注于扎实传授当时已有的技术,我们的处境会好得多。
我还用React构建过极其复杂的前端(数千用户规模,多处需要高强度视觉计算)。虽然我也并非特别喜欢React,但Next.js体验更糟。
最后还用Go语言和原生JavaScript构建过CMS。尽管开发体验偶尔欠佳,但我至少能预见操作后果——这难道很难吗?
在React和Next.js中,六年过去我仍在不断猜测可能发生的情况。没错,凭借对这些框架怪癖的积累经验,我几乎能修复所有问题,但整体设计实在混乱不堪。
在Go语言中,我最后一次猜测结果是在学习的前半年。此后再无意外。数年之前的代码库至今依然坚如磐石。
为什么前端就不能做到这样,该死的?
“多年未动的代码库依然坚如磐石。”这点对我至关重要。最近我翻出八年前的Java/Maven业余项目,首次编译运行就完美无缺。试想让一个八年的JavaScript项目正常运行会是何等煎熬…
你是说像这样?
nvm use && npm i && npm run dev
刚试过,我安装了某个指定版本的Node,将其固化到mise中,转眼就运行起来了。
我认为复杂的前端框架终将消亡。在尝试过HTMX、Alpine JS以及搭配Turbo+Stimulus的Ruby on Rails后,我完全认同这种范式。基础JavaScript或微前端框架才是真正的必需品。
或许有些夸张,但Cogent Core[0]确实存在; 它支持桌面应用、移动应用和网页应用,甚至兼容2D/3D场景。前后端全栈采用Go语言(后端)和WASM(前端)。
[0] https://github.com/cogentcore/core
尽管我像其他人一样讨厌Next.js,但请别再鼓吹全画布渲染方案了。它们简直糟透了。它们https://www.cogentcore.org/core/自己的官网加载缓慢,滚动时视觉体验极其痛苦——因为渲染频率似乎固定在60Hz,而非我显示器更高的原生刷新率。这种方案计算成本高昂,浪费机器资源来显示文本。想选取文本?最好祈祷开发者允许选取功能或没忘记实现——按钮内的文本就是典型案例。无障碍支持通常也更薄弱,屏幕阅读器常受其害,即便它们没出问题,其他组件也会出问题。
用Canvas替代DOM渲染 -> 🙁
编辑:再次尝试后问题更多,使用几秒就出现异常。左侧渲染存在缺陷,选中区域时常被截断,Ctrl+缩放无法像普通网站那样调整页面比例(但菜单缩放功能正常)。中键在新标签页打开链接失效,Z层级错误遍布界面。预计继续使用会发现更多问题。
天啊这太糟了!简直是灾难级的用户体验。请千万别推荐这个。
我试着在缩放栏输入“0”观察反应,结果程序彻底崩溃无法恢复…
我对Next.js的体验是:它的粗糙边缘是特性而非缺陷。所有设计都在逼你放弃自建环境,直接投靠Vercel托管服务。
上个月刚和客户合作过,他们雇佣非洲工程团队开发工具。最终交付的Next.js项目简直是一场灾难,与Vercel托管服务深度耦合,导致我在其他平台完全无法成功运行。客户是非营利组织,既不愿也无力承担Vercel托管费用,便求我尝试修复。当时我天真地想“不就是JavaScript吗,肯定能跨平台运行!”便着手处理。
折腾了一周后我只能摊手认输。那团意大利面般的JavaScript代码和堆积如山的库文件让我束手无策。所谓“编译”看似完成,但输出结果明显缺失大量内容却从不报错。从工具链到部署平台,处处充斥着诡异现象。
为什么要接受这样的结果?直接退回去让他们交付可用版本。
他们验收时系统确实能运行。但他们没意识到团队在Digital Ocean托管实例上运行着Django/Postgres后端,同时还存在两个不同的Vercel项目。这个使用率极低的项目每月耗费数百美元运行成本。
客户因看到系统运行而付款,但当要求调整至更廉价的托管方案时(可能因技术限制),咨询团队基本失联。由于所有托管服务均以咨询团队名义注册且未续费,网站最终被关闭。Digital Ocean因欠费清空数据库,导致海量手动录入的数据彻底丢失。
天啊,简直是恐怖故事
我为他们感到难过。他们是极其友善的人,我认为承包商并非有意欺骗,最终却成了所有人的噩梦。
我尚未听闻关于非洲IT与开发领域趋势、刻板印象、利弊的具体讨论。遵循HN指南——当话题趋于争议时(如本子版块),应激发更多好奇心,并寻求“对发言者最合理的解读”——我选择乐观假设:
这里究竟发生了什么?我推测该组织被选中必有缘由,但最终未能达标。
这家非营利组织在非洲运作,坚持尽可能利用本地资源。他们认识几位与内罗毕某团体合作的人士,对方口才极佳,且非营利组织成员本身不具备技术背景,对接触到的团队印象良好便贸然合作。这实属经典案例——我曾多次在外包项目中经历类似情境。
若事先咨询过我,通过提出关键问题并控制技术超支,本可为他们省去大量麻烦。
“我们对此了解不多,能出什么问题?”唉…
他们是善良的善意之人。他们深谙非洲工作的现实,却总以为人们最终都会选择正确的做法。
“我们对此了解不多,还是请专家来处理吧”
[已删除]
看到“非洲工程团队”时,我瞬间产生了“哎呀”的共鸣感——这源于我与其他大陆外部顾问开发的软件打交道的经历。我的经历涉及亚洲和欧洲团队而非非洲,因此发帖者成功唤起的正是应对距离、文化、时区及商业利益差异的体验(工程师需向不同管理层负责且追求不同利润目标),这些因素往往导致产出质量低于内部使用或面向SaaS市场的软件。
确实如此——劣质作品可能来自任何地方。但当合作方远在异国,存在语言障碍,且社会规范迥异时(例如美国项目经理是女性,程序员们完全无视她的指示。若我或其他男性在场,他们态度立变,立即执行指令。我们的项目经理几乎处处被忽视或碾压),合作难度极高。
没错,我们确实这么说。不过我不是美国人。但我们常会用“那些美国人”或其他不太友好的说法。
警察来了,时刻警惕着任何被感知或潜在的冒犯。警官您辛苦了。
趣闻一则:上次为朋友的小项目急需搭建全栈应用时,我心想“只需运行几个月,直接用next.js模板扔到vercel就行”。之前用过vercel+next觉得很简单。
打开Vercel,指向我的仓库和环境变量,开始构建后……出错了。我在Next问题页面搜索奇怪的错误日志,发现三年前有个仅获一票支持却无人回应或解决的单一问题。
于是我干脆搬到VPS上,直接用“prod build”命令构建,结果一切正常。
所以根据我有限的亲身经历,托管在vercel也省不了你什么事哈哈
我对Next.js的体验是:它的粗糙边缘并非缺陷,而是刻意为之。所有设计都旨在诱使你放弃自建环境,直接使用Vercel托管服务。
我对此深有同感。像服务器端渲染这类功能,通过极其流畅的引导流程强加给用户,但我担心这种表面的顺滑体验,实际上只能依靠我们这类人向Vercel等公司支付托管费用才能维系。
某种程度上,我感觉整个 React 生态系统可能已被企业所掌控。但愿并非如此。且让我们拭目以待。
它早已被掌控了。看看react.dev上创建新React应用的文档:
https://react.dev/learn/creating-a-react-app
它直接把你引向Next.js
可能?官方React文档推荐Next。
那次捕获发生在…两年前?(或许可以写篇好文章,如果还没人写的话)
大家好!我负责维护Redux,深度参与React社区,既花大量时间批判React团队的决策,也向社区解释这些决策背后的考量。
我确实撰写了那篇博客,并于今年早些时候在会议上就此发表演讲。内容涵盖:React团队为何转向引导用户使用“框架”构建React应用;React服务器组件背后的开发理念;为何直到数月前React文档才将Vite等工具列为可行选项;以及其他相关议题:
– https://blog.isquaredsoftware.com/2025/06/react-community-20…
– https://blog.isquaredsoftware.com/2025/06/presentations-reac…
感谢这些文章。阅读后我收获颇丰。
确实——过去两三年间,React的“官方立场”发生了非常明显的转变。令人遗憾的是,他们如此急剧地偏离了最初让React大获成功的简约理念和“专注做好一件事”的哲学。我从React早期就开始使用它,并用它成功打造了多个长寿项目,因此看到它如此失势,我真心感到痛心。
但客观而言,这份痛心无法改变现实。至少在我自己的专业圈子里,如今似乎没人愿意用React启动新项目了。近期我参与或讨论的付费前端项目中,几乎100%都已转向替代方案——最常见的是Vue,但其他选择也至少被认真考虑过。甚至有几位多年未合作的招聘人员突然现身,急切寻找能接手React项目的人选,并坦承这是因为他们苦于找不到愿意碰React的优秀人才。这一切与2020年代初React作为前端首选的盛况形成鲜明反差。而这种转变无疑是对React转型全栈框架的直接反应——该策略不仅增加了复杂性,更导致Vercel对官方React开发的掌控日益明显。
纵观历史,众多流行框架都曾被企业“掌控”——如React(Facebook)和.NET(微软)便是由巨头打造。我们这些普通开发者只能随企业风向起伏,但众所周知,一旦企业扼杀创新空间,所有人终将转向下一个热门技术。
Svelte由Vercel资助,谁知道Sveltekit会不会朝相同方向发展呢。
我知道,但我认为Sveltekit有Cloudflare等适配器,很难想象它会不引发强烈反弹就转向那种模式。
我对nextjs了解不多,也不清楚它是否像当前的sveltekit那样开源。
在我看来,nextjs(我认为)始终倾向于Vercel,但sveltekit在管理多适配器方面有着深厚的积淀。
话虽如此,仍存在突然撤资的风险。但若真发生这种情况,我会继续使用最后一个能兼容CF及其他云服务商的sveltekit版本。
仅有3/40位Svelte维护者供职于Vercel,且他们主要资助Svelte核心开发。SvelteKit的日常维护主要由Vercel外部人员承担。
说“仅有3/40”维护者未尽实情。具体是哪3位?这3人每月投入的工作时长占总工作量的百分比是多少?
过去一年中对SvelteKit贡献排名前三的开发者均就职于Vercel:https://github.com/sveltejs/kit/graphs/contributors?from=8%2…
Rich和Simon极其重要,但他们参与更多是出于对Svelte和社区的热忱,而非Vercel的薪酬。Tee目前承担着SvelteKit的大部分维护工作,主要依靠社区捐赠资助。这还不包括vite-plugin-svelte或Svelte CLI等完全由志愿者维护的基础设施。即便Vercel的资助比例接近多数,我也不认为他们承担了Svelte项目的主要开发经费。
作者列举的粗糙边缘问题在Vercel托管环境下同样存在。其架构设计似乎过于精巧,反而引发了各种问题。
商业激励确实会促使影响付费(托管)客户的问题获得比自托管更好的解决方案,但这不足以解释如此程度的痛苦,尤其在同样会影响付费客户的问题上。
我同意,但怀疑这种“聪明”部分源于专有行为,部分源于他们那套怪物般的系统——只有在自家基础设施上才能正常运行。
同感。感觉这是诱导用户陷入供应商锁定的陷阱。
还是用别的吧。
这描述简直是“咨询公司支持的开源项目”的典型套路。他们希望产品足够优秀成为行业标准,却又故意设置门槛,让预算充足的客户不得不付费找开发者解决。
我虽非专业开发者,但偶尔会做小型网页项目。
我听过Next.js的这种借口,本想通过使用Vercel绕过问题——它确实满足了我的项目需求,但似乎毫无区别。
> 我百分百赞同。我也遇到过同样的问题,绝不会用Next.js做任何项目,还会鼓励公司所有团队选用其他方案。
情况会先恶化再好转。目前像PluralSight这类在线课程几乎在所有React相关课程中都在推广Next.js。真不知是什么荒谬的思维导致这种悲哀局面,但事实就是如此。
这种思维逻辑就是“大家都在用什么?我也用那个”
> 这种思维逻辑就是“大家都在用什么?我也用那个”
我对此存疑。即便在 react.dev[1] 官网上,Next.js也被推崇为 create-react-app 的继任者——这种前提本身就很愚蠢。事情绝对不对劲。
[1] https://react.dev/learn/creating-a-react-app
最近处理前端面试很有意思。
我们设置了最多30分钟的实践环节:要求候选人创建React项目演示useState和useEffect等功能。我允许使用任意命令行工具,并开放谷歌搜索/ChatGPT辅助。
超过半数候选人完全不了解如何在Next.js之外使用React,甚至有人坚称这不可能实现——即便我明确告知并非如此。
这让我非常惊讶。我经常用Vite快速搭建新React应用来复现第三方库的问题。他们怎么会不知道,在CodePen或CodeSandbox上搭建项目根本不需要任何服务器端知识?(当然Vite自带小服务器,但你完全不必了解它)
有些开发者只在功能工厂里工作过,那里要求极低,还有资深开发者替他们处理项目。当招聘人员不筛选这类人时,基础的React测试就必须发挥作用。
你究竟在测试什么?这听起来像个糟糕的面试。
大概是基础React经验吧。粗略看,似乎所有面试形式在某些人眼里都糟糕透顶。你觉得什么方法有效?
更像是对React细节的随机测试。比如:选个框架,抽掉某个随机组件,看看你对被移除组件周边的理解程度。框架结构庞杂(或功能有限),指望候选人碰巧熟悉被抽中的随机实现细节,这根本是在筛选运气而非能力。
最近面试题对我来说变成了:“这是ChatGPT为(与招聘岗位相关的先前面试题)生成的代码”,问题出在哪?现在该怎么做?(该代码可能由ChatGPT生成,也可能并非如此。)
这更像是对随机React细节的测试。
它更像是检验你能否在需求出现时(必要时借助谷歌/ChatGPT)搞清楚随机React细节。这其实很好地模拟了实际工作中应对突发需求时查找零散细节的能力。基于React的开发并不需要太多原创性思考——绝大多数工作本质上就是摸清依赖项的细节以适应具体需求。
出于好奇,我让ChatGPT回答了这个问题,它毫不犹豫给出了完美答案。即便你对React的认知仅限于“它是开发Web组件的库”,也应该能轻松回答这个问题。
我原以为那场面试并未开放ChatGPT。若你只想测试能否理解我口中的词汇,只需将问题输入ChatGPT再念给我听,确实可行。
既然ChatGPT能回答其他React细节问题,为何偏偏禁止它参与面试某个随机环节?
因为人类交流需要社交互动,若无法通过社交线索判断何时该放开话匣子、何时该收敛言辞,那你就完蛋了。我对此也颇有困扰,所以他人遇到同样问题并不意外。
开工作会时,难道你真会在两台笔记本上分别打开ChatGPT和Claude,然后坐享其成30分钟到1小时?
最基础的React功能不属于“React细节”范畴。
必须记住,Next是唯一能支持最新版React部分功能的框架。
对许多人而言这只是基本逻辑:“所有人都想要最新React功能,而Next是获取这些功能的唯一途径,所以所有人都想要Next”。
至少Vite正与rsc同步发展。https://github.com/vitejs/vite-plugin-react/tree/main/packag…
> 别忘了,Next是唯一能支持最新React版本部分功能的框架。
这说法极其可疑,不是吗?
未必如此,因为这些功能涉及天生复杂的利基特性,比如统一的服务器/客户端渲染(例如RSC、带选择性加载的流式SSR、服务器操作)。
Next.js本质上是参考实现和测试平台。
人们的误区在于认为必须默认启用客户端加载这种天生复杂的利基功能——这其实是Web技术怪癖催生的利基优化方案。
> 未必如此,因为这些特性本质上涉及统一服务器/客户端渲染等复杂利基功能 (…)
我的观点是:他们推销的特性恰巧与唯一能实现这些功能的企业的价值主张高度吻合,这种巧合实在可疑。
若人人都能自主决策而非随波逐流,我们所有领域都会进步得多。
这种说法有些不诚实,因为现实中无法仅凭技术价值做决策。维持项目活力与更新需要大量资源投入,即便某些方案糟糕透顶,我们也几乎只能选择那些已投入资源的方案。
再加上如今“网页开发”涵盖范围极广——从博客这类内容型网站,到电商平台,乃至用户体验设计、视频编辑等复杂应用。
面对如此多元的网络解决方案,还妄想单一方案包揽所有场景,实在荒谬至极。
为何如此?在网页浏览器兴起前,微软的GUI框架和苹果的系统早已覆盖了大量使用场景。
既然Win32或MFC如此优秀,为何HTML能风靡全球?
因为它解决了不同问题。CSS固然糟糕,但部署便捷性和分发渠道的优势,远胜于HTML在图形界面构建上的缺陷。况且MFC被微软垄断的事实也无助于推广。
凭什么用HTML做GUI?它本就是为内容而非应用程序设计的。超文本标记语言嘛。
所以你也认同两者解决不同问题。这恰恰是当前前端领域的两种典型应用场景。
对我来说仅次于最糟糕的。我用过Sharepoint。
如果你用过Lotus Notes邮件系统,那绝对是第三糟糕的体验。至今我仍不明白,一个邮件和日历客户端怎么能把电脑拖得这么慢(依稀记得上次用还是2013年在公司,那时候还没SSD硬盘呢)。
嗯… 我不确定楼主所说的“技术”具体指什么,但Notes至少不该被当作开发平台。
它确实成了开发平台,但本不该如此。
我确实该说得更明确些。我指的是库或框架,而非广义上的技术。抱歉。
你让我想起Lotus Notes带来的噩梦。
我在IBM工作时,所有人都在用Fetchnotes(一款泄露到网上的内部工具,它封装Lotus Notes的.so文件,让你能用常规的邮件/通讯录/日历程序和格式操作)。
你说不想用调试器逐步执行脚本来理解官方API用法?虽然现在肯定改进不少,但Sharepoint是我年轻时接触的平台之一,每次提起它仍会让我产生不愉快的回忆。
记得早期有几个围绕SharePoint的项目,但最“特别”的是用SOAP API更新WordPress站点。简直是地狱般的体验——每小时自动抓取更新内容,再推送到特定WP内容类型(印象中当时内容类型功能可能尚未存在,毕竟是2008年)。
之所以采用这种方案,是因为IT部门从微软合作商处采购了内容管理系统——毕竟CIO曾是埃森哲/Avanade的顾问。但宣传册式的官网早前已外包给某家纽约网络公司。由于CIO不愿管理多重账号密码,WordPress站点建成后,他们又聘请SharePoint顾问构建员工使用的CMS系统。然而该系统仍无法对接WordPress,于是整合两者的任务落到了另一位承包商(我)肩上。
我虽有WordPress开发经验,甚至发布过几款颇受欢迎的插件,但SharePoint的复杂程度远超想象。我编写了代码生成工具,通过解析WSDL自动生成包含所有必要类和调用的库文件,无需SharePoint开发经验即可使用;还为少量“数据桶”编写了基础ETL流程。这场历时两三个月的工程中,那些库文件和代码至今仍在使用——该项目至今仍采用WordPress前端搭配SharePoint后端(至少2022年最后一次与仍在职人员沟通时仍是如此)。
除非亲眼所见,否则无人能理解其中艰辛。
深表同情。
在我看来,SharePoint始终在自我定位中迷失,试图包罗万象的结果导致功能膨胀失控,最终沦为性能糟糕的混乱系统。
在我看来,Sharepoint像是由原始编码代理随性编写的产物,随着时间推移愈发糟糕。
我用Next.js + GraphQL运行SaaS已有4.5年,坚持使用Pages路由器消除了大部分复杂性。
最近我重写了身份验证系统,改用better-auth(作为独立服务),这让我得以完全脱离Next.js(正考虑采用React Router 7或Tanstack Router)。
当初选择Next.js时,它让服务器端渲染变得异常简单,但后来发现我根本不需要这个功能。我的营销网站完全是静态的,应用程序也完全采用客户端渲染。
我后悔将个人网站“升级”到应用路由器。这简直是个大错误,尤其对于近乎纯静态的网站而言。
可悲的是,Tanstack几乎隔天就发布新版本,而React Router早在五代前就已成熟,却似乎无法通过持续变更API来适应永无止境的JavaScript相关性终结之战。
我也使用Next并采用Pages路由器,这为React前端开发提供了极佳体验。
某些场景下我需要将无头CRM/API“隐藏”在前端背后,此时用Next作为后端代理方案效果极佳。
我更推荐 React Router 而不是 Tanstack。两者都试过,RR 明显更简单可靠。
Tanstack 似乎在效仿 Next.js 的做法,把一切搞得过于复杂,而且大部分功能的文档都感觉不够完善。
我只用过 Tan 的 React-Query,在 React 代码库中使用它简直太棒了。
https://tanstack.com/query/v5/docs/framework/react/overview
为何要放弃Next?机会成本值得吗?
寻求对前端托管位置的完全控制权。当然,我可以在其他地方运行Next.js,但同样也能在其他平台运行React Router,且整个过程能获得更佳体验。
没错,即便你设法绕过了它那些极其愚蠢的限制——说真的,设计新路由机制的人到底是谁?他们真做过网站吗?——每次升级新版本都会搞砸所有功能。
相比之下,用Express实现DIY服务器端渲染虽然需要几天调试,却能为多个项目稳定运行多年。
Next.js最令人抓狂之处在于,它试图成为酷炫的Rails/Wordpress/Meteor式全栈万能框架,但你很快会发现它替你做出的所有决策,都是最无趣的选项(比如对中间件、图片缩放、全局SSR等领域的固执立场),而真正能提升效率的决策权(数据库、ORM、通信协议等)却全权交由用户处理。这根本无法与Rails/Wordpress/Meteor等框架相提并论。等等),而那些真正能提升效率的决策权(数据库、ORM、通信协议)却抛给你自己。它根本无法与Rails/Wordpress/Meteor相提并论。
框架定义的基础设施本是个极具魅力的理念,但如今Next更像是基础设施定义的框架——Vercel平台的架构设计才是Next特性背后的真正驱动力。本应是“Vercel适应Next”,结果却出现了四种微妙差异的函数运行时。我的使用仪表盘显示最常用的两项是“Fluid Active CPU”和“ISR Writes”。我每月支付20美元,只祈祷这些指标别超过100%——因为超标时我根本搞不清原因。
面板上半数标签都是《星际迷航》式的技术术语,我本该花时间学习,但深信这些术语在下次重大版本更新时都会改变——部分原因在于,我始终怀着希望与祈祷期待着这种改变。我认识不少曾经死忠的Zeit粉丝,如今都带着项目和客户另寻他处。说到底,如果他们问我下个重大版本该改进什么,除了“你们从App Router开始至今做过的每个重大决策都是错误的”之外,我实在不知如何回答。这种局面该如何挽回?我不知道。
许多抽象层和nextjs工具的功能,我的操作系统都能做得更好、更简洁、更可预测。
我猜过于复杂的ENV/.env加载层级(部分)是必要的,因为Windows没有(曾经没有?)环境变量。inotify、端口检测、线程管理也是如此:*nix系统做得很好,还算一致。但当你需要在*nix和Windows上实现相同功能的接口时,最终只会得到Next.js这类堆砌的轮子重造与抽象层(这些抽象终究会漏洞百出)
>Windows没有(曾经没有?)环境变量
不,Windows从DOS时代起就具备完全标准的环境变量系统
真正“缺失”的是“Bash式”启动能力:
KEY=value ./myApp。这种变量仅作用于单次执行。而Windows命令提示符需要两步调用:
PowerShell 同样需要:
或更“冗长/显式”的写法:
无论采用何种方法,这些变量均不具备“作用域”特性。
若需完全隔离,
cmd /C “set KEY=value && ./myApp”其实并非不可行方案。或者直接在PowerShell中执行:
Start-Process myproc.exe -Environment @{ FOO = ‘bar’ }这语法简直糟糕透顶。
PowerShell怎么会流行起来,我实在想不通。
因为它是对象,不是文本。它在脑中“扩展性”强得多,因为通常比bash需要掌握的知识少得多。再也不用写笨拙的sed和awk脚本,操作对象轻而易举。在需要从零创建对象时出现些别扭的语法,这代价很公平。
对吧?这就是Windows对你大脑的改造。
yeah | it's | so | much | better | with | bash | tr | cut -d' ' -t5 | jq .entry[].text | sed -i s/who/evenknows/g
任何维护过半复杂Bash调用管道链的人都知道,这套咒语脆弱得令人发指——只要你稍有异样眼神,或管道链中某个环节输出意外结果,整个系统就会崩溃。
PowerShell虽然读起来极其糟糕,写起来也差不了多少(瞧瞧,居然有正规的自动补全功能,不用再费劲地切换到第四行——等等抱歉是第五行——啊见鬼居然是第六行还有个隐形空格),但至少能产出一致可复现的结果。
不,你的Python脚本不算数,它逼我用pip安装requests。哦抱歉,pip不能这么用,得先跑apt install python3-pip否则整个系统就崩了。
> 因为Windows没有(曾经没有?)环境变量。
在我从业生涯中,Windows始终支持环境变量。这至少已有25年历史。无论是图形界面还是命令行,都能查看/编辑环境变量。
Windows在Linux诞生前就已具备环境变量功能。它还拥有FindFirstChangeNotification(若你自虐可选ReadDirectoryChangesW)这类特性,这在inotify出现前就已存在。
Windows几乎实现了你能想到的所有功能(尽管有时以极其糟糕的形式呈现),只是Vercel雇员根本不在乎如何正确使用原生API,总想把一切都映射成类UNIX的操作方式。
这似乎忽略了Windows最初可能就以类UNIX方式实现这些功能的可能性——那将比微软后来推出的方案好上千万倍。
Windows拥有这些API的时间远早于UNIX构想它们的年代。你可以整天吹嘘io_uring的优越性,但它早在Win32时代就存在了15年,且始终保持兼容性。我可不敢保证2040年io_uring还能留在内核里。
所以啊,事后诸葛亮可真容易。
PS:不,UNIX方式也烂透了,只是烂得方式不同。
端口检测?线程管理?这些跟next.js没半毛钱关系吧?
靠,没想到有人对Windows乃至操作系统历史如此无知。现在的计算机科学到底在教些什么玩意儿
我目前最大的问题在于官方React团队将其推崇为首选框架。当年它还使用Pages Router且不强迫所有内容塞进服务器端组件时倒也还能接受,但想到新手们要靠它学习Web开发,实在令人心疼。
我已将多数项目从Next迁移至Astro,从此再未回头。使用体验简直如沐春风。
听起来像是JavaScript版的Spring。
Java Spring本质上是通过依赖注入机制,以可控方式组合大型软件组件(单例)的解决方案。它本身并不规定具体实现方式,甚至不限定是否用于Web应用。公平地说,Servlet API(早于Spring诞生)始终表现优异(因此至今仍是Java生态中所有Web应用的基础)。有趣的是,Java的日志机制曾混乱不堪,直到slf4j和logback成为事实标准才真正完善。原帖提到的问题在Java、Spring、Spring Boot或Dropwizard中都微不足道。
Java虽不提供等构React服务器端渲染,但该功能本身价值存疑——多数单页应用既不需要搜索引擎索引,也不追求近乎瞬时的加载速度。
这是我读过最精辟的Spring框架解析。
尽管Spring存在粗糙边缘和怪癖,它仍是极其稳定的框架。反观Next,这盒惊喜总在令人以为见识尽数时不断带来新奇。
那是_Nest_而非_Next_。真正的怪物。
读到这段有点意外。我试过两者,Next留下了糟糕的体验,但Nest倒是挺有意思。不过没用它搞过太复杂的项目,所以很好奇大家对Nest有什么不满。
那你最后用了什么替代方案?
这个问题很难一概而论。我最近主要做提供GraphQL API的服务端开发,较少接触客户端渲染。API端我坚持用Go语言,毕竟最熟悉。但用Django或FastAPI开发服务也完全没问题。其实任何技术都行,只要是枯燥无味的那种就行。
若真要开发带UI的项目,我会选择标准的服务器端渲染多页面应用,同样用最无趣的技术实现。若你偏爱JavaScript及其生态,就用Express吧。如今直接运行TypeScript文件已无障碍,内置测试运行器也相当给力。
若单页应用更合理,则选用原生React。对于高交互性应用(尤其涉及登录场景),其实无需React服务器组件。
人们总爱抱怨Next,但世上本无完美解决方案。Next的问题根源在于其复杂性,这种复杂性在其他框架中往往以不同形式显现。Remix作为竞争对手也有其独特怪癖。当然你也可以用Vite、Tanstack路由器等工具自行构建,但这意味着要手动实现相同功能——尽管能更契合个人需求。这未必是坏事,但绝非适合所有人的选择。
理想方案是让React专注前端(这才是其核心价值所在),后端则采用PHP、Java、Ruby等技术。
这种服务器端React的疯狂尝试只会引入各种不必要的怪癖。
此外,风投支持的Vercel自然在刻意简化Next.js功能,以便让用户付费。这是个陷阱,大家务必警惕。
>最佳方案是将React用于前端(这才是其核心价值所在),后端则采用PHP、Java、Ruby等技术。
>这种服务器端React的疯狂做法只会引入各种不必要的怪癖。
我有点困惑你的意思。Next.js完全支持用PHP、Java、Ruby等语言构建后端,甚至可以用于服务器端渲染。
你说的其实是完全不做服务器端渲染?我需要澄清的是,这依然属于“React用于前端”的范畴。毕竟React还能用于什么场景?
服务器端渲染恰恰是Next.js的核心价值之一(尽管其复杂性处理未必完美)。若你不需要此功能,那确实…选择Vite更有道理。但后端选型与之无关。
听起来像阴谋论。让框架更易用有什么问题?没错,资助框架开发的厂商确实也在资助开发者打造黄金部署路径,引导用户使用其PaaS服务。但除非我们联合发起众筹,支持一个不依赖特定平台的框架,否则你期待框架开发如何推进?Heroku/Cloudflare/AWS/GCP同样完全有能力资助开发者优化其平台的部署体验。
> 让框架更易用有什么问题?
供应商锁定。魔法般的抽象设计看似完美,直到你需要调试几层深处的逻辑时,魔法突然失效。
> 你希望框架开发以何种方式推进?
或许是松散协作的开源项目。若这行不通,我宁可没有框架。
> 若此法不可行,我宁愿完全没有框架。
虽然我们都渴望隐居山林当木匠,渴望企业不复存在,但这显然不切实际。
魔法般的泄露抽象与供应商锁定无关,且源代码开放,所以我没看出锁定之处。当然,“嘿,直接部署到Vercel更省事省钱”这种锁定确实存在,但任何事物都需要成本——要么由开发者承担,要么由公司承担。
我从未声称自己的偏好是现实的!
事物确实需要成本,但问题没这么简单。Next和Vercel出自同一组织。我并不反对付费托管方案简化运维流程。然而当同一家机构掌控免费服务时,他们能让体验变得比“自然状态”更轻松(可能还更符合语法规范!谁知道呢)。
基于lit-html在轻量级DOM上渲染的原生Web组件,配合Express 5的简易API层——这大概是我见过最接近完美的方案了。
唯一的“缺陷”是缺乏防护机制,因此可能不太适合经验参差的大型团队。
最近真心爱上Astro,其简洁性让你能自由发挥,且轻松实现自主托管。它提供优质的前后端解决方案,并支持按需集成React、Vue、Angular等框架。
若仅需简单React前端,可使用react-router,后端则可采用其他技术栈。
虽非楼主但补充:React Router v7(原名“Remix”)具备Next.js所有核心功能,却没有冗余代码或Vercel驱动的糟糕体验。
我个人使用Remix时遇到了与博文所述类似的各种问题,包括Remix与React Router v7集成时的混乱局面。一年前使用Remix时,日志记录和中间件也堪称灾难——例如它根本不支持中间件,且无法从请求发起方(如Express等服务器框架)创建LocalContext来处理请求流经Remix应用的过程。
我也觉得他们大概会效仿Vercel的模式,把框架当作商业模式,最终被Shopify收购。虽然不清楚事情会如何发展,但这种模式我不会深陷其中。
除非他们发布v8版本,届时你将被迫按库开发者的意愿重构应用,否则就无法更新。
Hono
> Next.js绝对是我用过最糟糕的技术。
公平地说,这部分也归咎于使用它的人。例如:若你试图构建一个预期使用十年以上的项目,却不愿花20小时看Udemy的Angular课程,那么无论选择什么技术栈,你的项目注定会变成一团糟。
它简直像继承了ext.js所有的问题和诅咒。
我需要移动某个元素。仅仅几像素的距离。为此我花了4小时研究hydration机制,源代码里还留着备注解释为何无法移动以及我的临时解决方案。
半数问题源于对代码运行环境的相对认知不足。Next.js因浏览器、中间件、边缘与节点、SSR等层层交互而堆叠出多重复杂性,这种架构仅适用于以下特定场景:
否则请选择成熟方案:坚持使用react-vite单页应用,或采用Rails等常规SSR方案。
我虽不认同上述适用场景,但即便符合Next.js的匹配条件,其带来的生产力与可维护性下降也绝不值得。
我使用Gleam的Lustre并深感满意。Elm创始人曾发表过精彩案例研究演讲,其中揭示Next.js本质上与其理念背道而驰:
https://www.youtube.com/watch?v=sl1UQXgtepE
这场演讲非常精彩——感谢分享!
来自Gleam的Discord频道:
https://discord.gg/Fm8Pwmy
其中包含不少有价值的内容
Vercel是现代网络的毒瘤。他们将魔爪伸入每个框架生态系统,将其作为付费方案的销售渠道肆意滥用,却假装关心开源、竞争和网络发展。
感谢提醒。多年不用next.js后,心理健康状况显著改善。
Vercel资助了许多原本资金匮乏的开源项目。他们的框架专为自建平台量身定制,确实提供了良好体验。我目前虽未使用其服务,但用户确实会被引导至付费方案——因为开发体验确实出色。我承认他们是怀有商业动机的资本主义企业,但称其为“现代网络的毒瘤”未免言过其实。
用户被迫转向付费方案实属无奈。NextJS虽开源,但其运行所需的后端服务并非如此——所有复杂性都源于此。即便在Netlify平台,其图像处理堆栈也会引发棘手问题。这些所谓的“优化”与缓存机制令人完全无法理解,更会遭遇服务商的实现缺陷。当然你可以直接在容器中运行,但这样既要承担NextJS预优化带来的所有复杂性,却又无法获得任何优化效果。
Serverless框架曾试图构建NextJS的自主运行堆栈,但因Next的复杂性而漏洞百出。开源的本质在于可运行性。发布框架并资助增强NextJS的开源项目固然美好,实则暗藏陷阱——当需要正式部署时,你别无选择只能依赖Vercel。
Vercel在技术意见领袖领域独占鳌头。其恶名多源于周边的网红群体——这些网红虽如癌症般存在,却如同营销手段般近乎必要。
他们烦人、讨厌、总想获取你的邮箱,但该死的,他们确实能抓住你的注意力。
不,问题不在网红群体,他们只是无关紧要的麻烦。症结在于Vercel这家风投支持公司的商业模式。
他们招揽各大主流Web框架的核心贡献者,在自家屋檐下继续开发。于是Web平台的持续改进,竟在很大程度上取决于Vercel投资者的喜好。
他们假装平等对待所有托管服务商,但看看Next吧,它永远只会为Vercel量身定制。Nuxt何时会遭遇同样待遇?Sveltekit呢?Vercel如今已能对整个SSR市场实施战略布局。无论他们是否动用这种权力,光是掌握这种权力就够糟糕了。
这种做法何时奏效过?何时产生过良善结果?从未如此,也永远不会如此。
若你了解开源开发者,便知他们付出海量劳动却收入微薄。Vercel为他们提供了既能获得丰厚回报又能持续贡献开源的途径。我不认同“Vercel=邪恶”的论调,仅因其雇佣员工便断言如此。
你使用“SSR市场”这个术语本身,恰恰印证了Vercel通过技术意见领袖进行营销的成效。SSR领域根本不存在你所指的那种市场,有的只是网络托管服务。
工程世界远不止于JS框架的 Relevance Wars。顺带一提,不仅Vercel在推动这种框架论述,其他玩家也在复制其营销策略:借助技术意见领袖/开源开发者向开发者推广框架/技术栈,再由开发者推动其进入企业技术栈。
这种模式通常对初创公司无效,但Vercel证明了其可行性。
于是涌现出无数轻量级框架封装方案,围绕知名技术如 supabase 与 Postgres、Upstash 与 Redis、Vercel 与 AWS 层层包裹。
我同意,不理解本帖为何充满恶意。
我认为他们的付费托管服务原本相当出色,直到他们将每月20美元的套餐改为“费用随用量浮动+每月发送10封晦涩流量通知邮件”的模式。这才是让我放弃的原因——并非因为涨价,而是服务变得不透明、不可预测且令人烦躁,完全背离了最初的无忧体验。
即便你属于第一类用户,我也难以相信使用Vercel和SSR就能解决性能瓶颈问题。
当人们还在搞那些疯狂操作(数兆字节的捆绑包大小、数十次数据库往返的低效API调用等)时,做好基础的性能分析、优化和简化工作,显然比转向更复杂的架构更能解决根本问题。
> 这些问题的半数根源在于对代码实际运行位置的相对误解。
我曾认为无处不在的JavaScript是优势,而这恰恰是我现在认为它是糟糕主意的理由。
我们公司采用Inertia.js + Vue方案,体验显著提升。既保留了现代前端渲染的全部能力,整体架构却简单得多。路由完全在服务器端处理,无需通用API。(注:Inertia也兼容React和Svelte)
最初尝试Nuxt时简直一团糟。最终你需要管理_两台_服务器而非一台:实际的后端服务器,以及前端专属服务器。由于必须搞清楚代码实际运行位置的种种复杂逻辑,整个系统变得极其繁琐。
现在则简单明了:PHP代码在服务器端运行,JS代码在浏览器端执行。无需质疑这种划分带来的巨大便利,对我们而言堪称福音。
请注意,几乎所有鼓吹边缘执行JS的人,背后都有基础设施要推销给你。
这种技术被定位为解决企业内部困境的方案——当前端与后端开发者各执一词时,电商/产品团队需要逃生通道来构建自己的无状态后端函数。
身为后端开发者,我需要为活动搭建临时网站。当时觉得这是学习前端开发的绝佳机会。
浏览了二十种主流前端框架,被SSR/CSR等概念搞得晕头转向后,最终选择了Nuxt。当时心想:太棒了!Vue框架本就深得我心,这套方案似乎能让Vue应用开发更轻松。结果大错特错。当我将其与Supabase+Vercel集成时,遭遇了无数随机问题,差点直接放弃改用Squarespace搭建。
很想知道你遇到了哪些具体问题
具体体现在哪里?语言切换(尤其是PHP)似乎更头疼。是strlen($var)还是var.length?还是mb_strlen($var)?
你是否从PHP输出过JavaScript?
我最关心的其实是如何避免JS和PHP逻辑重复?验证逻辑尤其如此,但我发现业务逻辑也会在两者间泄露。在Next中统一处理就避免了这个问题。
说得像个中层管理者。
谁会从PHP发送JavaScript?何必在意重复几行JSON转换和空值检查…反正现在所有代码都一样。
切换语言?你根本无法直接使用大部分原生JS功能。就连split()这种基础操作都存在大量诡异bug,导致大家最终还是得依赖工具库来编写代码。
没错,基本就是这样。Vercel试图通过结合React服务器组件、部分预渲染、边缘服务器、流式传输等技术来优化性能。他们许多看似奇怪的设计和API决策本质上都源于此。如果你需要这些功能,它们的存在很有价值。但你也可以通过在边缘函数中实现部分SSR来达到相当好的效果。
我更倾向于认为这是多年同时受简历优化与工作保障驱动的开发所形成的终极产物。
用Brad Troemel的话来说,这就是LIRPslop(低效冗余设计)。
正如其他讨论所言,这些正是SSR框架数十年来具备的分层特性,或许人们该学会避免将所有场景都用单页应用解决。
这正是应用路由器的核心问题。它让代码运行位置变得极其难以追踪。页面路由器不存在此问题。
当真如此?只需查看文件开头的“use client”声明即可。
> 那么边缘语义确实有助于解决延迟问题
嗯…你用Node构建整个应用,通过React加载UI,在顶层堆砌一层又一层动态逻辑(说实话,若非Next.js演示过这些复杂架构,我根本不信有人能让它们正常工作),还放弃标准CDN方案,现在却指望分布式执行解决延迟问题?
> 否则,还是走老路吧——要么坚持用react-vite的SPA方案,要么像Rails那样做常规SSR。
请用成熟的方式编写SPA:选用适合API开发的语言框架(Rails、Spring或微软今年给.NET网络技术起的什么新名儿都行),前端则采用TypeScript。
尽管2015年无数JavaScript开发者学会了“等价”这个词,但前端与后端紧密耦合绝无必要。
前端后端使用同一语言并置于单仓库,对我而言是惊人的效率提升。当然,若前端后端团队规模庞大且独立运作,情况可能相反。但若你只想快速交付…我绝不会选择其他方式。
单仓库架构支持任意技术栈组合。
前端与后端可采用相同语言编写。
无意贬低你的高效方案,但Next.js确实将前端与后端紧密耦合,这点毋庸置疑。
> Next.js将前端与后端紧密耦合,这点毋庸置疑。
我质疑这个说法,因为它有误。NextJS服务器无需强制连接后端数据库,它仅需与内部API(即“真实后端”)交互即可。你可以将NextJS服务器与独立API项目置于同一单仓库中,Next甚至可仅用于执行优化数据传输或渲染缓存(作为无头CMS的头部组件)。虽然看似奇怪的选择,但你甚至可以构建近乎纯粹的SPA,让Next仅负责服务客户端组件。耦合紧密程度完全取决于实现者。
我确实不再使用next.js了。这正是我的做法。
完整堆栈的丰富模式(而非贫瘠的损耗性JSON Schema或其他语言无关模式)对长期质量和开发速度至关重要。它能彻底规避拖累团队的各类缺陷。Zod 4、ArkType和Valibot都是优秀选择。
这恐怕是Web开发固有的问题。JavaScript开发者以为自己登上了编程巅峰——毕竟他们拥有类型系统,还能通过强类型检查实现某些收益,这些检查本该应用于网络调用。
然而到了某个临界点,你不如干脆放弃编写Web应用,转而开发运行时略显蹩脚、不够精确的应用程序。这种应用既缺乏速度优势,又存在诸多缺陷。
更重要的是,你将失去网络最根本的核心价值——互操作性。虽然其他语言也能被改造为支持你的特定对象方言,但困扰其他类型系统的相同问题终将困扰你的系统。
这又回到了我的核心观点:将头埋进沙子里宣称世间别无他物,并不能真正解决问题。
你完全错失了关键——这是任何序列化通信的固有特性,其收益远超类型系统。Protobuf之流及所有现有类型系统,都无法与运行时能力及保障相提并论。
同意。更新按钮内边距就得重新部署整个后端,这确实有点疯狂。
但这问题不同吧?你可以用TypeScript同时实现Express后端和React Vite前端,放在单仓库里。
你是在按CPU周期付费用电脑吗?
我不认同。前端后端使用同种语言实在太便利,我绝不会退回到旧模式。宁可将前端编译为WASM,也不愿引入语言不匹配的问题。
我以前用Django时,因为必须在JS和Python中重复实现所有功能,导致各种问题层出不穷。
那HTML呢?你是否通过JS编写HTML?若非如此,你其实已在使用多种语言。
> 前后端紧密耦合毫无必要
我给你一个理由:Gel [1] 及其强大的TypeScript查询构建器[2]。
[1] https://www.geldata.com/ [2] https://www.geldata.com/blog/designing-the-ultimate-typescri…
或者干脆别写SPA。直接用后端语言/框架发送超媒体,搭配HTMX + Alpine或Datastar,就此收工。
我最近在尝试HTMX,感觉简单多了。
> 这些问题的半数根源在于对代码实际运行位置的相对误解。
观察其他语言,此类多线程问题通常通过标准库提供独立上下文或同步包(处理互斥锁和原子操作)来解决。
而这正是 Node.js 和浏览器端 JS 环境中完全缺失的:一个能避免这些陷阱的标准库,它能强制要求下游包和库提升质量。
这和多线程没关系吧?
完全正确。这些属于运行时执行上下文问题。其他框架(以及其他语言)确实会强制处理这类问题,但我认为next.js的困难在于另一个层面——源于文档不完善,以及为支持开发环境、Node.js服务器和边缘计算而设计的内置抽象机制。
虽然不是楼主,但那篇文章不就是讲大量异步/等待上下文问题吗?
如果中间件API的handle()方法能提供类似context.Context参数,文中描述的大部分调试问题不就迎刃而解了吗?
感谢反馈。我们深知中间件在开发体验方面存在诸多细节问题。15.5版本在支持Node运行时[1]方面取得重大进展,解决了用户长期反馈的诸多问题。
若能重来,我会将其命名为路由中间件或路由处理器。这是路由阶段的特定钩子,可传递至CDN边缘供专业服务商使用,也算是一种高级逃生通道。
鉴于原帖提及日志记录,值得说明的是:为实现可观测性,我们已采用OpenTelemetry框架,并制定了instrumentation.ts规范[2]
[1] https://nextjs.org/blog/next-15-5#nodejs-middleware-stable
[2] https://nextjs.org/docs/app/api-reference/file-conventions/i…
感谢回复。不过…
> 既然楼主提到日志记录,值得说明的是:为实现监控与可观测性,我们已采用OpenTelemetry并制定了instrumentation.ts规范
这听起来像是对笨拙日志机制的解决方案,竟是叠加另一层复杂架构。难道每个应用都需要OpenTelemetry吗?为什么 logger().info() 不能以合理的方式工作?这难道不是个简单的问题吗?其他所有语言和框架都能做到!
> 为什么 logger().info() 不能以合理的方式工作?
我认为OTEL作为无供应商依赖的方案相当合理。若需控制台日志器,本地开发调试时可使用控制台导出器[0]。况且Next框架旨在简化生产级应用构建,通过OTEL实现标准化o11y方案是值得的权衡?
若你认为这过于繁琐,或许你本就不是该框架的目标用户
[0] https://opentelemetry.io/docs/languages/js/exporters/#consol…
因为让NextJS易于运行和监控从来不符合他们的利益。正是这种难度促使那些将NextJS部署到生产环境的人转向他们的平台。他们的目标是提供更令人印象深刻的预优化方案,这些方案会让技术栈更加复杂,使得自行运行NextJS并实际使用这些方案变得更加困难。
再次强调,为什么需要如此笨重的工具?
多数框架都自带强大的日志工具,比如PHP领域的Monolog。
OTEL的重量级体现在哪里?其核心只是生成结构化日志的标准,外加一些导出/收集规范。真正的重量级在于具体实现,不同技术栈的实现差异很大
PHP中甚至存在Monolog处理器——两者并非必然互斥
https://github.com/open-telemetry/opentelemetry-php/blob/mai…
首先,鉴于当前舆论普遍负面,我想强调Next.js在功能实现层面其实相当出色。你们打造的这款软件已为数百万网站提供支持,实属卓越成就。
我认为负面评价很大程度上源于详细文档和参考资料的严重缺失。现有文档主要罗列功能清单,却未说明使用方法、执行机制、常见陷阱及注意事项等关键内容。
文档虽力求对新手友好,却忽略了API在不同执行环境中的细节差异,更未涉及服务器环境中使用React的衍生复杂性等问题。
这已成为当下众多项目的通病——常忽略所有细节与微妙之处。撰写优质文档实属不易,在用户友好性与细节深度之间寻求平衡尤为困难。
继续努力
> 在用户友好性与细节深度之间寻求平衡确实不易。
感谢指正!确实,当经验使某些内容变得不那么明显或无需进一步关联时,文档编写就更具挑战性。这是需要持续改进的领域。
> 该文档旨在为新手提供友好易懂的指引,但确实缺乏对特定API运行环境细节的阐述,也未涉及在服务器环境中使用React衍生出的复杂性等问题。
我认为文档在处理这个特定主题时存在一个假设:将Edge运行时(引入中间件时)单独列出,暗示其可能在另一台计算机上运行,同时也传达了它不与底层渲染服务器共享全局环境的信息。
我会进行一些更新来进一步明确这一点。
> 文档主要说明功能存在性,而非使用方法、执行机制、常见陷阱等细节。
您是否有更多相关示例?我正在完善 revalidateTags/Paths、布局、fetch、useSearchParams 等钩子、Response.next 的陷阱等内容。
我知晓原帖提及的问题未获回应,但这种趋势正在改变。若您发现/记得类似情况,请务必提交文档问题报告,附上文档页面的具体位置及困惑点——过去数月我们一直在处理这类问题。
作为运行了8年的框架,版本号已达15.x,您不觉得这很成问题吗?假设遵循语义化版本规范,这意味着15个版本都是向后不兼容的重大升级?
我们的升级过程大多相当顺利。虽然无法通过CI验证Dependabot合并请求,但通常只需运行自动代码转换工具即可完成,不过我始终会仔细查阅发布说明和迁移指南。对于应用程序的核心架构,这种做法似乎合理。
我没用过next.js,但看起来他们主要采用自动/代码转换迁移方案
`npx @next/codemod@canary upgrade latest`
我不认为这是语义化版本。
我认为是,正因如此它仍处于不稳定状态,每年有两次重大变更。
既然你在这里——我也插一句。
本文作者未能理解领域差异,却试图用统一方法调用所有函数。当然行不通。
Next.js的谬误在于试图混淆本质不同的函数域。停止这种做法就能解决问题。文档只会徒增混乱。将边缘计算、服务器端渲染、Node环境和客户端功能强行融合只会制造混乱,这种尝试只会导致框架复杂性层层叠加。
听起来你大概不太喜欢 React 服务器组件,毕竟混合领域正是它的核心价值。
混合领域很棒。但当框架能力不足导致某些层级无法实现日志记录时,这种混合就糟糕了。
确实,其实是Reddit上某位评论者推荐我走监控工具路线的。
我花在Next上配置OpenTelemetry的时间差不多,虽然标题可能不同,但经历过这事后我大概也会写篇博客。
这不怪你,但基本上每个我安装的OpenTelemetry包都标注为实验性功能。这种情况让人不敢把代码推到生产环境。
更糟的是,我花了很长时间都搞不定pino的监控配置。虽然最终解决了,但过程相当痛苦。
首先,必须将pino添加到serverExternalPackages中,否则OTel监控无法生效。
其次,自动监控对导入顺序极其敏感。而且不知为何,只有pino的默认导出项会被监控。这个又花了不少时间才搞明白。
模块局部变量的工作方式与预期不符,我不得不改用globalThis。
经历这些后,我仍遭遇了这个问题:https://github.com/vercel/next.js/issues/80445
虽然能用,但配置过程相当麻烦。当然,我选择的是手动路由器(即未使用vercel/otel)。
既然最终决定支持规范的服务器端中间件,为何仍限制为单个中间件函数?为何不提供其他成熟服务器实现普遍支持的中间件链功能?
请将 middleware.ts 视为根中间件。你完全可以在其中创建自己的链式处理(这其实很简单)。我的意思是,即使 Next.js 实现了该功能,最终效果也相同——总会有某个根节点存在。
这并未回答楼主的问题。
人们期望“中间件”具备特定含义和工作方式。
express/koa提供use()链式调用。next.js提供单一根节点,但你完全可以自行链式组合。语义相同,只是需要手动连接。
};
根节点已给定,链式调用很简单。这就是中间件。
在我看来这个实现一点都不简单——需要追踪中间件的注册位置,reduceRight的用法也不直观。
我期待框架能标准化这些细节并打磨所有尖锐边缘——这正是我选择框架的初衷。
reduceRight不过是段精巧的函数式代码高尔夫。其核心逻辑是:空中间件列表链式调用返回“OK”响应,首个中间件接收的函数调用时会执行剩余中间件链,依此类推。若改写为for循环或直接递归实现,逻辑就清晰多了。
(我的用户名从未如此贴切!)
Vercel/Next是否会出台关于页面路由/API路由的官方政策?这些功能是否会长期支持?由于在应用路由器上遭遇过多次极其糟糕的体验,我启动Next项目时仍坚持使用页面/API路由。
> 鉴于楼主提及日志记录[…],我们已全面采用OpenTelemetry
我实在厌恶这种流程。用户提出实际需求反馈,开发团队审议后耗费大量时间构思最完美的抽象方案,将问题泛化为宏大基础系统,最终推出极度复杂的“最佳”解决方案。这种纯粹主义委员会认证的方案,技术上确实能解决用户需求——只要付出大量努力。但重点早已偏离。实用主义被抛诸脑后,只剩下发明有趣的抽象谜题。
而用户真正想要的,不过是简单记录日志而已。
并非说当前情况完全如此,但评论中的措辞让我深有共鸣。
> 别忽略关键事实:你既不能使用多个中间件,也不能将它们串联起来.
这肯定不对吧?
https://nextjs.org/docs/messages/nested-middleware > 若存在多个中间件,应将其合并至单一文件中,并根据传入请求来设计其执行逻辑。
天哪,这不可能是真的。
我理解正确吗?他们竟主张放弃将代码拆分到不同文件的结构?这是NextJS的范围问题导致难以使用多文件吗?作为框架的声明实在荒谬至极。
这里指的是用于定义中间件函数的单一文件。你可以向其中导入任意内容,并自行决定如何“将[你的中间件]组合成单一文件”。
作为JavaScript开发者,手动合并/整合中间件完全违背了中间件的设计初衷。我更愿意将其称为回调函数。
比起在应用初始化阶段(如Express框架)四处散落app.use/router.use调用,我更倾向这种方案。
我总觉得某些决策是基于Vercel自身利益而非框架最佳实践做出的。
我不认为这个具体案例对Vercel有何利弊。但需要自行编写composeMiddlewares函数(或从各种讨论帖中寻找现成方案)确实是糟糕的开发体验。
他们在讨论帖中已确认过:这是架构层面的决策,只在边缘节点运行中间件。
这与我回应的投诉无关,问题核心在于只能提供单一中间件文件。这纯粹是开发体验问题。你完全可以自行开发composeMiddlewares函数,从不同模块导入各类中间件功能——只是需要自行承担实现成本,并为每个组合式中间件重新实现基础功能(如匹配正则表达式)。
初见Next.js时,我立刻联想到Meteor.js。我确实投入精力学习并完成过个人项目,但很快意识到它既过度抽象又缺乏灵活性,导致难以突破原型阶段。
但这类方案之所以屡屡出现,只因它们提供了一点:自包含/“内置电池”。前几天Hacker News上就有个Laravel与Symphony的讨论帖,本质相同:一旦复杂性介入,系统就会崩溃。
若将这些方案与让NodeJS/React SPA迅速爆红的旧模式对比:自助餐式工具/库。你本质上是用零部件打造专属瑞士军刀。由于所有组件都自成体系,它们必须定位在极低抽象层级(如React作为组件库、HTTP+Express作为后端路由器、Postgres作为数据库)。
这种方法虽有诸多缺陷,却能保持高度灵活性,避免巴别塔式过度工程化——即层层堆砌的复杂架构。复杂性并未消失,而是以众多平级层级呈现。当某层运作不佳时,替换为其他层级反而更具可行性。
“开箱即用”模式广受欢迎实属必然——将众多略有兼容性差异的工具库拼凑起来确实令人头疼。这种模式确实需要经验更丰富的人员来完成整体配置。
问题在于我被ASP.NET宠坏了——尽管它在开发者社区(尤其是初创圈)背负诸多负面标签,但其设计确实极具匠心。
它提供高度“开箱即用”的解决方案,同时允许随时突破框架限制进行覆盖。我从未陷入与框架对抗的困境。
我同样钟爱Blazor Server和Blazor Webasm,它们支持用C#编写前端。Blazor Server特别适合内部管理面板类应用,Blazor Webasm则擅长SaaS应用,而传统的服务器端渲染方案对其他场景依然游刃有余。
强烈建议对现有Web框架感到困扰的开发者尝试Blazor。它如今具备极强的跨平台性(十年前的最大缺陷是难以在Linux运行,如今情况完全逆转——反而更难在Windows上运行),同时兼具高速性能与易用性。虽然需要时间建立设计思维模型,但一旦掌握核心逻辑,遇到限制时就能快速找到覆盖方案(说实话,相比其他框架这种情况相当罕见)。
C#总能引发如此强烈的反感,而TypeScript却备受青睐,这现象真奇妙。它们明明是…由…同…一…群…人…创造的
我同意人们确实需要更新他们对.NET现状的认知。我在Linux上使用过它,体验非常棒
这或许源于C#的标准编写方式过于强调面向对象(暂且如此称呼)。TypeScript允许你编写处理常规类型对象的普通函数,这种抽象程度恰好满足多数人的需求(仅需1-2个类处理如
BTree之类的情况),同时保持了人们期望的类型化程度。这是因为C#采用名义类型(C语言风格),而TypeScript采用结构类型(JavaScript风格)。
https://en.wikipedia.org/wiki/Nominal_type_system
https://en.wikipedia.org/wiki/Structural_type_system
尽管命名类型并不必然意味着面向对象风格(重度继承),但它是(重度继承代码的)必要前提。
二者的区别并非非黑即白,但(现代)TypeScript(以及Flow)高度侧重结构化类型,而C#则侧重名义类型。事实上,组合与继承的根本争论,本质上就是关于如何使类型以更结构化的方式运作。
更可能的情况是他们从未尝试过C#,却对其抱有强烈偏见。若需实现,C#同样能编写出高度函数式的代码——它与TypeScript一样是多范式语言。
当然它“可以”这样实现。但主流库的架构并非如此。而主流架构决定了绝大多数商业应用的结构。人们使用语言的方式定义了语言的本质,无论它是否包含五花八门的替代范式。你无法否认C#在多数场景下不具备企业级特性。而在JS中,仅凭函数和选项对象就能构建在完整且极受欢迎的库生态之上。
Blazor毫无优势可言。请.NET前端开发者坚持使用JS,日后你会感谢我的。
你遇到了哪些问题?
尽管方法老派,但仅凭原生JS和.fetch()请求就能满足用户所有需求。
我一直在尝试Blazor,目前体验很棒。但正如所有技术一样,我知道它并非完美。
WASM性能存在问题。大型数据网格的渲染性能不佳,首次加载时间也极其糟糕——超过50MB的有效负载。
Blazor服务器端采用WebSockets技术,简直是另一堆麻烦。即便你能承受云成本的激增,也必须应对断连问题。
> 若将这些方案与当年让NodeJS/React单页应用迅速爆红的旧模式对比:自助餐式工具/库。你本质上是用零部件拼装瑞士军刀。由于所有组件都自成体系,它们必须定位在极低抽象层级(如React作为组件库、HTTP+Express作为后端路由器、Postgres作为数据库)。
我开发过几个Angular应用,上述体验/配置对我而言完全陌生。虽然它本质是框架而非库,但设计极为精妙(至少从Angular 2开始如此;我最近的组件项目使用的是Angular v20)。框架内置了绝大多数常用功能(我仅添加了NGXLogger用于日志记录),其抽象层设计优雅,与后端服务模式高度契合(服务封装库,组件依赖服务)。RxJS的学习曲线可能稍陡,但掌握基础后能带来巨大价值。至少在典型单页应用中,我极少与框架产生冲突。此外其文档和教程体系非常出色——我通过英雄之旅应用[0]入门,不过angular.dev[1]似乎已成为v20文档的新家。
[0]: https://v17.angular.io/tutorial/tour-of-heroes
[1]: https://angular.dev/
不,Laravel恰恰证明过度设计的抽象有时也能做得恰到好处。它在生产环境中运行完美,我从未后悔使用它。
我不认为Laravel过度设计。它更像是由轻量框架层整合的Web应用构建工具包。即便大量使用反射机制,其代码库仍极具可读性,且与Web应用领域紧密关联。
> 拼凑那些彼此略有冲突的工具库实在令人抓狂
这正是我的日常。我们团队规模小,我的职责就是维护系统更新。耗时惊人——既有依赖关系复杂的包,也有五年前就停止维护的包。
没错,我深有体会。如今React单页应用的情况好多了,但依然令人头疼。
事实上,在前端领域,若没有那种包罗万象、无所不知、无所不能的单体式“全功能框架”,唯一解决之道就是标准化——而这只能由浏览器推动。比如由浏览器自身决定打包工具的工作方式,而非让它们具备可扩展性。
这种工具地狱不仅限于浏览器前端,在游戏开发中同样普遍。诸如虚幻引擎这类“内置电池”的庞然大物,因其体量庞大且结构复杂,反而让问题排查变得极其困难。游戏引擎本质上也是打包工具,它将资源与代码整合为可在平台上运行的系统。
浏览器标准的进步正逐步消除对客户端JS的大部分需求,因此标准化打包器概念实属倒退。
当今绝大多数Web开发项目根本不需要SPA框架,所有痛苦都是自找的,收益却微乎其微。
这些工具仍有适用场景,但你的项目属于此类场景的概率正持续降低。
浏览器标准已大幅完善,填补了当初催生React的诸多技术空白。
我认为打包工具永远无法被取代,因为模块化编程的瀑布式请求问题(一个模块的导入依赖引发另一个模块的导入)根本无法解决。或许可以制定某种标准清单规范,告知浏览器哪些代码需预加载、哪些需延迟加载。但要高效生成此类清单,至少需要部分复现打包工具的现有功能。
代码压缩同样是个难题,或许可在CDN层实现即时压缩+缓存,但这本身又会衍生出新的复杂性。
单页应用框架仍有其价值,甚至可能更普及,但我预见WASM将为非JS语言栈打开大门。不过这些语言同样需要打包工具,且某些语言天生不支持最小化二进制体积和延迟加载代码——试着将C++编译为WASM,最终得到的.wasm文件往往超过10MB。
> 浏览器标准的进步正逐步消除对大多数客户端JS的需求
我之前表述可能不够清晰。
若你指的是模块加载中的瀑布式请求,那你完全误解了我的观点——你发送给客户端的JS量很可能比实际需求多出几个数量级。
值得深入研究过去5-10年浏览器的新特性,问问自己:当前需求是否仅凭原生HTML和CSS就能实现?必要时再添加少量JS填补空白即可。我们团队每个项目通常只有200-300行JS代码,这种规模根本不需要打包工具或模块管理。
> 搭建环境确实需要经验丰富的人员
关键不仅在于经验——更在于前期投入的时间成本及后续维护成本…而这一切究竟为了什么?人们往往严重低估了这种投入所需的精力
亲身实践过两种模式后,我真心认为Rails能带来十倍于Node拼凑式库组合的生产力提升
唯一可能遇到的灵活性问题,是当你根本无法认同框架的核心理念时。比如若你厌恶ActiveRecord模式,就该敬而远之
所谓“复杂性带来崩溃”实为技能问题
>既过度抽象又缺乏灵活性,这让事情变得极其困难
这种现象似乎已植根于那些搞荒谬白板Leetcode面试的公司文化中。他们专门寻找能在睡梦中构思出精妙复杂方案的人才,然后让这些人来干活。面试机制并未筛选擅长简洁明晰的人。于是你得到大批紧循环优化狂人,他们把所有代码都当作紧循环来优化…但若你的产品是普通人使用的库/框架,长期成功需要的是简单性。若你连手动挡都不会开,超级跑车的性能对你毫无意义。
没错,精巧的代码简直是我的噩梦。
只有我写的代码才允许精巧!
确实如此。作为React的坚定拥护者,我每天都在使用它,发现从类组件到钩子的转变提供了更优的编程模式。
但每当我使用Next时,总觉得我们迷失了方向。我尝试过许多框架,也喜欢晦涩的编程语言,但不知为何,React阵营推崇的尖端JavaScript框架Next.js却是我唯一会陷入这种境地的体验——半数时间里,我完全搞不懂它的错误提示(如果能收到提示的话)究竟在表达什么。那些耗费在诡异水化问题上的时间,我甚至数不清。
我并非React或Next.js用户。或许有人持异议,但我个人更倾向于通过在传统HTML+CSS文档中适度添加原生JS来最小化JS使用。
当发现简单的Next.js落地页在Firefox中崩溃时,我颇感意外。更糟的是,故障表现为整个页面被黑色背景覆盖,只显示白色文字:“客户端应用程序发生错误”。这种简单落地页无法渲染的情况确实令人惊讶,但当我发现罪魁祸首竟是JS前端框架时,又觉得这不过是意料之中。
或许对拥趸者而言合乎逻辑,但对我们这些未跟风者来说,确实令人困惑不已。
> 虽有不同意见,但我个人更倾向于通过在传统HTML+CSS文档中适时添加原生JS来最小化JS暴露风险。
若您不介意我问一句,您参与过哪些类型的应用开发?团队规模如何?项目生命周期多长?您参与开发的时间又有多长?就我个人经验而言,“原生”JS方案在扩展性上存在难以逾越的障碍。我几乎只参与过高度交互的SaaS应用开发,使用大量JS来串联交互逻辑或处理服务器端更新是不可避免的。
我任职过的公司工程团队规模从3人到2万余人不等。参与的项目团队规模则从3人到500-1000人不等(在大型企业中有时难以精确统计)。我参与过纯原生JS、“vanilla” JS、Knockout、Backbone、Vue及React[0]等技术栈的项目。上述技术按代码维护难度递增排序——我列举的顺序也大致反映了代码变得难以维护的速度。
[0] 此处并非我使用过的前端框架/库的完整清单,仅列出我具备足够经验可对其长期支持性[1]进行评估的技术。例如我曾深度使用Ember约一年,但该年份被两个各耗时六个月的项目分割。同样,我虽接触过Next.js,但仅用于几次原型开发,从未将其部署到私有服务器之外的环境。
[1] 唯独Lightning Web Components例外——我虽频繁使用却深恶痛绝,不愿将其与其他技术并列而玷污它们的名声。
Next早就在某时搞砸了自己。凡是经历风险投资周期的项目终将如此。
我为他们的成功和财富感到高兴,但无法再使用这个框架。现在我默认选择Vite,但更希望找到更轻量级的方案。
我认为Next.js中的“中间件”称谓并不准确。它本质是请求到达应用前运行的边缘函数——快速头部检查、路由处理及其他轻量级守护机制。它运行于边缘运行时环境,而非应用服务器。
博主似乎混淆了边缘运行时与服务器运行时。它们是受不同约束条件制约的独立环境,各有取舍。
我最初也因同样原因对Next.js感到困惑:必须明确各组件的运行位置(边缘/服务器/客户端)。由于全部基于JavaScript,边界往往模糊不清。因此建立清晰的思维模型至关重要。但将这种复杂性归咎于Next.js,无异于责怪工具箱里不止一把锤子。
> 但将这种复杂性归咎于Next.js,就像责怪工具箱里不止一把锤子。
核心问题在于这种复杂性实属自找。若你接触过任何语言的其他框架,“中间件”这个术语的含义本应清晰:它指在请求处理器之前被调用的函数或函数列表,且默认这些函数在同一进程中运行。而Next.js将其部署在边缘节点且仅允许单个中间件的做法,打破了这一基本假设。更重要的是,多数应用根本不需要这种额外复杂性。回到工具箱的比喻:更多工具意味着更高复杂度(和更高成本),因此你不会仅仅因为“可能需要”就添置新工具,而是在真正需要时才添置——边缘功能同样如此。若Next.js希望允许在应用调用前执行边缘代码,这无可厚非,但应采用选择加入机制——无需时无需顾虑,且不应冠以“中间件”之名。
是的,“中间件”这个术语确实不恰当,这一点毋庸置疑。
> 你不会仅仅因为可能需要某个工具就去获取它
不,但你选择框架正是因为它“内置电池”:许多应用都需要这些工具。你不必全部使用,但它们的存在能降低实际使用时的阻力。
> 若 Next.js 希望允许你在应用被调用前在边缘运行代码,这无可厚非,但应采用选择加入机制
它本就是如此。除非添加 middleware.ts,否则没有任何代码会在边缘运行。你可以完全不使用中间件构建完整应用。考虑到作者花费大量时间寻找替代方案并撰写文章,竟未能承认这一点,着实令人惊讶。
正如我在另一条评论中所述
> 若你刚了解Python中的包/模块概念,就试图不费脑力地将其套用到Go语言中,自然会抱怨Go语言糟糕。使用任何技术时,都应具备该技术的相应认知。
这个类比并不成立。中间件是Web框架领域确立的术语,而Next所处的领域同样如此。虽然Next的实现形式看似中间件,却违背了人们对中间件的核心认知。引发混淆实属正常。
我倒不认为这是命名错误,而是对术语的严重滥用。中间件在Web应用领域有长期确立的定义,若指代完全不同的概念就不该使用这个词。
我也正在使用Next.js和应用路由器,非常喜欢它。
问题可能在于Next.js极大简化了前后端切换流程,导致人们误以为这部分已被抽象化处理。
实际上这是相当复杂的系统,你需要自行应对这种复杂性。但复杂性并不意味着效率低下。
前后端明确分离的系统更易于理解,但实现起来也更繁琐。
因此,对于熟悉 React 并想转向 Next.js 的开发者,我要提醒:即使你精通 React,Next.js 仍有相当陡峭的学习曲线,某些内容需要亲身体验才能领悟。但一旦掌握,它将成为让你轻松穿梭于前后端之间、无需过多麻烦的便捷系统。
致那些给我评论点踩的人(而且人数不少):请说明理由。
我乐于学习进步。这里很多评论只是毫无根据的负面评价。让我们进行真正的讨论吧。
终于遇到个懂行的人。
若你连Python的包/模块是什么都搞不清,就想不费脑子直接套用到Go上,自然会抱怨Go不好。使用任何技术都该具备基础认知。
他们转向应用路由器时,简直像让训练营毕业生去“改进”Express API——后者本已相当成熟,其可组合的俄罗斯套娃式架构与servlets、Rack、Plug等所有服务器接口的设计理念高度契合。
除了糟糕的中间件API,他们还做出令人质疑的决策:用cookies()和headers()这类全局函数取代请求参数机制。
或许存在某些我未察觉的设计限制,使这些决策看似合理,但这确实像他们抛弃了所有来之不易的经验教训,决定重蹈覆辙。
我认为对流式处理的执念是新限制的主要成因。再加上要兼容最低标准的边缘运行环境。
他们之所以全面押注流式处理,根本原因在于系统持续进行海量数据传输。正如Sean Goedecke在API设计文档[0]中所述:技术底层薄弱的产品,几乎不可能构建出优雅的API。我认为Next.js正面临相同困境,所有这些怪异的接口都源于底层架构问题。
[0] https://www.seangoedecke.com/good-api-design/
或许从托管业务盈利角度看,这确实是种好设计
我总爱在新项目里尝试新工具。用过客户端Express+React、Angular、Vue、Next、Nuxt,服务器端用过Go、.NET、Node、PHP等。总能发现不同方案的优缺点,欣赏它们各自的特色。
唯独Next是个例外。我们用Next搭建过相当大型的应用,从头到尾都痛苦不堪。它的每个部分要么怪异、要么迟缓、要么繁琐,甚至完全荒谬。
我们仍在维护这款应用,而它是我此刻唯一深恶痛绝的“东西”。我明白这个生态系统相当完善,鉴于其极高的人气,用户似乎对结果相当满意。但我的个人体验却糟糕得无可救药。这很奇怪。
有人知道Vercel的邮寄地址吗?我想寄张“祝你在学校过得愉快!”的卡片给他们,毕竟这个项目明年都够资格上小学了:https://github.com/vercel/next.js/issues/10084
他们试图解决的根本问题非常棘手。当差异变得重要时,统一服务器端和客户端代码注定会引发混乱。
个人更倾向于明确区分服务器端代码、客户端代码和共享工具库的架构。但这种方案需要更强的类型安全意识,且可能吓退那些宁愿在运行时出错也不愿在编译时出错的大多数开发者。
你的观察具有普遍性。调试“魔法”框架往往比从基础构建和合理设计起步更复杂。额外好处是依赖更少、构建问题更少,页面或二进制文件更轻量。
说得好。
完全赞同,TRPc在这方面表现出色。
近期开始用Angular v20开发客户端Web应用。该应用相当复杂,包含约40个组件(多数嵌套),7-8个“页面”,数据仪表盘,多表单,持续从服务器更新数据等。应用内存在大量数据流。
迄今为止体验绝佳,尤其考虑到我的开发背景:
1. 我是资深开发者,主要从事Python开发、后端服务器、桌面应用、数据应用等领域,几乎没有接触过前端技术。
2. 我持续关注前端系统的_概念_、模式_与_架构,因我对设计和优质UI极感兴趣。
3. 我现阶段能用Vue、React、Angular及原生HTML/JS开发基础Web应用。
借助Angular信号机制及存储、服务、依赖注入等概念,我能轻松处理数据流,以一致方式为应用添加功能,并相对便捷地调试和追踪错误。
此前我研读过NextJS文档、Remix文档,并学习过使用Vite开发React应用的教程,但始终觉得这些内容如同令人困惑的迷宫。或许是年岁渐长(即将步入不惑之年),但二十五载编程生涯让我得以在Angular应用中轻松构建理想架构。
更重要的是,该应用将投入生产环境,对我的工作至关重要。这个影响深远的决策令人欣喜的是,它带来了超乎预期的满意效果。
或许选择React也能达到相同效果,但在深入评估技术栈的过程中(我曾用React、Vue和Angular分别测试应用核心机制),Angular的开发模式让我倍感亲切。
我参与过许多大型Angular应用开发,始终热爱使用它。虽然接触过大量React项目,但读过文档/示例/博文后,我对Next.js避之唯恐不及。
从JavaScript和JS/CSS诞生之初,我就深耕于ES6之前的时代,因此当工具契合需求时,我确实偏爱KISS风格的极简网站。但这绝不适用于大规模项目。Angular作为框架而非库,正是为此类场景而生。我欣赏它明确的立场(无需纠结“这个项目用什么路由器?”),如今开发任何项目(哪怕小型站点)我都离不开TypeScript。
各有所好。
> 总体而言,Next.js拥有大量抽象层,而99.9999%的项目根本不需要这些。
这适用于多数由多人协作的软件项目。Joe Armstrong曾提出解决方案:我们只应开源函数,让开发者用这些开源函数自行组合实现。我开始认同他的观点——与其使用“框架”,不如采用这套函数集。
一针见血。这正是核心症结所在。
功能量的多少并非问题所在。
症结在于这些功能并非可组合的函数与类集合,而是控制反转的“框架化”黑盒,其行为逻辑难以追溯。
我确实觉得很有意思,他一边这么宣称,转头却写了整篇博客说Next.js不行,就因为它没按他的喜好实现某个特定功能!
Unix哲学?
每个DOM元素都是一个文件。对于无法完美适配单维字节流的数据,你完全可以直接用JavaScript调用ioctl操作。
每次看到简单静态网站或开发者作品集挂着Vercel域名,我都会忍不住想:你真需要这个吗?
React和Next.js的发展方向,是让你能精细控制代码的运行位置与时机。
旧模式是:服务器接收到请求时执行代码,向客户端发送响应,然后客户端执行响应中的代码。而现在的情况是:部分代码在服务器端运行,部分在客户端运行,这些客户端代码又会调用服务器端的代码,所有这些操作都可能发生在服务器开始发送响应之前或之后。再加上Vercel等平台还提供了边缘函数作为额外变量。
这种设计虽然很酷,但也极大地复杂化了思维模型。
我正准备用Next.js启动新项目…有人愿意提些建议吗?
我即将开发一个电商网站(3-5万款海报印刷设计,即无库存模式),倾向于采用Django后端(因熟悉该框架)和…某种服务器端渲染前端方案。我并非专业前端开发者,但打算借此机会学习。这篇文章显然让我对选择Next.js缺乏信心——各位能否提供建议或分析不同方案的优缺点?
目前我看到的SSR方案有:
– Next.js:在AI训练数据中覆盖率高(但近期版本有破坏性变更?不确定),实际使用体验糟糕(根据本文及网上普遍反馈),还强制绑定Vercel?(我几乎不懂这意味着什么)
– SvelteKit:开发体验最佳且易用,但在AI训练数据中可能较少出现?
– Django模板+HTMX:可能存在局限性?项目规模达到一定程度后维护性下降?我不确定。
– 其他选项?
补充说明:若要构建静态生成/服务器端渲染前端,还有[Astro](https://astro.build/)
它支持向客户端推送独立组件,据我所知本质上是部分加载机制。
几乎任何JS框架都能实现独立组件,甚至无需框架——例如Web Components
Katie Sylor-Miller在Etsy任职时命名了这个概念,记忆中最初是将React组件嵌入PHP页面
我倾向纯Django方案。根据描述,项目似乎不需要复杂前端。按需添加脚本即可。若真想学习前端,Next绝对是最糟糕的入门、进阶或终极选择。
若模型输出错误内容,可通过Context7 mcp获取Svelte 5文档验证。
新推出的实验性远程函数解决了SvelteKit的重大痛点,如今我再无理由不向他人推荐Svelte和SvelteKit。
若已熟悉Django,建议直接采用Django并酌情添加HTMX。相比现代JavaScript单页应用,你只需付出零头努力就能获得95%的效益,维护起来更是轻松自如。更不必耗费大量时间调试打包工具、压缩器或ESM/CJS问题。
用你熟悉的工具。先构建后端。在此之前你可能无法确定前端需求。
当明确网站实际内容后,可用CSS美化输出,并通过JS添加必要交互。相较于糟糕的旧时代,浏览器API已基本标准化。直接调用API而非依赖库或框架,能让网站保持精简高效。
你掌握的CSS、HTML和浏览器API知识将在未来多年持续发挥价值。几年后,人们讨论的焦点又会转向某个新晋神奇框架。
至少要完整跑一遍Rails教程。去年我重新认识了Rails(也研究过Django/Laravel),如今它确实令人惊叹。
内置功能如此丰富,开箱即用。
个人偏爱Sveltekit。
虽需学习曲线,但掌握后其简洁与强大令人惊叹。
不过我尚未尝试过Astro和Nuxt。
这听起来像是使用Django的理想方案,可搭配Datastar或HTMX框架,并借助Web组件(Lit/React/VanillaJS)作为交互性强的模块的应急方案。Instagram、Threads、DoorDash、EdX、Octopus Energy等平台都已大规模运行Django多年。
你真该试试Nuxt(一个Vue框架,类似Next.js但更优秀)。
最近被迫使用Nuxt搭建的网页,简直一团糟。
以我九年前端经验来看,电商网站建议直接用Shopify、WordPress+WooCommerce或其他现成工具。晚上之前就能上线,成本不会比从头开发高多少,而且一切都能完美运行(Just Work™),视觉效果也不错(花50美元买个精美WordPress主题就搞定)。
若执意自主开发,Django + 模板系统已足够强大。集成Stripe等支付方案的现有代码库丰富,AI也能胜任开发工作。你也可以考虑研究MedusaJS或PrestaShop。这份清单或许值得探索:https://github.com/awesome-selfhosted/awesome-selfhosted?tab…,但关键在于乐趣。你应该用WordPress搭建,这是个坚如磐石的解决方案,支撑着半数互联网运行,能为你节省海量时间。
若此项目旨在学习新工具,请暂缓AI应用——这会剥夺你深入研读文档、积累领域知识的机会。更要远离Next.js,这套工具设计得荒谬而过度复杂。唯一该学Next.js的人,是那些在开发公司为客户批量生产全栈应用的工程师——前提是客户预算充足能承担Vercel的托管费用。即便如此,我认为他们也该直接用Django+React+Vite+Tanstack组合,顶多考虑用用。
HTMX很酷但我不确定其价值所在。若纯粹为娱乐当然无妨,但请自问:当你的网站上线运行后,接下来一年你都在忙着把东西装箱打印运单,结果2026年感恩节假期前夕应用突然崩溃,而你发现HTMX/nextjs/ 之类的框架已经历两次破坏性变更升级,其依赖的四个库也同样更新,更糟的是你依赖的两个库最新版本因依赖不同Node版本或其他狗屁问题无法兼容——此时你该怎么办?
直接用WordPress吧。
起初我百分百支持WordPress/WC,甚至已开始用它开发,但问题接踵而至。“Just Work(tm)”本是我的期待,现实却截然相反。
– 使用产品变体功能时,单品18个变体,“复制”按钮竟卡顿15秒!后来才明白每个变体都是独立实体,系统竟在创建18个新对象(即便在我的高性能开发机上仍如此卡顿)。试想3-5万商品×18种变体×元数据处理,这种组合怎么可能流畅运行。
– 虽然有插件能绕过产品变体功能实现商品字段扩展和定价规则管理,但要么手动点击操作,要么编写PHP代码与插件对接——这绝非我理想的开发方式。尤其与AI工具的集成效果极差,而AI工具目前已是我的核心开发利器。
– 我不希望产品与页面建立一对一映射关系。这与WordPress(乃至Shopify)的模型并不契合。
总体而言,经验丰富的WordPress/PHP开发者或许能解决这些问题,但既然需要学习新技能,我个人更倾向于掌握规范的前端框架(无论是您提到的任何选项)。善用AI工具也很重要。
感谢你的回复!这让我更有信心坚持使用Django + 模板方案。但根据我的观察及与其他开发者的讨论,WordPress不适合这个项目。再次感谢 🙂
不!PHP只是糟糕,但WordPress简直糟糕透顶。敬而远之。自1998年起从事Web开发(27年经验)。
好吧,那你有什么替代方案?只要避开WordPress那个愚蠢的块编辑器,我从未遇到过问题。但你经验远比我丰富,所以很想听听你的看法。
这才是真正有价值的建议。
专注核心业务:聚焦销售而非店铺技术。选个模板调整以匹配营销需求。
真正有用的技术是:与打印机/印刷厂集成,自动生成订单包装标签,或用AI制作新海报。
htmx不会经历两次破坏性变更升级
https://htmx.org/essays/future/
此外:
> 因此它们依赖于4个不同的库
HTMX没有依赖项。https://htmx.org/essays/vendoring/
> 实际上,你依赖的两个库最新版本目前无法互操作,因为它们依赖于不同的Node版本
HTMX无需构建步骤。https://htmx.org/essays/no-build-step/
或者使用PayloadCMS,它基于Next框架,至少不必处理WordPress的麻烦。
SolidStart
现代开发精髓所在。
你偏要用那些抽象到令人摸不着头脑的东西。
你这么做是为了省时。
结果出问题时就怪别人。
话虽如此,让我传个参数就能在生产环境获取开发日志。这才是作者真正想要的。
让我想起这场演讲https://www.youtube.com/watch?v=SxdOUGdseq4 🙂
许多公司将Nextjs列为岗位要求,招聘信息中随处可见。这几乎形成“React=Nextjs”的默认认知,甚至无需提及React本身。有些开发者对Vercel的商业模式投入了深厚精力。最终,开发团队必须为这些决策承担责任。
我在几个项目中遇到基于Nextjs的问题,最佳解决方案就是彻底弃用它。
最终效果更优美,开发满意度也更高。
若你在面试中敢说曾被迫从某个项目中移除Nextjs,那你基本没戏了!
我曾与一名计算机科学专业的学生交谈,他告诉我React已经过时,没人再用它了。他坚称现在NextJS才是主流。无论我如何解释NextJS本质是基于React的框架,他都充耳不闻。未来可想而知。
我觉得这其实是个绝佳的谈资开场白:“我不得不放弃[流行框架/工具]。”
后续可追问:
– 团队为何决定弃用?- 弃用过程涉及哪些环节?- 用什么替代了它?- 新方案比旧方案优越在哪里?- 如何评估耗费X天/周/月弃用框架的价值,是否值得牺牲功能开发时间?
我认为所有框架和工具都有其适用场景,关键在于把握使用时机与方式。
若企业连这点都无法理解,我绝不会为其效力。
若由我面试,这将为你加分。事实上,讲述这个案例或许能有效筛除那些盲从主义的公司。
我的两分钱:Next.js搭配页面路由曾是个绝佳框架(易学却功能强大)。
我始终用Node.js和Express开发后端API,仅在需要服务器端渲染时才用Next.js做前端。
这种开发模式曾带来纯粹的愉悦。
应用路由彻底改变了这一切,也毁掉了Next.js的好名声:它难学难管,实际优势寥寥无几。
我始终认为Vercel的核心“哲学”存在根本缺陷,他们总爱打造华而不实的表面功夫,内部却漏洞百出。
遗憾的是,开发者们缺乏洞察力,导致如今每个React开发者都不得不接触Nextjs。
除了Vite和Remix,还有哪些替代方案?
Rails和Laravel仍是领域内举足轻重的玩家,且随着时间推移愈发成熟。
> 除了Vite和Remix之外还有哪些替代方案?
我多次看到有人将Vite视为Next.js的替代品。但Vite只是打包工具,而Next和Remix是框架啊?Vite如何成为这些框架的替代方案?
Solid和sveltekit
比Next.js更糟糕的,是成为初创公司MVP项目的Next.js分包商。就此打住,不再回答。
> SvelteKit是Vercel的产品
虽然Vercel收购了SvelteKit和Nuxt.js背后的许多开发者,但我以为他们并不打算亲自运营这些项目,甚至不打算让它们变得相似。有人能纠正我的理解或解释他们的长期战略吗?
Rich Harris一年前在Reddit上回答过这个问题:https://www.reddit.com/r/sveltejs/comments/19ac6lp/concern_a…
目前Svelte仍主要作为独立开源项目进行管理。
我来这里正是为了这个。Vercel早已跨越了投资门槛,不再专注于打造酷炫功能,如今他们显然被管理PKI的项目经理们牵着鼻子走。我热爱Svelte,但可以肯定Rich Harris在会议中常听到这样的说辞:“当然,我知道这很重要,但我要提的功能能让下季度数据飙升…”而这正是滑坡的开始。
考虑到Svelte的营销和文档如今将Svelte本身视为SvelteKit的次要实现细节,这实在难以令人信服。
这似乎是Rich本人坚守的立场。他极力希望开发者默认采用带数据注入的SSR方案,而这只能通过Sveltekit实现。他们采用的新方向——通过更简洁的RPC式数据加载方式取代现有load函数——确实表明SvelteKit正朝着更易用的方向发展。目前看他们并未走向与单一托管供应商深度捆绑的趋势。此外,他们始终采用统一的托管集成抽象层,为所有托管方案提供了公平竞争环境。
作为从MEAN→Java(Spring)→MERN→Rails的迁移者,我实在难以理解JS社区究竟重造了多少次轮子。
如今我竭力扎根Rails生态,远离Next.js——那简直像个混乱的集群。
Next.js确实不太欢迎反馈,它本身也充满主观性。使用Next.js意味着你只能接受其现有方案并遵循其规范,若有需求只能等待…
对我而言,这绝非适合生产环境的框架。你的系统过度依赖无法掌控的外部因素。正如在自有代码中应保持解耦——不仅要避免内部依赖过度,更要规避框架库等外部代码的束缚。
正因如此,我更倾向于构建可(相对)轻松替换依赖的系统:无论出于何种原因,都能随时更换方案或在自有代码库中实现功能。若某个框架强制要求按其方式工作且成为系统基石,这应视为重大警示信号。
> 由于默认日志仅在开发阶段启用,我们自然无从知晓实际发生的情况
等等,既然存在日志问题,为何不配置OpenTelemetry?将其集成到项目中非常简单[1],若不想自行管理,Grafana Labs[2]等经济高效的解决方案也触手可及。
[1]: https://nextjs.org/docs/app/guides/open-telemetry
[2]: https://grafana.com/
这绝对是“现在我们有两个问题”的绝妙解决方案,我亲身经历可以明确证明。
根据OP的需求,OT可能过于大材小用。
当他们改动fetch原型导致团队耗费数日排查100kbs以上文件上传失败原因时,我已将其卸载。
个人认为Next.js试图包揽过多功能,迭代速度过快,每隔数月就重新发明轮子却始终未能完善。
这就是Next.js的悖论:它能同时给人极简与极繁的双重感受。对于托管在Vercel的小型业余项目,我推荐它;但用于构建完整的SaaS产品时,我常为此后悔。
我自己也遇到过这种情况。我尝试过中间件、AsyncLocalStorage,甚至用高阶组件包装布局和页面。经过大量研究,仍觉得这种简单任务被过度设计了。讽刺的是,据我所知Vercel上的Next.js默认就提供requestId。
另一个反复出现的问题是文档。一旦涉及稍复杂的内容,文档就过于简化而毫无用处,唯一能找到的指引就是零星的GitHub公开问题。
我很喜欢它。从事软件开发二十余载——从Perl、PHP、Java到Python,Next.js和React让我重新找回了开发Web前端的乐趣。
Next.js确实存在诸多陷阱和注意事项,但作为框架本就如此,除非自己开发,否则需要适应期实属正常。
某些方面仍有改进空间,尤其在非Vercel托管场景下。若前端后端逻辑过于复杂,我会考虑切换到Nest.js,但总体而言对Next.js很满意。
Remix(咳 其实就是React Router 7框架模式_咳_)则截然相反,它仅提供请求→响应处理器,需要手动构建服务器。起初觉得烦人,但确实赋予了完全的自由度。
近年来Next.js频繁变更API,导致开发流程日益复杂混乱,这让我深感沮丧。
正考虑用Remix重构即将上线的论坛/CMS系统,并实现自定义认证功能。不知是否有其他开发者用过Remix?
若你排斥API变更,建议避开Remix团队。我知道它不像Next那样稳定,但我用过react-router——它经历过API更迭,后来演变为remix,又回到了react-router…向后不兼容的变更就是它的标志。文档问题也因此而生,完全不同的功能被冠以相同名称,而且据我所知,他们现在正在构建全新的Remix,甚至不再基于React。
不过坚持使用单一版本应该不会有问题。
尽管“Remix”的更名/品牌重塑略显混乱,但React Router团队始终出色地提供着强大的解决方案,充分利用了网络平台的特性。其框架模式(原名“Remix”)比Next.js更简洁高效,比vite-ssr功能更丰富。需要数据变异?用表单即可。获取数据?底层直接调用浏览器原生fetch。核心始终回归本质:HTML与HTTP。你可以自主决定客户端JS的部署量,甚至基本消除其存在。或者,若想构建传统SPA,同样游刃有余。手机上匆匆写的HN评论难以尽述其妙——但它确实非常出色。维护者团队拥有卓越的开发记录(不像Next.js/Vercel存在供应商偏见)。
顺带一提,我自1998年起从事网页开发相关工作,2016年开始使用React。
他们对破坏性变更的顾虑早在Remix时代之前就存在——React Router每次重大版本都引入大量破坏性变更,导致需要大幅重写代码,这早已是社区里的老梗了
上次使用他们产品时,文档也糟糕透顶。
我曾用它搭配Shopify的Hydrogen框架。虽不甚喜爱Hydrogen,但Remix运行良好,基本能无障碍工作。依赖注入简直轻而易举 🙂
Next.js简直是毒瘤。但愿更多人能早日看清本质,免得Vercel彻底毁掉ReactJS。
我以为NodeJS本身才是毒瘤*。看来要跟上所有毒瘤的步伐真不容易啊 😉
* https://news.ycombinator.com/item?id=3062271
它没列在N下
https://web.archive.org/web/20190612110518/http://kill-or-cu…
我认为太多新晋“开发者”依赖基于Next.js等流行框架训练的AI工具进行创作,而Vercel对此乐见其成——毕竟当你需要部署这类技术栈时,他们终将成为“首选平台”。
这些开发者缺乏真正的替代选择,因为他们从未系统学习过服务器/网络基础知识、VPS操作等技能。而Vercel通过模糊服务器与客户端的界限、“边缘”函数等概念,再加上动态定价策略,进一步加剧了他们的困惑。
我从未用过Next.js。坦白说Express始终能轻松完成任务。理论上Next.js相较Express有哪些优势?
我发现NuxtJS在开发体验上遥遥领先。NextJS的架构总让人感觉束手束脚,而NuxtJS一切运行顺畅。
完全赞同。Nuxt直观易用——约定优于配置和自动导入功能省去了大量冗余代码。关键在于将其视为应用框架而非后端解决方案——在此范畴内,它能完美处理现代SSR/SPA的复杂需求。
Deno Fresh的思路很正确。它不复杂,文档简洁明快,能同时处理服务器和客户端逻辑而不混乱。
可惜它仅支持Deno(虽然我完全理解原因)
我认为HonoX与Fresh类似,都具备交互式岛屿功能
https://fresh.deno.dev/docs/concepts/islands
https://github.com/honojs/honox?tab=readme-ov-file#interacti…
哦哦不错,之前不知道这个。感谢分享,看起来很有意思,而且还能跨框架使用 🙂
(编辑:至少理论上是这样)
几年前我也尝试过做类似的东西,想处理多个渲染器和自选的SSR技术组合[0],但最终没能推进太多。其实应该基于Hono来实现,这样就能获得符合Web标准的Request对象。
[0] https://github.com/coxmi/ssr-tools
Fresh前景可期,但目前仍不支持Tailwind以外的任何CSS方案。
编辑:刚看到两小时前的邮件,他们因迁移至Vite而新增了对所有CSS方案的支持。这真是个好消息!
我曾计划用这个框架做项目。试点阶段进展顺利。Fresh在静态与动态岛的处理上思路正确。最终我们选择了Astro部署——它也有类似理念。但最终我没能让团队完全接受Deno。
真遗憾,我也经历过类似的决策过程。
不过Astro确实不错。虽然对某些设计决策存疑,个人也不太适应新文件格式和领域特定语言的要求,但它在框架无关性方面做得相当不错,尽管存在些许痛点。
我认为Fresh仅支持Deno并非核心问题。它基于Preact构建,而Preact本身就让许多人望而却步——它瞬间隔离了太多React生态系统的功能。
我对此表示认同,React生态庞大无比,重建大量复杂组件往往超出许多绿地项目的预算范围。
我也不认为React单一生态是好事,因此Preact或Solid生态的成长将极具积极意义——配合不断完善的Web/DOM标准,最终使这些框架更像是围绕基础更新的轻量封装层。
在我看来,React和Next.js都犯了典型的“架构太空人”错误,它们不仅提高了入门门槛,更让多数网页开发比实际需要的复杂得多。
这类帖子本质上都在说“基于我对其他技术的认知,这东西没按预期工作”。
但这次我确实认真尝试过用nextjs做认证和Stripe支付的项目,结果发现连模态对话框这种基础功能都难以在客户端正常运行,简直令人抓狂。
我拥有丰富的React单页应用经验,但Next的客户端/服务器划分对我而言依然晦涩难懂,以至于我决定重启项目改用Django(当初几乎就是从它开始的)。
所以没错,它确实不符合我的预期…
咦?模态框的实现方式和其他框架并无二致。
我最近将两个项目从Next.js迁移到Vite。虽然工作量很大,但Next.js确实包揽了太多事务:翻译、认证、打包、CSS处理、缓存…不过这绝对值得,它大大降低了我的日常烦躁指数。除非万不得已,我不会再用Next.js了。
Next.js具体如何处理翻译和认证?它既不强制要求也不提供简化方案啊。
其实有next-i18next和NextAuth.js,这些集成能快速搭建基础框架。
我正参与一个基于Next.js的项目启动。有经验的开发者能否分享看法或潜在风险?
简而言之这是个概念验证演示/探索性项目。
当前采用Next.js应用路由器,通过多根路径管理模块(如marketing、app、login等)。
数据库选用Neon实现分支管理,可能集成Vercel AI SDK处理AI功能——项目很可能需要添加AI模块。
唯一顾虑是:当结合BetterAuth使用时,不确定无服务器层/后端如何与用户认证授权交互?
目前托管在Vercel上,感觉Next.js与Vercel在托管方面堪称绝配。当然这可能源于我对Vercel不熟悉——毕竟Next.js让在Vercel快速部署概念验证变得极其简单直观。
不知这算不算经验丰富,但我已探索Next.js约一年,其中后半年从探索阶段转向了正式项目开发。
我理解作者的挫败感。在处理Next.js的中间件及其他组件时,我也曾有过类似体验。其实每款软件和框架都曾让我如此沮丧。很多时候问题根源在于强行将方枘塞入圆凿,直到建立起正确的思维模型后才豁然开朗。
作为从90年代CGI脚本时代起步的Web开发者,服务器端渲染、客户端渲染、边缘运行时等概念对我而言都相当陌生。但每当我忍不住嘶吼“为什么不让我这么做?”时,答案往往是“因为这样做毫无意义”。身份验证模块正是如此——反复纠结“为什么不能通过中间件查询数据库用户”的过程,反而让我重新梳理了Next.js那些被我忽视的核心机制。
关于是否必须绑定Vercel进行托管,完全不必如此——只要你不选择使用他们全部的服务。根据我的经验,他们提供的示例Docker构建包完全适用于任何环境部署。
或许我这么说是因为没试过其他几十种现代 TypeScript 框架(过去十年我困在技术孤岛,跟不上潮流),但我确实挺喜欢 Next.js。等我开始开发原生移动应用时,可能会意识到自己被诱导忽略了干净的 API 层而改变看法,但目前功能添加过程相当顺畅。
是的,我们尽量避免这样做。但我知道很多人确实这么干。
不过这似乎是最不理想的新项目框架选择理由。希望未来它不会成为决定性因素。
比单纯用React加Node后端强得多吗?我怀疑,这里比较的依据是什么?
如果你试图在中间件(后端)和渲染(前端)之间共享上下文——是字面意义上的共享而非逻辑上的共享(仅通过请求ID实现)——那么在我看来,这源于对后端与前端本质区别的根本误解…
我怀疑市面上这些“全能型”JS框架不幸让人们误以为Web应用是由“一个庞大而完美的JavaScript应用构成,太棒了!”,而实际情况是两个(甚至三个)完全解耦的运行时环境(后端与前端)。
对于想尝试其他方案的人,我推荐SolidJS和SolidStart。我个人从未尝试Next.js,正是因为它总在替我做太多决定却不留选择余地——我不信任这类框架。
根据项目需求,写个API服务器的SPA即可;若是静态网站,直接预渲染后部署到Cloudflare就行。我不理解这种复杂性的吸引力。若需SSR支持SEO,SolidStart便是简洁优雅的解决方案。
或许这里是提问的好地方…我正考虑用Next.js替换运行数年且持续稳定增长的Gatsby.js项目。鉴于诸多负面评价,有哪些更优替代方案?我甚至没用过SSR或GraphQL功能。相较于早期“自带路由库”的方案,Gatsby吸引我的核心优势在于:极简配置、优质开发服务器、易用的插件生态。它省去了大量重复性工作,让我能专注于React组件和Hooks等核心开发。后端采用Django处理API,前端则使用静态托管方案。
目前发现的潜在替代方案:
更新:研究后我非常欣赏 Tanstack 的路由方案:https://tanstack.com/start/latest
最近我将多个Gatsby项目迁移到NextJS,整体体验相当不错。但高度抽象化的设计和黑盒化特性确实导致偶尔会遇到因未文档化行为引发的bug,而我们完全_无从_知晓正确的处理方式。
嗯…听起来可行,但也有点令人担忧 🙂 我宁愿避免神秘的错误,况且GitHub上还有那么多未解决的问题…
顺便提下,remix其实已经不存在了,它在很久以前就被合并到react-router里了。
Astro在原型开发中表现相当稳健,我喜欢它能控制客户端代码岛的渲染,且兼容多种渲染库。不过近期的发展方向有些让我不满意,担心像astro db这类功能会导致它变得臃肿
对Next的任何负面评价…对Gatsby而言都将放大千倍。
同样地,React Router就是…我向人举例说明糟糕工程设计的典型案例。正如你所言,存在_多个_版本的React Router,只因开发者完全不称职,根本不懂自己在做什么。结果就是,他每次发布新版本都会以破坏向后兼容性的方式彻底改写整个库…每次。
Astro和Remix都是可行的选择,但Next(尽管存在缺陷)能持续主导市场自有其道理。
React Router v4于2017年3月发布(距今已逾8年!)。v5于2019年5月发布,未改变API。v6于2021年11月发布,引入基于React钩子的全新API(该API远优于前代)。v7于2024年11月发布,引入“Remix” API(最重要的是“框架模式”)。因此版本间隔分别为4年和3年——这似乎并不离谱。
我个人将Remix v2项目升级至React Router v7时,通过多种标志位预先准备,最终实现零问题快速升级。
> 整个库都以大规模、向后不兼容的方式…每次新版本都如此
新主版本出现破坏性变更有何稀奇?
对Tanstack库有何看法?我们使用他们的查询库,讽刺的是它让我想起ext.js的某些特质,但整体相当稳定。
我认为工具选择不当不应成为抨击工具及其设计决策的理由。多数抱怨源于对NextJS工作原理及其设计决策的误解。例如中间件机制——它们本质上是钩子。倘若NextJS像Express那样提供处理器机制,你们恐怕又会抱怨处理器执行顺序之类的问题。
若你们在不了解NextJS中间件架构、Vercel供应商锁定、SSR策略、水化方案等特性时就选择它,责任在你们自己。我和许多开发者都通过NextJS显著提升了交付速度,最终创造了客户价值。
两年前我离开 React 生态转向 Elixir/Phoenix/LiveView,体验非常出色。但该框架同样存在源于设计决策的挑战。开发过程中总会遇到不尽如人意之处,而 NextJS 似乎成了人们宣泄不满的替罪羊。
NextJS被许多人大力推广,常被默认为React应用的标准选择。我认为这是错误的,主要因为NextJS包含大量多数应用根本不需要的复杂功能。
React官方文档臭名昭著地将NextJS作为“默认方案”推荐。尽管在大量批评后有所收敛,但创建新应用时它仍是官方的首选方案[1]
[1] https://react.dev/learn/creating-a-react-app
抱歉,但请务必读完这篇帖子。
中间件部分只是铺垫。真正的麻烦在于:即使从Next中移除并使用自定义服务器,你依然无法实现任何功能——因为Next是个黑盒子。
我本以为安装fastify并直接使用其中间件就能解决问题,但连这都行不通。
你在Elixir开发中遇到了哪些挑战?
AsyncLocalStorage.enterWith是错误的方法;.enterWith会改变整个*同步*执行过程的日志器。当仅存在单次请求时(如本地测试场景)这不构成问题。但这正是实际项目中失效的原因。
请改用 .run 方法。
我的enterWith在异步/同步流程中运行完美——请求与响应间的所有操作都会透明地记录请求ID。我使用自定义服务器进行独立构建和开发服务器,Next.js 14,Node 22。
不行,这个也不行。
看来我们都加入了反Next.js阵营,尤其当越来越多Vibe编码应用似乎都在基于Next.js构建时!
这框架本就是为Vibe编码设计的,就像其创建者注定要被AI取代的群体一样。
补充关于Next.js与Remix.js的讨论。
去年我们为新应用选框架时调研了行业现状。Next.js当时/至今仍是最热门的选择,但负面反馈和警示也最多。Remix虽非完美,但我欣赏它更少的抽象层和简洁的请求/响应结构。
另提醒招聘前端/全栈岗位的同仁:
多年来招聘特定前端框架岗位时,我们总会遇到自称框架专家的候选人——无论是React、Angular、Vue还是Remix,他们总能展现惊人技术。但实际入职后才发现,这些人根本不懂核心JavaScript原理,脱离框架就毫无用武之地。
正如其他人指出的那样,若需React框架,React Router v7(框架模式)远胜于Next.js。我认为它是个出色的框架——没有过度抽象,只是在标准网络技术基础上构建的React。
不必因过往的破坏性变更抱怨而却步(顺带一提,在尚未厘清服务器/客户端应用架构最佳方案时,这类变更实属必然) – React Router当前通过多标志位[1]的策略,能让你提前为断言变更做好准备。
[1] https://reactrouter.com/community/api-development-strategy
> 你以为我在开玩笑?数百个问题报告堆积如山,配着无数表情符号,官方却多年毫无回应。等到终于得到回复,却被告知你的做法有误,而解决你真正问题的方案正在路上。接着他们就让这个“解决方案”在测试环境里无限期滞留。
雇人负责优先级排序(或降级)、行为准则执行之类的事,远比踏踏实实做好本职工作轻松得多。
Next.js的营销策略就是让你锁定在Vercel托管服务上。
用了几年Next.js,分文没付给Vercel。既然已有庞大社区支持的React SSR框架,何必自建解决方案?
容我自卖自夸——认同此观点者或许会喜欢我的全栈框架[1]。刚发布新版,速度大幅提升且功能更丰富。虽为SaaS优先设计,但传统网站同样适用。核心理念是“绝不坑人,开箱即用”,彻底告别JS框架常见的耗时陷阱。
[1] https://cheatcode.co/joystick
等不及看那个充满主观意见的分支三周后被弃养的场景了。
他专门建了个新博客吐槽Next.js,结果博客本身就是用Next.js搭建的
听起来是切身经历啊
我也遇到过类似问题,写了篇指南教大家如何用LogLayer + pino正确实现服务器端日志。
https://loglayer.dev/example-integrations/nextjs.html
总体而言,Next.js的日志体验糟糕透顶,更别提无法串联中间件了。
中间件抽象化很棘手,因为第三方库往往滥用它,导致性能问题。此外,Vercel为其云业务优化了中间件,包含多种性能优化方案。
我认为这并非重大问题,毕竟若实在无法接受这些权衡,存在诸多绕行方案。
人们宁愿忍受这些复杂操作,也要避免使用Ruby on Rails、Phoenix、Django等框架。为何?Next.js对用户体验真有那么大的提升?我表示怀疑。
我们也是出于类似原因放弃Next.js。当前架构的灵活性不足以满足多数“高级”团队的需求。
我是不是疯了?我超爱Next.js。还有哪个React部分服务器端渲染方案比它强?
没错,Next.js的中间件简直垃圾
我认为,如果文档从未给出功能用途的清晰示例,这很可能意味着该功能是被生硬添加的,未考虑任何具体使用场景。我逐渐意识到Next.js可能希望我们用RSCs实现大部分中间件行为
我刚说服一家规模不小但非全球性的客户,从他们过时的系统迁移到Next.js。他们绝对不会在生产环境用Vercel部署——毕竟他们对成本极其敏感…现在看到这篇和其它“Next.js生产环境噩梦”的帖子,我真的非常担心。我之前只在Vercel上部署过小项目。超级焦虑。
选择NextJS的依据是什么?若不用Vercel,NextJS的价值就大打折扣。像Astro这类框架可能更适合。
任何足够先进的开发生态都充斥着问题,也总有人乐于指出来。无论缺陷如何,它在你当前场景中仍可能是对旧方案的改进。
这观点我认同。
我也有类似经历。
Next.js问世八年仍文档不足,一旦偏离常规路径就会迷失方向——而有时根本不存在常规路径。
坦白说,当我发现他们竟复活了90年代的目录路径规范时,就该意识到这是傲慢与对Web开发历史的无知。这本该是警示信号,但我却被Vercel的字体和网页设计迷惑了。
投资于隐式且僵化的路由模式——比如我们没有好东西时用的基于目录的路由——就像是个有钱的嬉皮士。哦,还不止于此。能(开玩笑,你必须)把文件命名为
[带参数的路径].tsx是不是很酷?你可能以为方括号只是占位符,但并非如此!方括号是文件名组成部分,表明该路由接受参数。诚然,我们都曾尝试过设计精巧的路由模式,有时甚至过度沉迷于不断改进这些本不该存在的方案,但鲜少有人像这样执着地沿用如此之久。
所幸,只需将单一路由设为路由器文件并添加间接调用,就能轻松规避上述模式。虽然会损失部分树摇动优化及其他依赖隐式规范的优化效果,但代码库仍可通过grep检索。
我强烈推荐vike.dev。
真希望更多人了解并讨论vike,这工具实在太强大了
NextJS开发者生命中最美好的两天:项目启动日和迁移到React Router那天。
看这些评论真有意思,去年此时NextJS还是世上最伟大的存在。
现实正在迎头赶上。
React服务器组件和服务器端渲染本就不该纳入React核心,而应作为独立项目存在。
完全同意!但这并非React首次改变核心。对我而言,它已死。
作为探索过几乎所有JS方案(包括但不限于React、Angular等)最终选择Phoenix/Elixir的资深开发者,我可以告诉你:没有任何技术能比得上这种从容——只需打开文本编辑器,随心所欲命名模块,编写函数,管道传输结果,用
with.. do..else进行验证,最棒的是——编译时就能捕获错误。一切始终如一地运行——即使五年后依然如此。反观JS,即便代码分毫未动,每隔半年甚至更短时间就会出现问题。认真考虑试试Elixir吧。
JS开发者患上斯德哥尔摩综合症早已不是新闻。
通过客户接触到InertiaJS后,React与Laravel的组合让工作变得轻松许多。Laravel的路由机制更符合我的习惯,数据从后端传递到前端也毫无痛点。
我同样偏爱服务器端——PHP比JavaScript服务器更适合我。nodemon、pm2等服务器工具曾给我带来不少诡异的bug。
我也推荐Inertia。
它功能精简,相较Nextjs令人耳目一新。
这是为前端风格API设计的轻量级后端工具,涵盖基础路由、数据获取和表单提交功能。
虽然Inertia最初为Laravel设计,但我认为它与Adonisjs配合更佳。由于Adonisjs基于TypeScript,能自动推断后端返回的数据类型。
我对Next.js得出痛苦的结论:
它不适合构建全栈应用,但仍是现有框架中最佳选择。
https://x.com/paradite_/status/1941016421934551477
> 科学家在https://github.com/vercel/next.js/issues发现新超大质量黑洞
这下可把我整惨了
记得前阵子那篇“Rails是贫民窟”的文章。
现在看来,比起Next.js之类的东西,Rails反而显得理性多了。
既然不用Java却要用TypeScript,至少能享受JVM生态系统的红利?
对我来说我会坚持用Rails——这是我熟悉的领域
每次听到现代JS/TS框架,我都惊叹于它们不必要的复杂性。
在Python/Bottle.py里,这不过是在before_request()或before_response()里加个stderr打印语句。多一行代码而已。
这个快速发展的领域里框架层出不穷,我得靠记忆法区分哪些受好评哪些不靠谱。这个框架倒是简单明了:Next.js?Next。
我的结论是仅在极简单项目中使用Next.js。问题在于简单项目会成长为重要项目。所以干脆别用Next.js。
Next在Dokploy上运行没啥大问题。我没用太多功能,只用些SPA路由和dôme RSC路由。
这样就够了。运行良好。
题外话:如何实现这个功能?当我在博客中选中文本时,会弹出生成指向选定内容的唯一链接的选项?
每个段落/元素都有一个唯一的ID,从0开始,递增为1。您最初访问的是页面https://blog.meca.sh/3lxoty3shjc2z。创建链接时,您实际跳转至的是https://blog.meca.sh/3lxoty3shjc2z/l-quote/64_25-65 _174#64 的页面。额外链接元数据格式为 `64_25-65_174#64`,具体含义如下:
– 在第64个元素/段落处开始高亮。该段落内从第25个字符起始。
– 在第65个元素处结束高亮。该元素内于第174个字符终止。
– 页面加载时自动滚动至第64个元素。
页面上还添加了代码,用于在指定起止点创建高亮的span标签。
若网站维护者在此:目前链接存在问题,应指向https://blog.meca.sh/3lxoty3shjc2z/l-quote/64_25-65_174#64 (https://blog.meca.sh/3lxoty3shjc2z/l-quote/64_25-65_174#64),但实际跳转至[https://blog.meca.sh/3lxoty3shjc2z/l-quote/64_25-65_174#64_2…](https://blog.meca.sh/3lxoty3shjc2z/l-quote/64_25-65_174#64_25),且页面加载时`64_25`并非有效ID。
好的,我发现从上一行的末尾开始选择就能复现该问题。
我已在https://github.com/hyperlink-academy/leaflet/issues/196提交了该问题报告。
博客本身是基于 leaflet.pub 构建的。
我会查看并提交问题报告。
大家对 Nuxt 也有类似感受吗?他们最近刚加入 Vercel…
哈哈,当我在Discord上向开发者解释这个时,他们总会激烈反驳,我干脆不再深究。
只要看到招聘启事把Next.js列为“要求”或“常用工具”,我立刻就会忽略这个岗位
嘿嘿,好帖子。直接用Hono或Hono X吧。对我这种后端为主的人来说,它更容易理解底层逻辑。
没细读帖子,但他们说的很有道理。我接手过Next.js项目,这绝对是最糟糕的框架。
无需/不想多说。
Next.js就像Laravel一样充满魔力——当你期待魔法时它很棒,但若想理解底层原理就糟透了。
Laravel易学易用,上手门槛低。无需编译器,后端不写JavaScript,Eloquent是优秀的ORM。它没有太多魔法,没有async/await。当然你也不该用Livewire——那简直糟透了!
确实挺令人沮丧,但我真心喜欢它的SSR能力
我用pikku分离前后端,这样既能继续使用常规服务器端功能,又可在需要时启动独立API服务。
在Vercel上测试不多,但Next.js会忽略中间件——除非是前端代码相关。
https://pikku.dev/docs/runtimes/nextjs-app
免责声明:我是pikku核心开发者
说真的,早期用页面实现时还行,没那么多魔法/抽象。
如今Vite要优雅得多。
Next.js在企业软件领域自有其价值。但对我们普通开发者而言,是时候简化了。我甚至追求到近乎荒谬的极致。
2025年我为何坚持纯HTML+CSS:
https://joeldare.com/why-im-writing-pure-html-and-css-in-202…
或许是我多疑,但Next.js总让我觉得像是为训练营学员设下的陷阱。
只需记录你的REST服务,而非视图。Next.js本身就是视图——它既属于服务器又脱离服务器,本就难以保持一致性。
不过采用纯CRA方案更合理,毕竟它本质就是视图层
应用路由确实颇具挑战性。正因如此我们坚持使用页面路由,效果相当出色。当然某些场景下应用路由仍能发挥作用。
坦白说,Next.js其实没那么糟糕,甚至可以说是我用过最出色的Web框架,但它确实存在自身问题,对新手而言可能难以承受。
深表认同。我遭遇过太多文档和默认设置的问题,而Vercel团队的回应总是让你怀疑自己操作有误。
真相是,这根本就是设计糟糕的软件。
它的优势在于提供开箱即用的入门体验。他们深谙:只要降低用户激活门槛,就能吸引足够多的用户容忍其缺陷。这恰恰是开发工具生态的典型写照。
我们最初用Next.js启动了一个纯前端项目,但很快转用了React Router。诚然它确实方便(当它正常工作时),但你根本看不清或理解其运作机制,更别提如何控制它——而黑魔法般的崩溃简直是雪上加霜。现在终于轻松多了。
App路由器的改动简直毁了它。Pages路由器设计得如此合理直观,实在不明白团队为何要彻底抛弃当初让Next脱颖而出的核心创新。
鉴于这里对NextJS的集体吐槽,说这话怕是要挨踩…但我们确实用Next.JS,没托管在Vercel——而且真的没遇到什么大问题。
不过我们的应用基本按标准React架构构建,很少使用服务器端渲染,且主要通过TanStack Query加载数据。因此Next.JS的核心价值主要体现在路由和项目组织上。
好奇问下,你试过Tanstack Start吗?它似乎是类似Next和Remix/React Router的框架。
我一直很喜欢Tanstack的库,正找借口用Start呢哈哈。
有趣的网络问题(协作工具、创意软件、科学计算)总要对抗为最平庸的落地页设计的框架生态。
Nextjs和多数Web框架都假设你在建电商网站,只需靠加载速度取胜。
Vercel耗费巨资推广Next.js的营销手段实在令人咋舌——去年我的产品经理竟试图强迫我采用它。在他看来,若不用Next.js(一个仅数百人使用的SPA),我们的应用就无法正确构建,更别提招募开发者了,毕竟人人都在用Next.js。
真心讨厌这些家伙把React搞得这么流行,害得我工作难上加难。
我完全认同文章中的感受——那种沮丧。根源在于Next.js急功近利地推出半成品功能的通病,从应用路由器开始就已显露端倪。
典型案例
“使用服务器端”“使用客户端”
直觉上,一个在服务器运行,一个在客户端运行。但你错了。实际情况复杂得多。现在将这种混乱乘以100个功能。结果就是一个晦涩难懂的框架,除非仔细研读数千页文档,否则其行为往往难以预测。
人工智能也帮不上忙,因为API变更过于频繁,生成的文档往往显示13版而非14.5版,更别提15版或最新版本了。
这种情况很可能源于开发团队为赶上Next.js大会而仓促发布功能——就像许多SaaS供应商为各自大会赶工一样。
最令人恼火的是,我感觉找不到可靠的替代方案来构建企业级React应用——那种能长期稳定存在的方案。我曾依赖create-react-app,但如今它已被弃用,我常常自问:还有什么能真正值得信赖?我不想耗费开发周期处理webpack之类的事务,但这似乎正是React团队推崇的方向,难道我只能被动接受?
另一个让我困惑的是Next.js的目标市场似乎与Ruby on Rails重叠——大量电商、媒体等领域。而我编写的B2B应用显然不属于这类场景…
Vite.js呢?
最近的实验是使用Vite.js,后端采用Go语言,通过embed.FS嵌入Vite应用。虽然尚未在生产环境测试,但这种方案似乎颇具潜力。
边缘基础设施和服务器端渲染最让我抓狂。
NextJS作为框架相当优秀,虽然转向RSC后有所下滑,但对多数场景仍很适用。问题在于Vercel及其与该平台的深度绑定。
最近我用NextJS开发了一个小型内部应用,我们使用Azure PostgreSQL,订阅了Vercel的Pro套餐(说实话这都算过度配置了,免费套餐就完全够用;而且免费套餐非常非常慷慨)。但遇到一个问题:托管在Vercel上的NextJS应用永远不会获得静态固定IP, 因此除非向全世界开放访问权限(这绝非可行方案),否则无法直接连接数据库。这种设计实在愚蠢至极,竟敢自称全栈框架简直令人震惊。
任何在Next.js中运行后端代码的人都根本不懂自己在做什么。如果是玩具项目想快速实现功能或许还能接受,但即便是小型操作或MVP,整个方案都缺乏连贯性。
我之前写过关于Next.js的文章:https://omarabid.com/nextjs-vercel 我的观点依然不变:Next.js中的多数问题并非缺陷而是特性——一种仅在Vercel平台上生效并锁定开发者的特性。
鉴于其路由/SEO/内容功能如今也日渐退化,尤其在React服务器组件出现后,使用Next.js的理由已所剩无几。
Next.js的表现竟比PHP更糟。
Next.js竟比JEE更糟糕,这至少堪称讽刺。当初人们转向Node.js,正是因为Java生态“过于臃肿”。十五年后,Node.js社区竟也染上了JEE的毛病。历史重演,新开发者将视Next.js为臃肿之物,转而投向没有这种污名的新平台…
现代前端生态圈大多令人抓狂。早期 React + TypeScript 时代虽略显烦人但尚可忍受,若抽象层能改进,本可预见相对美好的未来。结果 Vercel 之流把这点缓慢进步扔进了昂贵的垃圾桶。
真心求解——还有什么替代方案?
React Router + Express?
还是其他选择?
我们几个应用都用React Router 7(前身为Remix)搭配Fastify,涵盖内外系统。开发体验相当满意。这里也有不少NextJS项目。多数团队将其当作“更优的Create React App”使用,几乎不触发其核心功能。我才是唯一用过服务器端功能的人。
对我所在团队而言,React Router 7真正做到了“不碍事”。若以框架模式(SSR)使用,你将获得在服务器端运行的加载器和操作功能。我发现它简洁得多。上文提到的日志器示例在React Router里简直是小菜一碟——要么导入日志器并在加载器中运行(可添加.server.ts文件明确限定服务器环境),要么通过上下文注入日志器。
React Router v7 其实很棒,需要时也能实现服务器端渲染。
React Router 非常出色。Express 的服务器端实现尚可(hono 或 fastify 更有优势)。
还有坦斯塔克路由器和坦斯塔克启动器…根本不必纠结框架选择。
最令人抓狂的是人们竟不利用TSC编译器插件的代码生成功能,来自动化数据库实体的双向同步…
数百万行代码,白费功夫。
我用Svelte,还行。
说实话我对整个Node.js生态都持这种看法。在我看来它从头到尾都像被诅咒了。每次试图破除诅咒,只会堆砌更多框架、层级、抽象和新范式。
我已经数不清多少次为启用简单的日志功能而挣扎。
仅仅为了让单行“log(‘hello’)”出现在任何地方——文件里、控制台里、甚至天空中的烟雾里。
令人抓狂、沮丧、烦躁到极点。
在生产环境中关闭日志简直是脑残操作,因为生产环境恰恰是最需要日志的时候。
我始终在next.js中使用自定义服务器(连express也能胜任),因为中间件和边缘计算那些玩意儿简直是过度复杂的垃圾。客户端运行如常规react应用,对初始属性直接从服务器端填充的页面采用SSR处理,系统整体逻辑清晰易控。虽然现在有其他框架也能实现,但我习惯这个方案,它运行良好,没必要改变。
这似乎是Web全栈框架的自然演进。当年用Rails时我也这么想:要么英雄般死去,要么活得够久成为反派!
依然热爱Next,但纯粹因为我早期就喝了迷魂汤。若现在才接触,恐怕会被其复杂性吓退
看吧,我们又经历了一个Web框架的蜜月期。
这是我见过最被过度炒作、供应商锁定最严重、漏洞最多的Web框架。
读完这篇帖子深有同感。NextJS在v11左右初现稳定时确实出色,那时create-react-app根本无法使用。但此后它就迅速走向衰败。
百分百赞同。
过去五年我的核心技术栈是Flutter+gRPC+Go,读到这类文章时既感到震惊又暗自庆幸。
多年来我一直倡导技术社区坦诚面对Web技术栈(尤其是JS)的发展历程,客观评估其在现代软件开发中的适用性。这个技术栈的意外复杂度已达到疯狂程度,而人们似乎乐此不疲地为其堆砌更多层级。
个人而言,我尽量避免使用它,效果相当不错。当然,权衡取舍无处不在,庞大的Web开发社区本身也具有独特优势。例如我怀念Flutter中丰富的设计系统选择(相较于Web),不过话说回来,Flutter计划在2026年实现核心与设计系统的解耦。但拒绝在产品中使用存在根本缺陷的工具,其净收益是巨大的。
> 我的核心技术栈是 Flutter+gRPC+Go
用于网页应用?性能如何?开发体验如何?
是的,Web构建功能堪称额外惊喜,我从Flutter早期支持Web渲染器时就开始使用。如今我专程用它开发客户端Web应用。
开发者体验嘛…通常我会在本地以MacOS桌面应用形式运行构建版本进行开发。借助热重载和Flutter开发工具,这种方式便捷得多。常通过DevicePreview[1]封装器测试不同尺寸、深浅色模式、字体缩放等效果。除非涉及平台依赖功能(如推送通知或调用系统相机/机器学习框架的二维码扫描器),否则我甚至无需在移动端或网页端测试——我知道它会像素级完美呈现。优化方面存在一些注意事项,比如“默认情况下Web构建不包含表情符号以减小体积”,但除此之外,Web构建在像素级上与MacOS或移动端视图完全一致。测试时我使用Maestro在iOS模拟器中运行。
居中布局(“
不过我对当前Flutter设计的主要质疑在于:两大核心设计系统(Material Design与Cupertino)与核心框架的耦合度过高,且缺乏多样化的替代设计系统选择。需要澄清的是——在Flutter应用中创建自定义组件/主题/视觉风格极其简单,但构建一套深思熟虑的设计系统则是完全不同的挑战。所幸解耦工作正在推进[2],期待这能催生更多优质设计系统的实现方案。
此外,由于Flutter最初定位于移动端开发,向桌面/网页端的扩展近乎偶然,其绝大多数组件都针对移动UI进行了优化。例如,若想为桌面用户打造具备掩码功能且带日历选择器的日期输入框——祝你好运。由于我开发桌面/移动应用的比例各半,目前暂采用深受shadcn启发的forui[3]设计系统。
性能问题在Flutter开发中从未困扰过我,因为其引擎最初就经过深度优化,能实现<15ms的帧渲染速度。现代网页渲染器采用Wasm技术、着色器预编译以及某些连我都懒得探究的“黑科技”。坦白说,我对“网页应用”的糟糕体验早已麻木,任何非原生浏览器的渲染管道都不可能比这更糟了。比如界面闪烁、组件无响应、状态管理混乱、必须刷新页面(本质上相当于网页端的“重启应用”)、表单/字段操作卡顿——这些在网页端简直是家常便饭。但在Flutter网页应用中我从未遇到过这些问题。它们或许比基于HTML的网页应用略显“非原生”,但我从未听闻真实用户会介意这一点。
[1] https://pub.dev/packages/device_preview
[2] https://docs.google.com/document/d/189AbzVGpxhQczTcdfJd13o_E…
[3] https://forui.dev
对于经验丰富的开发者,您认为学习Flutter的最佳资源是什么?
直接用正确参数运行生产环境就能输出日志?还是把应用切换到本地环境尝试复现?语气也完全不对。
Next.js 中的中间件似乎和框架 X 中的中间件概念不同。整个讨论串看起来像是对 Next.js 的误解大集合。
Node/Express/PostgreSQL永远不会让你失望
还有更恼人的事吗?某些页面根本无法加载(只显示空白页面,或者像这次遇到太多重定向错误的情况)——除非你启用了Cookie和本地存储。
哎呀抱歉!我是 leaflet.pub 的开发者之一,这个博客正运行在该平台上。刚修复了这个问题(讽刺的是用 nextjs/vercel 实现的)。重定向循环是为在“主”域名和用户自定义子域名间共享认证而设计的。通过 ATProtocol 实现的认证用于订阅和评论等功能!
GitHub响应迟缓很可能是裁员导致
nuxt.js同样令人抓狂,我们需要另写一篇文章讨论它
糟了
用原生代码编程时,很多这种破事都能避免啊……说真的,Web的唯一优势(不用分发新二进制文件)真的值得吗?
标题该改成《Next.js中间件/日志功能令人抓狂》才对
Next.js相关的可观测性功能绝对是场噩梦。工作中我们通常会为Prometheus实现指标接口——对市面上几乎所有API或全栈框架来说这都很简单,唯独Next.js例外。当建议方案要么是使用自定义服务器(这完全违背了Next.js的所谓优势),要么是采用Vercel专有指标服务(对我们毫无帮助)时,我就彻底放弃了。糟糕透顶的东西——再也不碰了。
每次讨论Next.js都让我感觉被精神操控。我曾为几个项目学习它,只因客户听说过就硬要用。结果在初始帖里不断发现这种狗屁问题,提交issue咨询时,Next.js专家们却告诉我“你所有操作都错了”。好吧,我改用Vercel部署。结果客户又想尝试Cloudflare Edge——因为他认识CEO,想借此向投资公司推销更优访问权限什么的鬼东西。于是我得让它在Edge上运行,结果发现不能用某个库的xyz版本,还得用Next.js的尖端版本…诸如此类。
日复一日,我怀疑自己是不是疯了,竟想回归纯粹的原生JavaScript开发。或者干脆用React单页应用对接FastAPI后端部署到S3——这套方案从未让我失望。
和Meteor.js类似,他们试图抽象化那些根本无法抽象的东西(这词存在吗?)。
它对边界情况的处理极其糟糕,遇到这类场景时,要么得找变通方案(导致代码丑陋难维护),要么只能放弃。
我曾用它做过项目,庆幸再也不用碰了。
关于Node.JS后端,我再补充几点:看看有多少分支版本:Deno、Bun…简直乱七八糟。
相比Java/C#(性能/稳定性)和Ruby/Python(开发速度),Node.js作为服务器端平台实在糟糕。
它之所以成功,纯粹因为人人都会JS/TS。但普及度绝非使用理由。
事件循环机制确实简洁易懂,但仅此而已。它本质是单线程且暗藏陷阱,在我看来这终究是其缺陷所在。
许多其他语言/平台也提供事件循环实现并发,却鲜少被采用。归根结底,无论我们多么厌恶线程,它们数十年来经受实战检验的价值无可替代。
遗憾的是,企业选择编程语言往往基于炒作和潮流,而非技术考量。
究竟要等到何时,才会有人为用CSS编写Web服务器而兴奋不已——尽管这根本毫无意义?
简而言之:不仅是Next.JS,整个NodeJS生态都存在问题。
>这算个词吗
只要表达清楚,任何词汇都可以成立。别让任何人否定你。若有人质疑,就回敬:фakdelengiчpolis!
尽管我热衷探究行为背后的动机,但对“仅因”这类论断仍持谨慎态度。我知道这是修辞夸张,但终究如此。
例如我发现Node能完成Python的工作,且表现更优。比如Node能绕过操作系统包管理器完成依赖解析。归根结底,人们终将选择自己熟悉的工具。
这并非精确科学,人们也抗拒将其定型为科学。(若真如此,或许就能更轻松地让人们接受那些“次优”工具——尽管它们其实毫无用处。)
但关键在于后端技术各有千秋。核心基准是CPU,开发者能从真实工具中选择——这些工具都拥有真实的历史与真实的权衡取舍。即便权衡是“运行JS”或“采用C++编写”之类的抉择。
而在前端领域,基准是JS及其浏览器扩展功能。正如我们这些清醒者反复强调的:JS生态早已演变成“操作系统中的操作系统”,在应用程序与JS之间构建完整框架纯属徒劳——这无异于绕道他人护城河而非直接调用API(后者设计通常更优)。
因此,要实现市场差异化,就需要在JS之上构建至少一层“多层架构”,其中某些产品将显得毫无意义,而另一些则相对不那么无意义(当然,这些评价因人而异)。这样用户就能在“更糟糕”和“稍好些”之间抉择,而开发者既能从“次恶选择”获得关注度,又能在关注不足时直接操控选择方向——可谓双赢。
身为Rails开发者,这让我笑出声。本以为JS框架这台鲁布·戈德堡装置如今该更稳健人性化了,结果更像是堆砌在堆砌之上的权宜之计,既无远见也缺技术领导力。
RoR从未被电商以这种规模采用。多数疯狂操作源于对性能的极致榨取——只为避免潜在客户发出“呜哇”的惊呼转投他处。
RoR应用部署后便安稳运行,如同可靠的拖拉机般默默完成任务。
而典型的Next.js应用则分散在源站、地理位置优越的边缘节点及前端之间,这完全是另一种使用场景。
此话何解?Shopify作为最大电商平台正运行着Rails。
我认为他们指的是:Shopify无法决定Rails的运行状态,而Vercel能掌控NextJS的运行环境。
Tobi是前Rails核心贡献者,而Shopify对Rails和Ruby的贡献相当显著。
正如文章所述,如今大多数其他JS框架其实都相当出色。
我同时使用Vue 2和Vue 3,两者都支持选项和组合API,都相当优秀。虽然不清楚它在超复杂UI场景的表现,但完全满足我的需求。
顺便提一下Pinia,我爱你!
我手头有个庞大的Vue 3项目,比起那些所谓“优雅”的React代码库(我曾被迫接触过),我更青睐Vue 3。React至今仍占据市场份额实在令人费解——除了组件库资源(对我领域影响不大)和TypeScript支持略胜一筹(主要是Vue props的问题)外,Vue和Svelte几乎在所有方面都更胜一筹。
确实如此。我从不使用组件库,更倾向于自建组件。不过我也知道像shadcn这类工具也有Vue版本。
没错,Vue确实易于上手,概念更简洁,还拥有更多实用特性。
我认为React本身没问题,问题在于我接手的每个React项目都烂透了——人们总把简单事情搞复杂,组件里塞满五六个useEffect,庞大的全局状态引发各种bug,前端逻辑堆砌得离谱。光是用来模拟HTML/CSS功能的JS代码量就令人咋舌。
人人抱怨React又慢又烂,其实不然。慢的是他们的代码,又烂的是他们的实现。正确使用时React快得惊人。
> 慢得要命又糟糕的是他们的代码,正确使用React时它快得惊人。
问题恰恰在于此——用React自掘坟墓简直易如反掌。若几乎每个项目都犯同样的常见错误,那就不只是使用者问题,而是更广泛的系统性缺陷。而用Vue或Svelte的话,你得费尽心思才能搞出类似的烂摊子——因为Vue的惯用写法(尤其是Options API)实在太简单直观了。比如我们见过多少文章哀求大家别再用
useEffect?更何况React的响应模型糟糕透顶,是诸多性能陷阱的根源。Vue和Svelte基于代理/信号的设计从一开始就更高效,而Vue的差异化协调算法远胜React——仅此一项就能避免大量无谓的重渲染,相比“标准”React应用效果显著。
我不懂Vue所以无法比较。我只知道编写React应用时,代码总是简洁、优雅且响应迅速。我甚至不是前端开发者或JavaScript开发者,主要从事后端工作。这并不难——事实上,编写混乱糟糕的代码才真正困难。我常需耗费大量时间清理他人留下的烂摊子,才能开始真正的工作。
顺便说一句,这并非React独有的问题——后端代码同样常被写得一团糟。因此我实在难以相信,那些人不会把Vue应用也搞得乱七八糟。
不过也许你说得对,它确实可能更优秀。我无从知晓。
别误会我的意思,我不是说Vue是完美的,它也有不少令人烦恼的地方,希望这些问题能得到解决——主要是TypeScript体验不佳,比如props和事件之类的问题。但就日常使用体验而言,我认为Vue比React高效得多,更重要的是对团队中技术水平较低的成员来说,它比React更不容易搞砸。正如我最初评论所言,我维护的公司代码库确实是个怪物,但正因Vue的模式设计,它比同等混乱的React版本更易于处理和修复。
我建议尝试Vue(尤其是带Options API的版本,CompAPI也不错)和Svelte。尽管它在某些方面(如事件/属性系统)比React更复杂,但初学时就能清晰感受到其简洁性。
> 众人抱怨React又慢又糟糕,实则不然。慢的是他们的代码,正确使用时React快得惊人。
既然有能强制规范使用的工具,何必选择需要“规范使用”的方案?
因为我始终不相信存在绝对无法被滥用的编程语言/库/框架。
需要澄清的是,我并非认为React优于Vue。我对Vue不熟悉,或许它确实更优。我只是说根据经验,React本身没问题,问题在于人们过度复杂化导致混乱。这种情况在我经手的几乎所有软件中都存在——后端/前端/任何领域皆然。所以所谓Vue能神奇地避免混乱的说法,我实在难以信服。
我理解这种观点。对我个人而言,Vue提供的生活周期钩子数量有限,因此不易选错。而React则为各种场景都提供了某种形式的钩子,这首先增加了理解应用生命周期和正确选择钩子的难度。
在Vue中,我开始用Pinia管理整个应用的状态(即数据存储)。这种逻辑干净且集中,而React我实在想不出替代方案。
我知道React也能做到简洁,但(依我之见)这需要对框架有更深的理解。
可95%的新创公司融资时都在用那个诅咒般的NextJS框架。
这简直像当年风投资金涌入时人人都在用MongoDB的翻版。我认为这恰是竞争优势——当对手采用这类技术时,你反而能灵活应对并打造更优产品。
我曾因Hacker News上充斥的负面评价,在未尝试前就认定自己讨厌App Router。
如今在三个生产级项目中使用后,我认为这是卓越的同构JS/TS框架。它堪称JS生态中最接近“开箱即用”(如Rails或Phoenix)的系统。
然而我察觉到人们普遍喜欢贬低Next.js,只因他们未曾投入少量时间去真正理解它——尤其那些从Pages Router转来的开发者更易如此。这种谬误在原帖中尤为明显:哀叹“无法使用多个中间件或进行链式调用”……其实完全可以实现?虽然它与Express的实现方式不同,但你绝对能让它按需工作。
在真正理解Express并掌握其用法之前,它真的有那么简单吗?
> 在真正理解Express并掌握其用法之前,它真的有那么简单吗?
是的
sails.js也曾是顶级框架。
读完这些评论让我重燃希望。终于有人说出了我们长期孤立无援的共同心声——Vercel/Next.js正为服务其商业模式而逐渐走向_垃圾化_。
原帖描述的中间件问题并非漏洞,而是症状。当基础日志功能失效,当WebSockets无法使用,当你被迫部署在不支持Node.js API的边缘运行时——这些都是刻意引导你转向Vercel平台的策略。Guillermo及其团队将股权结构置于开放网络之上。
这正是我上月发起https://github.com/openuiai/next.js的初衷。网络是人类最伟大的协作成就。我们正在构建全球去中心化网络,它应当为所有人提供无限制的计算与存储能力。要实现这一愿景,我们需要具备Next.js曾经拥有的强大功能与灵活性的框架。然而我们眼睁睁看着Vercel将其肢解,将开发者锁定在自家平台。
Next.js曾是卓越的框架,如今却沦为供应商锁定的特洛伊木马。众多开发者将其与SharePoint和Lotus Notes相提并论的事实,应当成为警钟。
致所有感到沮丧的人:你们没有错。除非你在Vercel上,否则这个框架正在积极地与你们作对。
我每天都在推进OpenNext.js的开发——虽然仍在攀爬这个庞大意大利面代码库的学习曲线,但已全情投入。我们已在分叉版本上运行OpenUI,该版本支持完整的Node.js中间件和原生WebSockets。待我彻底清理冗余代码(仅搞定CI/CD就耗费400美元解谜),这些功能将并入OpenNext.js。但我愿花时间做好这件事。
Guillermo:Vercel/Next.js不该限制我们的部署选择,它本应尊重网络的开放本质。你心知肚明,却选择了金钱。
公平地说,Next.js只是延续了其产品一贯的演进轨迹:通过牺牲供应商锁定来实现全方位部署指导。意识到Vercel的局限性并非关乎开放网络,而意味着你此刻就该自行搭建服务器。
这段
我公司有位开发同事曾盛赞Next.js的一键部署功能。交谈中才发现他从未亲手搭建或管理过服务器,甚至觉得学习这些纯属浪费时间。
这让我心痛。考虑分享这篇文章,但担心显得过于激进。
我不理解这类抱怨。作为Java和.NET框架出身的人,Next.js是唯一理智的前端框架,其次才是Angular。
此言出自1998年就投身Web开发的老兵——Perl和CGI的黄金年代见证者。
我认为这篇帖子并非无理取闹。光是需要费心思考如何从应用程序中获取日志这件事就够荒谬了,更别提这里还得跳过重重障碍。日志记录本应是任何投入生产环境的系统的核心功能。
有趣,因为大多数框架并非如此——几乎所有地方都需要额外库和配置才能实现日志功能。
在多数语言和框架中,日志功能只需导入、初始化即可完成。这里却远非如此。
真的吗?具体哪些?
因为Java、.NET和Python都确实需要配置和相关基础设施。
> .NET
你大概很久没用.NET了吧?如今日志配置简单得离谱。通常甚至无需配置,因为基础功能已包含在大多数模板中。直接使用Logger类就能运行。
唯一需要耗费超过30分钟的情况,是使用外部日志库时。而这类库大多设计合理且易于使用,因为创建自定义日志提供者实在太简单了。
Log4j的配置简单到只需添加依赖即可使用。当然,你也可以进行更复杂的配置,将日志桥接到其他系统(这需要更多工作量),但开箱即用的体验非常直观且功能齐全。我实在难以相信你会认真辩称某些最常用的语言没有优秀的日志方案。
哪个Java框架能让log4j在正式生产环境中开箱即用?
Spring Boot就是例子,它堪称当今Java开发最主流的方案之一。若你真有论点,直接陈述会更清晰——目前你的发言毫无实质内容。这些问题本可轻易通过搜索找到答案。
又错了。
Spring Boot 无法在无需配置的情况下提供_真正的生产级质量_部署。
仅支持基础日志输出到标准输出?没错。
但这并非_生产级质量_。
_生产级质量_应包含:遥测日志记录、日志轮转压缩、将日志转发至 Kibana 或 Datadog 仪表盘。
这纯属无稽的“苏格兰人谬论”。你先含糊其辞,又在无人理解时摆出高姿态。任何人都能捏造一套任性要求,让所有语言和框架都无法满足。但事实是:对多数语言和框架而言,日志记录本就是个枯燥却已解决的问题。Next.js未能提供此功能,其意义不言而喻。
不亲爱的,暗示日志功能能在生产级部署中开箱即用、无需任何额外配置或代码修改的正是你。既然如此,请在符合条件时让用户满意。
把词语呕吐到标准输出里算什么生产级?
看来你可能错过了函数式编程入门课。我开篇就强调过需要导入并初始化日志功能,这两点都涵盖其中。只有你在自己构造稻草人论点来反驳。这又不是Reddit论坛,你总该能表现得更好些吧。
Angular绝对不是个理性的框架。我曾认为它曾经是,但经历了四年多四次重大破坏性重构的“地毯式抽走”后,你根本无法再信任它。当一个框架犯错如此之多时,我开始怀疑它的真正目的究竟是什么。
Angular在向后兼容性方面表现不差。典型例证: ParamMap曾作为Params的替代方案推出,但用户强烈反对,最终两者并存且不偏袒任何一方:
https://github.com/angular/angular/pull/43529#issuecomment-9…
事后看来颇具讽刺意味,但至少我们不必为更新这种本已正常运作的细微功能而修改所有项目。
真正糟糕的是迁移工具。最佳方案是手动更新 package.json 中的版本号,仔细阅读破坏性变更文档并采取相应措施。
在我看来Angular向来疯狂,但每版迭代都在趋于理性。如今我们终于有了类型化表单(等了很久)、独立组件,最重要的是信号系统——它承担了RxJS的大部分功能,却没有那些杀死初级开发者的隐藏状态和内存泄漏问题。
咨询行业不负责升级,我们只管交付成果,离场后仅提供现有部署版本的维护合约。
升级成本终究需要有人买单,无论过程多痛苦,最终都会体现在项目预算里。
更多开发者该学会将工时成本换算成金钱价值。
这恰恰凸显了“产品导向”与“项目导向”工作的核心差异。项目属于客户的问题($$$),他们通常会因所需成本和投入而犹豫,最终接受风险。即便使用过时框架,实际风险往往微乎其微——“反正还能用,对吧?”
十三年来真正重构导致重大破坏的只有一次,这部分是因为我们积累了大量经验,更因为浏览器本身发生了翻天覆地的变化。我不会说当时处理得完美无缺,但那时候谷歌提供的支持也远不及现在。
我大致明白你的意思(也经历过类似重构…),但具体是哪次重大变更对你影响如此深远?
> Angular绝对不是个理智的框架
我没用过旧版本,但V20和信号系统确实相当不错。
看到基于目录的路径规范时,你是不是怀念起CGI时代的旧时光了?
更像是技术发展像钟摆般在不同世代间反复摆动的讽刺。
前端的一切不过是精心雕琢的粪便,干涸后仍残留着隐约的恶臭。
无论雕琢得多么精致,终究是狗屎
请勿在此发表无实质内容的评论。
JavaScript的表达能力被刻意削弱(参见艾希、Sun公司、Scheme语言)。
尽管如此,他还是偷偷塞进了元编程能力,只是没达到像宏那样显而易见的程度。
所以当顶级厂商从2008-2010年开始认真对待前端时,他们构建的东西基本碾压了让JS本身尚可忍受的特质。取而代之的是,他们基于JavaScript标准命令式原语构建框架,使语言走向更接近C++的风格。
“增长黑客”在此显现,正如备受推崇的苹果公司所践行的垂直整合。将标记语言与状态管理、服务器/客户端流程耦合的唯一途径并非构建框架——这根本算不上框架,因为网络并非由风投公司发明。
若想从中榨取框架价值——即把开发者的实战经验捆绑到_你的_生态系统(将其从通用工具变成领域专用工具,但去他妈的开发者体验)——就必须在技术栈中错误地耦合组件,正如本文开篇已遭诟病的“缺陷功能”所示。
换言之,若想让托管业务的入门流程在2025年仍能奏效,你必须先花一代人的时间灌输糟糕的架构理念。(这正是Meteor.js未能普及的原因。)
一方面,Vercel能在React/TS怪物的漫长阴影下生存实属务实之举。另一方面,它不过是又一家凭空存在就让我的工作和生活毫无理由地变得更艰难的公司——尽管我社交圈里根本没人是他们的客户。
和许多人一样,我也是在谷歌搜索“到底是谁搞出这破玩意儿,害得前端团队现在被卡死,不仅把开发速度拖到0.01倍速还把人脑子烤焦了”时才知晓他们的存在。现在他们来了,我想提醒他们:试着衡量下你们造成的外部效应吧。
有解决方案吗?你们认为最不糟糕的交互式前端方案是什么?
感谢你屏蔽了“shit”这个词。我们努力保持Hacker News的纯净,为家庭和儿童营造良好环境。
不客气