资讯专栏INFORMATION COLUMN

nextjs踩坑

JayChen / 690人阅读

摘要:踩坑几乎一整年没咋写文章,主要是懒,加上工作也挺忙。遇到一些坑,也有一些收获这里记录一下。个人习惯使用启动服务。总结说了上面那么多,其实官方文档里都有相关例子,就当我的个人踩坑记录吧。

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101514.html

相关文章

  • Next.js踩坑入门系列(一)— Hello Next.js!

    摘要:接下来我们运行一下。因此,我们需要新建一个目录。接下在再重新启动一下试试。因为默认开启服务端渲染,也就无需我们进行任何的配置,因此现在这个极其简单的应用就是一个。代码地址参考文章服务端渲染与打造高可靠与高性能的同构解决方案 写在前面 说实话,我个人还是觉得文笔越来越不错了,以前的文章都是一个问题闷到天黑,文章写的有点乱由于文章过于庞大导致不是一气呵成的,思路有时候会很混乱。所以我也准备...

    rockswang 评论0 收藏0
  • 这是一个快速构建nextjs应用的工具

    摘要:官方也有一个的工具,但是只能简单的安装基础的依赖,或者通过他们提供的某个例子来构建自己的项目。由于目前还在开发阶段只支持上面这些的配置也是平常我用的比较多的配置。 因为公司的业务需求现在开发都用nextjs来实现React的服务端渲染,为了之后开发方便自己写了个脚手架工具。 官方也有一个create-next-app的工具,但是只能简单的安装基础的依赖,或者通过他们提供的某个例子来构建...

    scola666 评论0 收藏0
  • Next.js之基础概念(二)

    摘要:样式在中写样式,一般可以归为类,一类是基于文件的传统方式包括,等,另一类则是。我们回到我们的代码中,更改,代码如下在标签中,我们写我们的,必须包含在中,否则会报错。至此,的基础概念已经介绍完了,更高级的用法,可以参考官方的例子。 本篇教程基于上一篇的基础,主要讲解服务端渲染,样式以及部署相关的一些知识,若你没有看过上一篇的内容,或者你看过又忘了,建议重新去看一遍。 顺便说一句,Next...

    DirtyMind 评论0 收藏0
  • Next.js之基础概念

    摘要:是一个基于实现的服务端渲染框架,地址为。本文先从简单地基础概念开始,一步一步带大家认识。本篇教程到此结束,后面会跟大家介绍的服务端渲染及以及部署相关的一下概念及示例代码。 Next.js是一个基于React实现的服务端渲染框架,github地址为next.js。 使用Next.js实现服务端渲染是一件非常简单的事,在这里,你完全可以不用自己去写webpack等配置,Next.js全都帮...

    2bdenny 评论0 收藏0
  • Nextjs中文文档

    摘要:中文站点中文站当前翻译版本为。注意将不能使用在子组件中。只能使用在页面中。替换路由组件默认将新推入路由栈中。以防服务端渲染发生错误,建议事件写在生命周期里。禁止文件路由默认情况,将会把下的所有文件匹配路由如渲染为如果你的项目使用 Next.js 是一个轻量级的 React 服务端渲染应用框架。 Next.js中文站点 http://nextjs.frontendx.cn Next.j...

    luckyw 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<