mock server端冗余模块优化next.js dev/build 性能
随着项目的增加,运行next dev
时候电脑越来越卡了,注意到 next-server
这个进程随着热更新的次数增加内存也占用的越来越大。
进行一些排查之后,发现了一个优化点:
就是在server
端导入了比较多的冗余module
,可以通过mock
某些不需要在server
导入的依赖,优化 next.js
dev/build
性能。
实现原理
举个例子:
在某个页面需要调用依赖a
,但是a
不会在server
中运行,运行a
可能是在onClick
中,或者某些强依赖browser
的环境中。
那么此时对于server bundle
来说,导入a
就是多余的。
如果a
的代码量非常大、模块数量非常多或者有很多a
独有的依赖。势必会降低server
构建的性能以及可能的 server
运行的性能。
详见 example/next-js
。
在这个例子中,我们使用了 @web3modal/ethers
和 ethers
,但是这两个依赖并不会在服务端运行,他们依赖的browser
。
所以我们可以尝试mock
这两个依赖的导出,使得业务代码不用做任何更改,同时优化server
端的性能。
如何 mock
通过指定webpack.resolve.alias
来 mock 依赖。
例如:
在 server 构建时候,webpack 解析到如下代码:
import { createWeb3Modal } from "@web3modal/ethers/react"
时。
通过 webpack.resolve.alias
指定 @web3modal/ethers/react
为 mock.js
,
使其真正参与编译的时候被替换为 mock.js
的实现。
// mock.js
function f() {}
function mockHooks() {
return {};
}
const mock = new Proxy(f, {
get(target, p, receiver) {
if (typeof p === "string") {
// 当做 react hooks 使用,确保返回值不是空的
if (p.startsWith("use")) {
return mockHooks;
}
if (p === "__esModule") {
return false;
}
}
// 默认返回自身,确保可以循环引用
return mock;
},
construct(target, argArray, newTarget) {
return mock;
},
});
// 只会在 server 中被导入,使用 cjs 导出
// 并且 es module 不支持 动态导出,需要声明清楚需要导出的 member
// 这里用 cjs 能很好的完成 mock module 这个任务
exports = mock;
module.exports = exports;
对比
这里给出部分对比数据,感兴趣可以运行 example/next-js
查看。
next dev
module count
- 常规
- 优化
访问同一页面next
构建的module
数量有所下降,即在server
端没有去构建@web3modal/ethers
和ethers
。
memory usage
冷启动后
- 常规
- 优化
热更新多次后
- 常规
- 优化
next build
通过运行 node --heap-prof ./node_modules/next/dist/bin/next build
生成的 heapprofile
获取的内存数据
memory usage
- 常规
- 优化
bundle-analyzer
- 常规
- 优化
总结
从以上对比数据来看,mock
掉在server
中用不上的依赖确实能带来有效的性能提升。
最后
抛砖引玉,希望能看到更多的想法,当然也可能后续turbo pack
稳定后也不需要乱七八糟的hack
方法。
可以直接安装ssr-optimize
试试咸淡。
转载自:https://juejin.cn/post/7353060906698014755