Next.js+Egg.js配置
Next.js简介
Next.js作为React官方钦定的轻量级同构框架,有以下几个特性:
- 开箱即用,支持0配置开发
- 支持约定式路由
- 支持自定义服务端路由,可以配合其他Node框架使用
- 渐进式babel和webpack配置
- 支持客户端渲染
- 社区活跃,团队不太监
- 对前端新技术支持较为全面,比如AMP、TypeScript、PWA、Serverless等
在Nemo FE以React为主要开发框架的前提下SEO项目重构选择Next.js就非常合适
Next.js自定义配置
官方文档在自定义服务端路由的例子中给出了一个demo,可以给后面和Egg.js配置提供参考性
1 | const { createServer } = require('http') |
上面的代码大概可以拆分成两个部分:
1 | const dev = process.env.NODE_ENV !== 'production' |
1.对next进行实例化,缓存环境变量以及一个处理页面资源请求的方法
1 | app.prepare().then(() => { |
2.当前期的实例和方法都准备好以后,下一步就是启动服务了,启动http服务之前先跑了一次app.prepare
,而这个app.prepare
是干什么用的呢,在这里可以先不急着去看prepare的源码,可以先去看看next官方脚手架是怎么跑起来的。
官方给出的命令配置如下:
以dev
为例,可以从next源码中找到对应的cli/next-dev.ts
文件
next-dev.ts
可以看到在dev
启动命令同样调用了app.prepare
这个方法,而前面那个startServer
又是个什么东西,可以根据路径找到对应文件
start-server.ts
看到这里就大概知道官方启动next的流程是先启动一个http服务,启动完后再去调用app.prepare
方法,除了dev
生产模式下的start
也是同样流程,所以这个时候prepare内部是干什么的已经不需要去知道,源码怎么做这边自定义启动next就怎么做
至于是先创建http服务还是先跑app.prepare
方法也不重要,只要在开始做路由匹配前都启动完成即可
Egg.js自定义配置
Egg.js自定义配置较简单,只需要在根目录新建一个app.ts
,调用beforeStart
即可
1 | import {Application} from 'egg' |
beforeStart
定义
所有的配置已经加载完毕,可以用来加载应用自定义的文件,启动自定义的服务
搭建思路
本文采用的Next9.x版本进行配置,已成功进行上线验证
Ok,在分析完Next.js和Egg.js的自定义配置过程后,下面开始思考下🤔怎么把这两个官方团队相互排斥的框架结合在一起做成自己想要的配置
先捋一下搭建思路:
- 首先路由是由Egg控制而不是使用Next的约定式路由,因为契合SEO需求一个页面可能被多个路由使用,所以用Egg控制路由更合适
- 既然项目用Egg启动也就是Next需要自定义在Egg启动的时候实例化,实例化要做两件事情:
- 1.自定义一个render方法然后挂载在上Egg上,因为View层渲染是由Next去完成,然后通过的Egg的Controller返回给客户端
- 2.上文提到Next有一个
getRequestHandler
的方法用于处理路由和静态文件,放到这里路由是不用处理因为这事是Egg做的,但是静态资源还是要由Next做的,所以也应该想办法把这个方法挂到Egg上
- 考虑到SEO需求需要使用Google的
AMP
框架,鉴于之前使用Egg模版引擎Nunjucks
在写H5页和AMP页的时候HTML和CSS都需要分开写&&打包,既然Next支持AMP,为了体现重构的价值争取用最少的代码实现一步到位
大概就是这三点是整个搭建过程需要考虑的
Egg.js+Next.js Initialize Combine
首先使用egg脚手架初始化项目
egg-init [project] –type=simple
按上面说的到的思路在Egg启动的时候对Next进行实例化,那么先来建一个ssr.js
,对next
的实例化主要分为3步:
next
实例化,然后调用prepare
方法- 因为需要通过
next
生成html模版
,已经不需要使用Egg的render
方法,所以要自行封装renderTsx
方法,方法内部调用render
方法返回渲染好的html模版
- 然后把
renderTsx
方法挂载到Egg的ctx
上
server/ssr.ts
1 | import {Application} from 'egg' |
根据Egg.js文档自定义Egg服务需要在根目录建一个app.ts
,并要默认导出一个类,然后引入刚才在ssr.ts
中封装的启动函数
app.ts
1 | import { Application } from 'egg' |
上面说到requestHandler
在挂载到ctx
上后需要在中间件调用,为什么要在中间件调用呢,
通过阅读Next的源码分析了getRequestHandler
调用的全过程,简单总结下调用的过程大概是:
- Next在启动Node服务
createServer
后会调用getRequestHandler
- 调用后截取全部请求URL(这个URL不单指只在浏览器输入的,还包括其他静态脚本文件),同时与内部的
Router
数组做一次匹配 - 如果匹配到对应的前缀比如
/_next/static/
后会回调Router
中的fn
方法,而这个fn
做的事情不限于渲染页面、页面热更替等等
既然要截取全部请求URL那放在中间件就再适合不过了,至于为什么要手动挂载到Egg的中间件上,原因也很简单,因为我们并没有启动Next内部的Node服务,所以要手动调用挂载这个方法
middleware/next.ts
1 | import { Application } from 'egg' |
然后在config
文件夹中配置config.default.ts
文件,默认开启中间件
config/config.default.ts
1 | module.exports = (appInfo: EggAppInfo) => { |
看到这里看官们应该会有一个疑问🤔️,为什么egg的自定义配置用的是common.js
规范而不是esmodule
规范,在我的Next.js使用总结文章中有总结原因,在这里就不再赘述,欢迎各位看官去看一看,说不定还有一些你现在使用Next.js的一些疑难杂症(手动斜眼。
至此Next.js+Egg.js配置已基本结束,楼主还是会一直持续关注Next.js的变化,顺便写一些Next.js源码解析文章,毕竟工欲善其事必先利其器,灵活运用一个框架了解其原理是必不可少的,所以敬请期待🎉🎉🎉