Next.js踩坑

几乎一整年没咋写文章,主要是懒,加上工作也挺忙。但是想趁着年底发一篇,希望明年更勤奋一点。其实不是没东西写,就是想深入一个东西还是很困难的,要查各种资料,最终还是懒就是了。

next.js是react的同构库,很多文章里把他当作一个脚手架,也不是不行,但是个人认为next.js比一般的脚手架能做的更多,但也有局限性。这两天下班回去实践了一下nextjs的开发。遇到一些坑,也有一些收获这里记录一下。

请求数据

nextjs没有客户端的生命周期,只有一个静态方法getInitialProps,所以获取接口数据也只能在这个方法里了。getInitialProps的返回数据就作为该组件的props。getInitialProps有两个参数:req和res,也就是我们非常熟悉的http参数。说句题外话,现有的node web框架都是req作为输入,res作为输出,增加各种中间件。

所以个人觉得nextjs的组件形式太适合无状态组件了。下面是一个简单的例子代码:

// 获取电影列表并渲染

class MovieList extends Component {

static async getInitialProps(){

const data = await getMovieList()

return {

list: data

}

}

render () {

return (

{this.props.list.map(movie => {

})}

)

}

}

当然这里最终仅仅是服务端输出的列表,我们可能还会有其他操作,比如删除加载下一页之类的,但是这些操作都没必要在服务端操作的。添加几个相应的方法即可。

路由管理

nextjs的路由是基于文件系统的,相当清晰和简单,比如在pages文件夹下面增加一个movie-detail组件,并写上相应的代码,我们就可以访问/movie-detail 这个路由了。起初觉得这样的路由形式实在太优雅了,但是用久了就会发现很多问题。

路由嵌套

首先是嵌套路由,比如我想建立/user/profile这个路由,这个其实很好解决,就是在pages文件夹下面依次嵌套就行了:

具名路由

其次是没有官方实现具名路径,什么是具名路径呢?就是/movie/:id这里这种形式,个人感觉nextjs在这方面是追随react-router4的。vuejs的同构框架nuxtjs则不存在这个问题,因为vue-router本身也是统一管理路由的。先不说这种情况的好坏,还是找找解决方案吧。

根据我找到的实例和文档,目前有两种解决方案:

使用query代替具名路

下图可以看到其实在nextjs router里query是存在的。

那我们需要访问具名路由页面的时候可以这么写, 将id用query传过去/movie-detail?id=xxx:

// 电影详情页面

class MovieDetail extends Component {

static async getInitialProps({ req }) {

const { id } = req.query

const detail = await getDetail(id)

return {

detail

}

}

render () {

return (

// do anything you want

)

}

}

custom server 解决

使用query传参数过去确实可以解决问题,但是太不优雅,与rest的思想也不太符合。所以next社区找到了另一个解决方案,使用custom server。

在说具体方案之前我们我们可以了解一下,说到底nextjs并不是一个生成静态资源的脚手架,next最终还是要单独部署node服务的。也就是nextjs其实内置了一个http服务,如果我们不使用custom sever的话,内置服务还是可以很好的帮我们完成渲染页面的任务。

但是如果我们的node不仅仅是渲染页面,还需要写接口。那么这时候的情况就很类似传统后端的开发模式了:不仅仅需要写接口还需要渲染页面。

很显然nextjs的内置http服务是无法完成这个任务的,我们需要更加完善的web 框架。毕竟专业的事还是交给专业的。这时候就是custom server大显身手的时候了。nextjs里也有一系列的例子:

那么custom server是如何解决具名路径的问题的呢?我们是借用nextjs的渲染能力。这里以express为例,具体代码如下:

// server.js

const express = require('express')

const next = require('next')

const dev = process.env.NODE_ENV !== 'production'

const app = next({ dev, quiet: false })

const handle = app.getRequestHandler()

const SERVE_PORT = process.env.SERVE_PORT || 8001

app.prepare().then(() => {

const server = express()

server.get('/movie-detail/:id', async (req, res) => {

// 渲染movie-detail这个组件

const html = await app.renderToHTML(req, res, '/movie-detail', req.query)

res.send(html)

})

server.get('*', (req, res) => handle(req, res))

server.listen(SERVE_PORT, err => {

if (err) throw err

console.log(`> Ready on http://localhost:${SERVE_PORT}`)

})

})

