学习 Next.js: 获取数据

#1

为页面获取数据

得益于 Next.js 路由API的优点, 我们知道了如何创建一个具有简介URL的 Next.js 应用程序.

实际上, 我们通常需要从远程数据源获取数据. Next.js 提供了一个标准API用于为页面获取数据. 我们使用一个 async 函数 getInitialProps 来达到获取数据的目的.

以此为基础, 我们能够给以页面从远程数据源获取数据, 然后把数据穿给我们的一个页面组件的属性. 我们可以编写getInitialProps函数让他能够同时在客户端和服务器端运行.

在这节课中, 使用 getInitialProps, 我们将使用 TVmaze API构造一个显示Batman TV Shows 相关信息的应用程序.

现在开始!

设置

下载需要的示例程序:

git clone https://github.com/arunoda/learnnextjs-demo.git
cd learnnextjs-demo
git checkout clean-urls-ssr

用下面的命令运行:

npm install
npm run dv

然后, 访问 http://localhost:3000

获取 Batman Shows

在我们的演示程序中, 显示了一个博客列表, 现在我们改造演示程序以要显示一个Batman TV shows列表.

和之前博客列表的硬编码方式不同, 这次我们从远程服务器获取列表数据

这里我们使用 TVMaze API 获取电视节目信息. 它是一个搜索电视节目信息的API.

首先, 我们需要按照 isomorphic-unfetch. 我们使用这个库来获取数据. 它是一个浏览器 fetch 的简单实现, 并且可以同时工作在客户端和服务器端环境中.

译注: 这类能够同时在客户端和服务器运行的应用程序, 我们称之为同构应用程序

然后, 用下面的代码, 替换 pages/index.js 文件:

import Layout from '../components/MyLayout.js'
import Link from 'next/link'
import fetch from 'isomorphic-unfetch'

const Index = (props) => (
  <Layout>
    <h1>Batman TV Shows</h1>
    <ul>
      {props.shows.map(({show}) => (
        <li key={show.id}>
          <Link as={`/p/${show.id}`} href={`/post?id=${show.id}`}>
            <a>{show.name}</a>
          </Link>
        </li>
      ))}
    </ul>
  </Layout>
)

Index.getInitialProps = async function() {
  const res = await fetch('http://api.tvmaze.com/search/shows?q=batman')
  const data = await res.json()

  console.log(`Show data fetched. Count: ${data.length}`)

  return {
    shows: data
  }
}

export default Index

到现在, 上面的代码一切看来都是很熟悉了, 除了 Index.getInitialProps:

Index.getInitialProps = async function() {
  const res = await fetch('http://api.tvmaze.com/search/shows?q=batman')
  const data = await res.json()

  console.log(`Show data fetched. Count: ${data.length}`)

  return {
    shows: data
  }
}

这是一个静态的 async, 可以把它添加到应用程序中的任何页面. 使用它, 我们可以获取数据, 并且作为页面组件的属性使用.

如你所见, 现在, 我们要获取 Batman TV 电视节目信息, 并且把获取的节目信息, 作为页面组件的 shows 属性进行访问.

如你所见, 上面的 getInitialProps 函数, 它打印一系列获取到的数据到控制台.

现在, 看一下浏览器的控制台和服务器的控制台输出. 然后重新加载页面.

仅服务器

本来我们预想的, 客户端和服务器都能输出同样的信息, 但实际上, 在这种情况下, 输出信息只显示在了服务器端的控制台上. 这是因为, 我们的页面是在服务器端进行渲染的. 我们在服务器上已经获取到了电视节目的数据, 没有理由在客户端再获取一次.

实现信息展示页面

现在我们要实现一个 /post 页面来展示电视节目的详细信息.

首先, 打开 server.js 文件, 用下面的代码修改路由 /p/:id:

server.get('/p/:id', (req, res) => {
    const actualPage = '/post'
    const queryParams = { id: req.params.id }
    app.render(req, res, actualPage, queryParams)
})

然后, 重启应用程序

先前, 我们映射了 title 查询参数到页面, 现在我们重命名为 id.

现在, 用下面的代码替换 pages/post.js 的内容:

import Layout from '../components/MyLayout.js'
import fetch from 'isomorphic-unfetch'

const Post =  (props) => (
    <Layout>
       <h1>{props.show.name}</h1>
       <p>{props.show.summary.replace(/<[/]?p>/g, '')}</p>
       <img src={props.show.image.medium}/>
    </Layout>
)

Post.getInitialProps = async function (context) {
  const { id } = context.query
  const res = await fetch(`http://api.tvmaze.com/shows/${id}`)
  const show = await res.json()

  console.log(`Fetched show: ${show.name}`)

  return { show }
}

export default Post

我们再来看一下 getInitialProps 函数:

Post.getInitialProps = async function (context) {
  const { id } = context.query
  const res = await fetch(`http://api.tvmaze.com/shows/${id}`)
  const show = await res.json()

  console.log(`Fetched show: ${show.Title}`)

  return { show }
}

现在这个函数的第一个参数为一个 context 对象, 其中包含了我们用于获取信息的查询字段.

在我们这个例子中, 我们从查询串中获取电视节目ID, 然后通过它来从 TVMaze API 获取数据.

getInitialProps函数中, 我们添加了一个 console.log 调试输出来显示电视节目的标题. 下面我们来验证我们的程序是否能够正确运行.

打开服务器和客户端控制台, 访问 http://localhost:3000, 点击第一个电视节目标题.

输出显示在客户端还是服务器控制台?

从客户端获取数据

这里, 我们只在客户端的控制台上看到了调试输入. 这是因为我们是通过客户端进行导航的. 因此从客户端获取数据是更好的方式.

如果你直接访问Post页面(例如: http://localhost:3000/p/975), 你将会看到调试输出显示在了服务器端而非客户端.

最后

现在你学到了 Next.js 最为关键的特性: 通用数据获取服务器端渲染(SRR).

我们了解了 getInitialProps, 在大多数情况下, 就足够了. 如果你要了解关于数据获取的更加深入的信息, 参考data fetching 文档.

2 Likes
#2

Github上的代码并不是最新的,能否更新学习下

#3

如何在客户端发起http请求获取数据呢?