Next.js 入門

歡迎關注我的公衆號睿Talk,獲取我最新的文章:
clipboard.png

一、前言

當使用 React 開發系統的時候,常常需要配置很多繁瑣的參數,如 Webpack 配置、Router 配置和服務器配置等。如果需要做 SEO,要考慮的事情就更多了,怎麼讓服務端渲染和客戶端渲染保持一致是一件很麻煩的事情,需要引入很多第三方庫。針對這些問題,Next.js提供了一個很好的解決方案,使開發人員可以將精力放在業務上,從繁瑣的配置中解放出來。下面我們一起來看看它的一些特性。

二、特性介紹

Next.js 具有以下幾點特性:

  • 默認支持服務端渲染
  • 自動根據頁面進行代碼分割
  • 簡潔的客戶端路由方案(基於頁面)
  • 基於 Webpack 的開發環境,支持熱模塊替換
  • 可以跟 Express 或者其它 Node.js 服務器完美集成
  • 支持 Babel 和 Webpack 的配置項定製

三、Hello World

執行以下命令,開始 Next.js 之旅:

mkdir hello-next
cd hello-next
npm init -y
npm install --save react react-dom next
mkdir pages

package.json中輸入以下內容:

{
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  }
}

在 pages 文件夾下,新建一個文件 index.js

const Index = () => (
  <div>
    <p>Hello Next.js</p>
  </div>
)

export default Index

在控制檯輸入npm run dev,這時候在瀏覽器輸入http://localhost:3000,就能看到效果了。

clipboard.png

四、路由

Next.js 沒有路由配置文件,路由的規則跟 PHP 有點像。只要在 pages 文件夾下創建的文件,都會默認生成以文件名命名的路由。我們新增一個文件看效果pages/about.js

export default function About() {
  return (
    <div>
      <p>This is the about page</p>
    </div>
  )
}

在瀏覽器輸入http://localhost:3000/about,就能看到相應頁面了。

clipboard.png

如果需要進行頁面導航,就要藉助next/link組件,將 index.js 改寫:

import Link from 'next/link'

const Index = () => (
  <div>
    <Link href="/about">
      <a>About Page</a>
    </Link>
    <p>Hello Next.js</p>
  </div>
)

export default Index

這時候就能通過點擊鏈接進行導航了。

如果需要給路由傳參數,則使用query string的形式:

 <Link href="/post?title=hello">
   <a>About Page</a>
 </Link>

取參數的時候,需要藉助框架提供的withRouter方法,參數封裝在 query 對象中:

import { withRouter } from 'next/router'

const Page = withRouter(props => (
  <h1>{props.router.query.title}</h1>
))

export default Page

如果希望瀏覽器地址欄不顯示query string,可以使用as屬性:

<Link as={`/p/${props.id}`} href={`/post?id=${props.id}`}
  <a>{props.title}</a>
</Link>

這時候瀏覽器會顯示這樣的url:localhost:3000/p/12345

五、SSR

Next.js 對服務端渲染做了封裝,只要遵守一些簡單的約定,就能實現 SSR 功能,減少了大量配置服務器的時間。以上面這個 url 爲例子,直接在瀏覽器輸入localhost:3000/p/12345是會返回404的,我們需要自己實現服務端路由處理的邏輯。下面以express爲例子進行講解。新建一個 server.js 文件:

const express = require('express')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app
  .prepare()
  .then(() => {
    const server = express()

    // 處理localhost:3000/p/12345路由的代碼
    server.get('/p/:id', (req, res) => {
      const actualPage = '/post'
      const queryParams = { title: req.params.id }
      app.render(req, res, actualPage, queryParams)
    })

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

    server.listen(3000, err => {
      if (err) throw err
      console.log('> Ready on http://localhost:3000')
    })
  })
  .catch(ex => {
    console.error(ex.stack)
    process.exit(1)
  })

當遇到/p/:id這種路由的時候,會調用app.render方法渲染頁面,其它的路由則調用app.getRequestHandler方法。

