跟文檔學習next.js

前言:Next.js 是一個輕量級的 React 服務端渲染應用框架。 html

 

Next.js中文點擊這裏 react

Next.js中文站Github點擊這裏git

新建文件夾安裝它:github

npm install --save next react react-dom

將下面腳本添加到 package.json 中:npm

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

這裏注意。啓動項目服務後,react默認的端口號是3000,而next的入口文件默認是隱藏的,這點後面再說。因此若是你有項目端口已經用3000的端口號,那麼能夠修改package.json 中的腳本:json

{
  "scripts": {
    "dev": "next -p 2019",
    "build": "next build",
    "start": "next start"
  }
}

如上,在dev後面添加「-p 端口號」便可;瀏覽器

因爲Next.js 只支持React 16,咱們使用 React 16 的特性,因此不得不放棄對 React 15 以及如下版本的支持。服務器

Next.js 具備如下幾點特性:app

  • 默認支持服務端渲染
  • 自動根據頁面進行代碼分割
  • 簡潔的客戶端路由方案(基於頁面
  • 基於 Webpack 的開發環境,支持熱模塊替換
  • 能夠跟 Express 或者其它 Node.js 服務器完美集成
  • 支持 Babel 和 Webpack 的配置項定製
  • 文件系統是主要的 API. 每一個.js 文件將變成一個路由,自動處理和渲染。
  • 以pages文件做爲服務端的渲染和索引
  • 熱加載
  • 靜態文件服務. ./static/ 映射到 /static/ (能夠 建立一個靜態項目文件夾在你的項目中,裏面放置一些圖片和字體等)

 繼續:新建 ./pages/index.js 到你的項目中;框架

 

在咱們的項目中,確定不止一個項目的。那麼咱們就須要在多個頁面之間進行跳轉,就要藉助next/link組件,下面咱們新增一個頁面game頁面並將index頁面進行改寫;

game頁面

 

Link標籤支持任意react組件做爲其子元素,不必定要用a標籤,只要該子元素能響應onClick事件,Link標籤不支持添加style和className等屬性,若是要給連接增長樣式,須要在子元素上添加;

因爲next支持熱加載,因此直接保存就能夠看到效果哦;

如今已經能夠正常跳轉了,那麼就須要傳遞參數了,若是須要給路由傳參數,則使用query string的形式:

static async getInitialProps ({ req }) {
    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent
    return { userAgent }
  }

  render () {
    return (
      <div className="app-box">
        <p>我是next.js頁面</p>
        <Link href="/game?title=hello">
          <a>To Game Page</a>
        </Link>
        <p>{this.props.userAgent}</p>
      </div>
    )
  }
static async getInitialProps ({ req, query }) {
    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent
    return { userAgent, query }
  }
  render () {
    console.log(this.props.query);
    return (
      <div className="game-box">
        <p>我是game頁面</p>
        <Link href="/">
          <a>To index Page</a>
        </Link>
        <p>{this.props.query.title}</p>
      </div>
    )
  }

從上面兩段代碼能夠看到:

  • 當頁面渲染時加載數據,咱們使用了一個異步方法getInitialProps。它能異步獲取 JS 普通對象,並綁定在props上;
  • 當服務渲染時,getInitialProps將會把數據序列化,就像JSON.stringify。因此確保getInitialProps返回的是一個普通 JS 對象,而不是DateMap 或 Set類型;
  • 當頁面初次加載時,getInitialProps只會在服務端執行一次。getInitialProps只有在路由切換的時候(如Link組件跳轉或路由自定義跳轉)時,客戶端的纔會被執行;
  • 當頁面初始化加載時,getInitialProps只會加載在服務端。只有當路由跳轉(Link組件跳轉或 API 方法跳轉)時,客戶端纔會執行getInitialProps;
  • 注意:getInitialProps將不能使用在子組件中。只能使用在pages頁面中。

getInitialProps入參對象的屬性以下:

  • pathname :URL 的 path 部分;
  • query : URL 的 query 部分,並被解析成對象;
  • asPath : 顯示在瀏覽器中的實際路徑(包含查詢部分),爲String類型;
  • req : HTTP 請求對象 (只有服務器端有);
  • res : HTTP 返回對象 (只有服務器端有);
  • jsonPageRes :獲取數據響應對象(只有客戶端有);
  • err : 渲染過程當中的任何錯誤;

固然除了上面的Link跳轉以外,還能夠經過事件跳轉,那麼就須要藉助next/router實現客戶端路由切換:

import Router from 'next/router'

export default () =>
  <div>
    Click <span onClick={() => Router.push('/game')}>here</span> to read more
  </div>

這個Router對象的 API 以下:

  • route : 當前路由的String類型;
  • pathname : 不包含查詢內容的當前路徑,爲String類型;
  • query : 查詢內容,被解析成Object類型. 默認爲{};
  • asPath : 展示在瀏覽器上的實際路徑,包含查詢內容,爲String類型;
  • push(url, as=url) : 頁面渲染第一個參數 url 的頁面,瀏覽器欄顯示的是第二個參數 url;
  • replace(url, as=url) : performs a replaceState call with the given url;
  • beforePopState(cb=function) : 在路由器處理事件以前攔截;

注意:Router.beforePopState() 截斷Router操做的設置只有在客戶端生效(需在componentDidMount中設置)且進入此函數中的方法只有Router棧中有值的時候才能夠!

push 和 replace 函數的第二個參數as,是爲了裝飾 URL 做用。若是你在服務器端設置了自定義路由將會起做用;

 ok,經過上面的瞭解,咱們已經學會了頁面跳轉的幾種方式,既然能夠跳轉,那麼能夠監聽跳轉嗎?即路由事件:

監聽路由相關事件。 下面是事件支持列表:

  • routeChangeStart(url) --- 路由開始切換時觸發
  • routeChangeComplete(url) --- 完成路由切換時觸發
  • routeChangeError(err, url) --- 路由切換報錯時觸發
  • beforeHistoryChange(url) --- 瀏覽器 history 模式開始切換時觸發
  • hashChangeStart(url) --- 開始切換 hash 值可是沒有切換頁面路由時觸發
  • hashChangeComplete(url) --- 完成切換 hash 值可是沒有切換頁面路由時觸發

咱們嘗試調用路由開始切換的事件:routeChangeStart

  // 監聽路由開始切換時觸發
  static onListenRouteChangeStart = () => {
    Router.events.on('routeChangeStart', (url) => {
      console.log('routerUrl' + url);
    });
  }

componentDidMount () { RouterApi.onListenRouteChangeStart(); }

若是不須要監聽了,請釋放它:Router.onRouteChangeStart = null;

既然都聊都了路由這塊,那麼就說說路由的另外一個概念:淺層路由;

所謂淺路由模式,其實就是淺層路由容許你改變 URL 可是不執行getInitialProps生命週期。你能夠加載相同頁面的 URL,獲得更新後的路由屬性pathnamequery,並不失去 state 狀態。

你能夠給Router.push 或 Router.replace方法加如:shallow: true參數。以下面的例子所示:

const href = '/?counter=10'
const as = href
Router.push(href, as, { shallow: true });

如今 URL 更新爲/?counter=10。在組件裏查看this.props.router.query你將會看到更新的 URL。

須要注意的是:淺路由模式只支持相同的 URL,若是頁面路由變化,仍是會觸發 getInitialProps;

更多的使用場景,可能相似與分頁數據,咱們只改變 page ,而不須要整個初始化。固然,咱們能夠監聽:

componentWillReceiveProps(nextProps) {
  const { pathname, query } = nextProps.url
  // fetch data based on the new query
}

你能夠在componentdidupdate鉤子函數中監聽 URL 的變化:

componentDidUpdate(prevProps) {
  const { pathname, query } = this.props.router
  // verify props have changed to avoid an infinite loop
  if (query.id !== prevProps.router.query.id) {
    // fetch data based on the new query
  }
}

對於路由的一些基本狀況如上了,有具體問題具體分析;

那麼下面咱們來練習一下高階組件:若是你想應用裏每一個組件都處理路由對象,你可使用withRouter高階組件。下面是如何使用它;

import React, { Component } from 'react';
import { withRouter } from 'next/router'

class RedEnvelope extends Component {
  render () {
    return (
      <div className="envelope-box">
        <p>我是紅包組件</p>
        <h1>{this.props.router.query.title}</h1>
      </div>
    )
  }
}
export default withRouter(RedEnvelope);

我在game頁面調用這個組件裏面調用到this.props.router這個對象了:

 

ok,瞭解這些,就開始寫項目吧,後面的配置和部署,接下來再更新

相關文章
相關標籤/搜索