前言

本文会总结使用Next.js在具体业务场景下碰到的一些大坑以及具体解决方案,在开始总结前先表达下对ZEIT团队的敬佩,感谢🙏他们开源出一款优秀的同构框架,解救了曾经蛋疼的陷在使用模版引擎做SSR项目的开发者(比如我),通过Next.js重构后的项目更加轻量化,性能上也有大幅度的提升,如下图

重构前
01.png
重构后
02.png

性能数据是通过ABTest使用开源的Nemetric抓取FetchTimeFirstContentfulPaint两个性能指标,然后传到kibana上对比,对比后的性能数据在不同纬度下提升20%-30%,这里多提一句可能很多人对优化后的效果很难感知到,或者优化的价值在哪,但实际上根据谷歌发布的性能为何至关重要,真的是一寸光阴一寸金,所以在看到这个数据对比后我的感觉就是:

前言讲完,就开始讲讲总结一下使用了大半年Next.js的感受了。

Next.js适用场景

首先写Next.js之前要先了解它适合用在什么地方,经过看文档总结无外乎以下三种:

  • SEO WebSites: 需要SEO自然流量的项目,如果资讯站、博客站等
  • Server Render WebSites: 用户增长业务型项目,需要保持较高转化率,如活动H5等
  • Static WebSites: 静态网页,如app官网等

重点是第一点和第三点Next.js都做的非常的优秀,举个例子:

  • SEO Sites现在基本只面向海外(也就是Google,至于国内搜索引擎什么的大家懂的),而Google推出了AMP框架专门用于提升Google搜索排名,简而言之要做海外业务的产品配套的官网之类的websites基本都会用到AMP,而Next.js刚好可以兼容AMP使用,这点很赞。
  • 支持打包静态网页,换句话解释就是用Next写完一些没有接入服务端的静态页面后可以直接用next export打包成HTML文件,在不影响页面性能的下又可以让开发体验感没有降低。

如果你开发的业务满足上述三点中的两点,相信Next.js可以满足你当前的业务需求。

与Egg.js结合使用

Next.js官方github有一个example library,里面主要是Next.js与其他技术结合使用的一些demo,但没有与Egg.js结合的例子,而在Egg.js官方github有人提过issueEgg.js怎样去接入Next.js,官方给出的答复是Koa/Egg的底层实现与基于Express实现的Next有冲突,不建议两者结合使用。所以网上对如何把Egg.js和Next.js一起使用的资料比较稀缺。

解决方案:
Next.js+Egg.js配置

上面的搭建配置是经阅读了一点Next.js源码探索出来的,现在已经可以支持我司推广站正常运行。

按页面分割样式

在开发的时候由于VidMate Sites是多页应用,按预期是每个页面只需要加载对应页面的css文件和公共css文件,但是查看network的时候发现每一个页面都是加载同一个styles.css文件

经过查看Next的css插件next-css源码定位到问题在哪

1
2
3
4
5
6
7
8
if (!isServer) {
config.optimization.splitChunks.cacheGroups.styles = {
name: 'styles',
test: new RegExp(`\\.+(${[...fileExtensions].join('|')})$`),
chunks: 'all',
enforce: true
}
}

上面配置的含义是全部fileExtensions文件中引入的css文件全部抽取出来打包成styles.cssfileExtensions就是webpack配置中的entry这种做法对单页应用是很有用的,全部组件页面css文件集中成一个,原来的n个css文件请求减少至一个,但是对于多页应用来说这种分割方式是不适合的,因为跳转页面需要重新请求,这个时候需要做的是减少页面文件的体积,所以腾讯新闻使用styled-jsx应该也是这个原因

解决方案:
使用styled-jsx方案,自动打包到每个页面对应的js中,但由于写styled-jsx是在.tsx中所以开发的时候不会有语法提示,所以可以用另一种方式引入scss文件转化为styled-jsx,需要在next.config.js中配置一下

next.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
webpack: (config, { defaultLoaders }) => {
config.module.rules.push({
test: /\.(sa|sc|c)ss$/,
use: [
defaultLoaders.babel,
{
loader: require('styled-jsx/webpack').loader,
options: {
type: 'scoped'
}
},
'sass-loader'
]
})
...
return config
}

兼容AMP

由于我司做的是海外产品需要做谷歌SEO,所以就用到了谷歌开源的AMP框架,这个框架主要是针对谷歌进行优化,兼容AMP是目前使用Next最大的问题,因为这相当于把两个黑盒框架组合使用,难度是可想而知的

AMP也是一个对可配置性限制很大的框架,特别是对css只能嵌入不能引入,并且大小限制在50000Bytes内,然内嵌的自定义css要求以<style amp-custom>.....</style>方式写入,如果缺少amp-custom就无法通过amp validator。而按正常的css开发套路需要对全局样式做一个通用定义,特别是移动端做rem适配需要对html文档做一个初始值font-size定义。

而很操蛋的在<Head/>组件使用<style>...</style>定义全局样式是缺少上面提到的amp-custom属性的,

使用<style jsx global>...</style>又可能出现初始样式加载失败的情况

解决方案:
初始化样式存放在一个reset文件,然后在页面样式文件中引入。

Typescript编译兼容

在使用Egg.js文档关于typescript配置的的时候当egg端使用esmodule规范开发会无法通过编译,经过我本人的踩坑,终于发现是tsconfig配置有关,是的没错,就跟下面这一行有关

1
"module": "esnext"

module的意思是指定生成哪个模块系统代码,而因为nodejs执行的是common.js规范,所以改成common.jsnode端使用esmodule规范进行编译就没有问题,但悲剧的是这个配置next是强制性的,就算改成common.js也会变成esnext,so,先凑合着用:)

我基于公司现有ts项目的tsconfig.json文件配置了两个tsconfig文件,为啥会有两个呢,因为在Nextv9版本中支持typescript0配置,所以会在编译中自动生成tsconfig配置,但是这个配置对编译egg端就不太行,所以只能用这种折中办法,ts.egg.json用于编译egg,tsconfig.json编译next,然后tsconfig.json去继承ts.egg.json,还有就是需要保持typescript版本在3.7以上,这样在开发和生产编译中就不会产生编译报错

具体json配置可以到我的github上找,这里篇幅略长就不放出来了。

生产编译的时候需要先把egg自定义配置(app.tsssr.ts)打包编译,推荐使用npm run local-tsc进行本地编译,打包到线上服务器后再使用npm run tsc编译egg端的业务代码

package.json

1
2
3
4
"scripts": {
"tsc": "ets && tsc -p ts.egg.json",
"local-tsc": "tsc app.ts && tsc ./server/ssr.ts",
},

其他总结将持续更新,敬请期待~~