上面是server.js的简略代码,当然在组件里我们也要做相应处理,代码如下

// /pages/movie-detail.jsx

// 电影详情页面

class MovieDetail extends Component {

static async getInitialProps({ req }) {

const { id } = req.params

const detail = await getDetail(id)

return {

detail,

id

}

}

render () {

return (

// do anything you want

)

}

}

页面缓存

对于csr的的react应用来说,渲染耗时100ms并不是什么太大问题,但是到了服务端,100ms很明显是没法忍受的。首先客户端渲染并不会造成服务器资源的浪费,其实也不会对服务器造成太大鸭梨。但是服务端就不一样了。一旦用户量大了,势必会引起各种问题,所以页面缓存还是很有必要的。

具体页面缓存在哪里并不是我们考量的范围,同样页面缓存也需要用到custom server,具体服务端框架自定吧。这里以lru-cache为例做一个简单的页面缓存,其实换成其他的诸如redis也是没有任何问题的。

const dev = process.env.NODE_ENV !== 'production'

const next = require('next')

const express = require('express')

const LRUCache = require('lru-cache')

const ssrCache = new LRUCache({

max: 1000, // cache item count

maxAge: 1000 * 60 * 60, // 1 hour

})

const app = next({ dev, quiet: false })

const handle = app.getRequestHandler()

const SERVE_PORT = process.env.SERVE_PORT || 8001

app.prepare().then(() => {

const server = express()

server.get('/', async (req, res) => {

renderAndCache(req, res, '/', { ...req.query })

})

server.get('/movie-detail/:id', async (req, res) => {

renderAndCache(req, res, '/movie-detail', { ...req.query })

})

server.get('*', (req, res) => handle(req, res))

server.listen(SERVE_PORT, err => {

if (err) throw err

console.log(`> Ready on http://localhost:${SERVE_PORT}`)

})

})

const getCacheKey = req => `${req.url}`

// 缓存并渲染页面,具体是重新渲染还是使用缓存

async function renderAndCache(req, res, pagePath, queryParams) {

const key = getCacheKey(req)

if (ssrCache.has(key)) {

res.setHeader('x-cache', 'HIT')

res.send(ssrCache.get(key))

return

}

try {

const html = await app.renderToHTML(req, res, pagePath, queryParams)

// Something is wrong with the request, let's skip the cache

if (res.statusCode !== 200) {

res.send(html)

return

}

// Let's cache this page

ssrCache.set(key, html)

res.setHeader('x-cache', 'MISS')

res.send(html)

} catch (err) {

app.renderError(err, req, res, pagePath, queryParams)

}

}

其中renderAndCache是关键。这里判断页面是否有缓存,如果有的话则直出缓存内容。否则的话就重新渲染。至于缓存时间还有缓存大小看个人设置了,这里不赘述了。

部署上线

部署上线这一块实在没什么好说的,简单的话直接起一个node服务的就可以,复杂一点就要包括报警重启等等,都是看个人情况的。

个人习惯使用supervisor启动node服务。

总结

说了上面那么多,其实官方文档里都有相关例子,就当我的个人踩坑记录吧。

对于nextjs来说,我认为如果是展示型的应用,就应该放心大胆的用起来。不光开发快还爽,同时屏蔽webpack配置,有什么理由不用?

如果是功能性的,比如一系列的绘图组件则完成没必要使用了,对于canvas之类的还是必须用客户端渲染,然而nextjs又没有生命周期,用nextjs可能会相当坑。

对于个人开发这我则是相当推荐。何必去配置webpack浪费生命啊。

如果是完全静态的应用,我推荐gatsbyjs。具体怎么使用则是另外一个话题了。

如有谬误,轻点喷。 over