無論是服務端渲染還是客戶端渲染,往往都需要發起網絡請求獲取展示數據。如果要同時考慮 2 種渲染場景,可以用getInitialProps這個方法:

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(`https://api.tvmaze.com/shows/${id}`)
  const show = await res.json()

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

  return { show }
}

export default Post

獲取數據後,組件的props就能獲取到getInitialProps return 的對象,render 的時候就能直接使用了。getInitialProps是組件的靜態方法,無論服務端渲染還是客戶端渲染都會調用。如果需要獲取 url 帶過來的參數,可以從context.query裏面取。

六、CSS in JS

對於頁面樣式,Next.js 官方推薦使用 CSS in JS 的方式,並且內置了styled-jsx。用法如下:

import Layout from '../components/MyLayout.js'
import Link from 'next/link'

function getPosts() {
  return [
    { id: 'hello-nextjs', title: 'Hello Next.js' },
    { id: 'learn-nextjs', title: 'Learn Next.js is awesome' },
    { id: 'deploy-nextjs', title: 'Deploy apps with ZEIT' }
  ]
}

export default function Blog() {
  return (
    <Layout>
      <h1>My Blog</h1>
      <ul>
        {getPosts().map(post => (
          <li key={post.id}>
            <Link as={`/p/${post.id}`} href={`/post?title=${post.title}`}>
              <a>{post.title}</a>
            </Link>
          </li>
        ))}
      </ul>
      <style jsx>{`
        h1,
        a {
          font-family: 'Arial';
        }

        ul {
          padding: 0;
        }

        li {
          list-style: none;
          margin: 5px 0;
        }

        a {
          text-decoration: none;
          color: blue;
        }

        a:hover {
          opacity: 0.6;
        }
      `}</style>
    </Layout>
  )
}

注意<style jsx>後面跟的是模板字符串,而不是直接寫樣式。

七、導出爲靜態頁面

如果網站都是簡單的靜態頁面,不需要進行網絡請求,Next.js 可以將整個網站導出爲多個靜態頁面,不需要進行服務端或客戶端動態渲染了。爲了實現這個功能,需要在根目錄新建一個next.config.js配置文件:

module.exports = {
  exportPathMap: function() {
    return {
      '/': { page: '/' },
      '/about': { page: '/about' },
      '/p/hello-nextjs': { page: '/post', query: { title: 'Hello Next.js' } },
      '/p/learn-nextjs': { page: '/post', query: { title: 'Learn Next.js is awesome' } },
      '/p/deploy-nextjs': { page: '/post', query: { title: 'Deploy apps with Zeit' } }
    }
  }
}

這個配置文件定義了 5 個需要導出的頁面,以及這些頁面對應的組件和需要接收的參數。然後在package.json定義下面 2 個命令,然後跑一下:

{
  "scripts": {
    "build": "next build",
    "export": "next export"
  }
}

npm run build
npm run export

跑完後根目錄就會多出一個out文件夾,所有靜態頁面都在裏面。

clipboard.png

八、組件懶加載

Next.js 默認按照頁面路由來分包加載。如果希望對一些特別大的組件做按需加載時,可以使用框架提供的next/dynamic工具函數。

import dynamic from 'next/dynamic'

const Highlight = dynamic(import('react-highlight'))

export default class PostPage extends React.Component {
    renderMarkdown() {
      if (this.props.content) {
        return (
          <div>
            <Highlight innerHTML>{this.props.content}</Highlight>
          </div>
        )
      }

      return (<div> no content </div>);
    }

    render() {
      return (
        <MyLayout>
          <h1>{this.props.title}</h1>
          {this.renderMarkdown()}
        </MyLayout>
      )
    }
  }
}

當 this.props.content 爲空的時候,Highlight 組件不會被加載,加速了頁面的展現,從而實現按需加載的效果。

九、總結

本文介紹了 Next.js 的一些特性和使用方法。它最大的特點是踐行約定大於配置思想,簡化了前端開發中一些常用功能的配置工作,包括頁面路由、SSR 和組件懶加載等,大大提升了開發效率。

更詳細的使用介紹請看官方文檔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章