android原生调用nextjs方法,nextjs踩坑相关推荐

  1. android settext方法,Android中EditText setText方法的踩坑实战

    1.平平常常中就这样开始 某一天,我准备做一个搜索功能,这个搜索功能呢大概是在主活动A中,用EditText接收输入,当EditText监听到输入框中内容有变化,跳转到活动B中,活动B中准备有搜索历史 ...

  2. android代码混淆个人总结及踩坑

    android代码混淆个人总结及踩坑 前言 公司项目使用组件化开发的形式,需要对自己负责的模块进行一些混淆配置,关于混淆相信做android开发的都或多或少有过一些接触,通过对混淆文件的配置从而将代码 ...

  3. android 原生调用js,js调用原生

    原生调用js方法,带参数 activityBaseWebAddWebView.loadUrl("javascript:changeColor('" + viewColor + &q ...

  4. 原生Javascript 操作 css类名 - 踩坑篇

    文章目录 原生Javascript 操作 css类名 效果图示下: 案例 · 代码如下: 重要代码提示: 其他无关参考: 官方参考: 原生Javascript 操作 css类名 不建议用 .class ...

  5. springCloud项目不能向EurekaServer 注册多个EurekaClient时(端口不一致)方法及踩坑经历

    spring cloud 问题说明:springCloud项目不能向EurekaServer 注册多个EurekaClient时(端口不一致)方法及踩坑经历: 前提--->已经能够通过Eurek ...

  6. vue项目中将视频链接分享至推特的解决方法及踩坑记录

    vue项目中将视频链接分享至推特的解决方法及踩坑记录 将动态改变的视频链接分享至推特,并希望能直接在推特上播放视频的需求实现方法及踩坑记录 如果只要将文本或链接分享到推特,不需要推特识别图片/视频等媒 ...

  7. android小程序_小程序踩坑记

    小程序踩坑记 希望这个文章能尽量记录下小程序的那些坑,避免开发者们浪费自己的生命来定位到底是自己代码导致的还是啥神秘的字节跳变原因. 前记 小程序大多数坑是同一套代码在不同平台上表现不一致导致的,微信 ...

  8. android sharesdk qq授权失败,项目踩坑之Share SDK指定平台分享

    现在很多应用都包含了社会化分享的功能,最近由于项目需要,所以亲自去踩了踩Mob社会化分享的坑.接下来就介绍一下怎样集成Share SDK吧!!! 一.集成ShareSDK的步骤: 1)注册ShareS ...

  9. weex-eros+vue Android拍照并预览图片踩坑【小白向】

    [TOC] 最近做一个移动端的手机拍照预览,碰了不少墙,但还是遇到好心人帮助,成功预览.感谢"小豬仔"~ 开干之前,请先阅读 Weex-Eros文档 和 Vue教程 Windows ...

最新文章

  1. jquery checkbox勾选/取消勾选的诡异问题
  2. [实战虚拟化]无需借助虚机安装,就能从VHD原生启动
  3. Realm Configuration HOW-TO--官方
  4. Python学习笔记__1.5章 循环
  5. docker 打包_Springboot2.0学习11 使用maven插件打包docker部署应用
  6. CSDN 编程挑战——《coder的计算器》
  7. mysql 主从 keepalived_MySQL之双向主从加keepalived高可用
  8. 在ASP.NET中防止注入攻击[翻译]
  9. 最详细的YOLOv3论文笔记
  10. Odoo10教程---模块化三:模型约束,高级视图,工作流,安全性,向导,国际化和报表等
  11. linux用户和用户组及权限管理
  12. 转:C++ map的基本操作和使用
  13. Ubuntu下配置JDK
  14. xml属于php还是js,xml是什么
  15. AI初学者用什么软件?适合AI新手的9款人工智能开源软件
  16. Lenovo System x 硬件Windows Server驱动下载
  17. 吴恩达 深度学习 2021版 笔记
  18. word导出PDF,彩色无故变成黑色的解决办法。
  19. 从三本院校到斩获字节跳动后端研发Offer
  20. 论文阅读SG-PBFT: a Secure and Highly Efficient Blockchain PBFT Consensus Algorithm for IoV

热门文章

  1. 【图解DSA数字签名算法】DSA签名算法的Python实现 | 物联网安全 | 信息安全
  2. flex 联机游戏开发 - 四国军棋游戏:(二)棋盘棋子
  3. 药学薛定谔专题干货总结
  4. Java17 新特性确定,Java之父:终于可以和一个长达25年的漏洞说再见了
  5. 寒假学习心得--从0开始学破解
  6. 监管趋严,在线教育机构要如何做直播呢?
  7. pb调用精伦电子sdtapi.dll读卡函数的心得
  8. 杜洋STM32100步回顾总结
  9. 【教程】dropbox+droppages搭建静态网页
  10. 加米谷大数据干货:Kafka如何实现每秒上百万的超高并发写入?