Next.js入門教程 原

在瞭解Next.js之前,需要掌握React的基本使用方法。

參考代碼:https://github.com/chkui/nextjs-getting-started

搭建

安裝

# 創建項目目錄
mkdir you_project
# 進入項目目錄
cd you_project
# 初始化package.json
npm init -y
# 安裝依賴包
npm install --save react react-dom next
# 創建一個pages文件夾
mkdir pages

依次執行以上命令之後,Next.js運行所需的最基本的目錄和依賴就創建好了。

運行

package.json裏的“scripts"字段修改爲:

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

運行以下命令啓動Next.js

npm run dev

在瀏覽器打開http://localhost:3000/ 看到輸出"404 - This page could not be found",表示Next.js安裝成功。

添加頁面

./pagesNext.js默認的網頁路徑,其中的index.js就代表整個網站的主頁。創建一個*./pages/index.js*組件:

const Index = () => (
  <div>
    <p>Hello World!</p>
  </div>
)
export default Index

添加*./pages/index.js*後網站會自動刷新,呈現"Hello World!"。

頁面與導航欄

頁面

添加http://localhost:3000/about 路徑下的頁面。

創建*./pages/about.js*文件,添加以下內容:

export default () => (
  <div>
    <p>About page</p>
  </div>
)

然後在瀏覽器輸入http://localhost:3000/about 即可看到新增的About。

導航欄

對*./pages/index.js*稍加修改引入導航欄功能:

import Link from 'next/link'

const Index = () => (
  <div>
    <Link href="/about">
      <a style={{fontSize: 20}}>About Page</a>
    </Link>
    <p>Hello Next.js</p>
  </div>
)

export default Index

注意:使用了Next.js作爲服務端渲染工具,切記僅使用next/link中的Link組件。

除了<a>標籤,<button>或自定義的組件都可以被Link包裝,只要傳遞Click事件即可,將上面的代碼稍作修改實驗這個效果:

import Link from 'next/link'

const Index = () => (
    <div>
        <Link href="/about">
            <button>Click Me</button>
        </Link>
        <Link href="/about">
            <A/>
        </Link>
        <p>Hello Next.js</p>
    </div>
)

export default Index

const A = props => (<div onClick={e => {
    props.onClick(e)
}}>Click Me</div>)

關於Next.js路由管理相關的細節內容,可以到這裏查看

頁面、資源與組件

./pages是一個保留路徑,在*/pages*路徑下任何js文件中導出的默認React組件都被視作一個頁面。

除了*./pages*,Next.js還有一個保留路徑是*./static*,它用來存放圖片等靜態資源。

Next.js會對*./pages中的React組件進行“包裝",所以./pages*內外的React組件在呈現結果上有一些差異,看下面的例子。

創建網站結構

在工程根目錄創建*/components*文件夾,然後添加以下組件:

import Link from 'next/link'

const linkStyle = {
    marginRight: 15
}

const Header = () => (
    <div>
        <Link href="/">
            <a style={linkStyle}>Home</a>
        </Link>
        <Link href="/about">
            <a style={linkStyle}>About</a>
        </Link>
    </div>
)

export default Header

然後將Header整合到about.jsindex.js中:

import Header from '../components/Header'
export default () => (
    <div>
        <Header />
        <p>Hello Next.js</p>
    </div>
)

再次進行頁面操作,就會出現表頭靜止頁面變換的效果。

網站佈局

通常情況下,開發一個網站先制定一個通用的佈局(尤其是響應式佈局的網站),然後再向佈局中的添加各個部分的內容。使用Next.js可以通過組件的方式來設計一個佈局,看下面的例子。 在*/components*中增加LayoutFooter組件:

// componments/layout.js
import Header from './header'
import Footer from './footer'

const layoutStyle = {
    margin: 20,
    padding: 20,
    border: '1px solid #DDD'
}

const Layout = (props) => (
    <div style={layoutStyle}>
        <Header />
        {props.children}
        <Footer />
    </div>
)

export default Layout
// components/footer.js
const Footer = () => (
    <div>
        <p style={{color:'blue'}}>Footer</p>
    </div>
)

export default Footer

然後將*/pages/index.js*修改爲:

import Layout from '../components/layout'

export default () => (
    <Layout>
        <p>Hello Next.js</p>
    </Layout>
)

這樣,頁面的內容和佈局就完全隔離開了。

頁面跳轉

傳遞參數

在實際應用中,經常需要在頁面間傳遞參數,可以使用高階組件withRouter來實現。 下面的代碼對*/pages/index.js進行了一些修改,使其在跳轉時攜帶query*參數:

const SubLink = props => (
    <li>
        <Link href={`/post?title=${props.title}`}>
            <a>{props.title}</a>
        </Link>
    </li>
)

export default () => (
    <Layout>
        <h2>Information</h2>
        <SubLink title="First Post"/>
        <SubLink title="Second Post"/>
        <SubLink title="Third Post"/>
    </Layout>
)

點擊First Post之後瀏覽器的URL會出現這樣的路徑:“http://localhost:3000/post?title=First%20Post” 。接下來利用withRouter來獲取這個參數。創建*./pages/post.js*的文件:

import {withRouter} from 'next/router'
import Layout from '../components/layout'

const Page = withRouter((props) => (
    <Layout>
        <h3>Post Page</h3>
        <p>Info:{props.router.query.title}</p>
    </Layout>
))
export default Page

現在點擊First Post鏈接之後,跳轉的頁面會顯示First Post

路徑隱藏

Next.js提供了一個讓URL更加清晰乾淨的特性功能——URL隱藏(官網直譯的話應該叫“URL遮擋”),他的作用是可以隱藏原來比較複雜的URL,讓網站路徑更加清晰,有利於SEO等。實現這個特性非常簡單,在使用Link組件時傳遞一個as參數。下面將繼續修改*./pages/index.js*中的內容以實現這個特性:

const SubLink = props => (
    <li>
        <Link as={`p/${props.as}`} href={`/post?title=${props.title}`}>
            <a>{props.title}</a>
        </Link>
    </li>
)

export default () => (
    <Layout>
        <h2>Information</h2>
        <SubLink as="first-post" title="First Post"/>
        <SubLink as="first-post" title="Second Post"/>
        <SubLink as="first-post" title="Third Post"/>
    </Layout>
)

注意觀察SubLink組件中的修改,爲Link增加了一個as參數,這個參數傳遞的內容將會在瀏覽器的地址欄顯示。例如點擊FIrst Post後,瀏覽器的地址欄會顯示http://localhost:3000/p/first-post ,但是我們通過withRouter組件獲取的URL還是href傳遞的路徑。

服務端渲染

只要運行了Next.js,他時時刻刻都在執行服務端渲染,可以通過刷新頁面看到效果。如果沒有太多需求,不進行任何調整Next.js能爲我們完成靜態頁面的服務端渲染,但是通常情況下,還需要處理異步請求等等情況。

二次服務端渲染

前面介紹了在Link組件上使用as參數可以設置瀏覽器路徑欄上顯示的內容。但是這個時候僅僅支持客戶端跳轉,如果進行頁面刷新會出現404頁面。導致這個問題出現的原因是在服務端並不知道*/p/first-post對應/pages*文件夾中的哪個文件。爲了解決這個問題,需要在服務端進行二次渲染。

首先需要添加Express服務:

npm install --save express

安裝完成之後在根目錄添加一個server.js文件,其內容如下:

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

// 不等於'production'則表示運行的是開發環境
const dev = process.env.NODE_ENV !== 'production'
// 創建一個服務端運行的Next app
const app = next({dev})
// 請求處理器
const handle = app.getRequestHandler()

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

        server.get('/p/:id', (req, res) => {
            //將/p/:id的路徑切換成/post?title=req.params.id的路徑
            app.render(req, res, '/post', {title: req.params.id})
        })

        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)
    })

然後修改package.json的“scripts"字段,將啓動方式方式指向server.js

"scripts": {
    "dev": "node server.js",
    "build": "next build",
    "start": "NODE_ENV=production node server.js"
  }

完成這2步網站服務端也可以正常跳轉,實現功能的位置是這段代碼:

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

他將原來的請求“/p/:id”轉換爲請求"/post?title=id"。

更多的服務端渲染的配置說明請看這裏

數據異步請求

對於一個前後端分離的系統來說,異步數據請求是幾乎每個頁面都需要的。Next.js通過getInitialProps來實現。 下面的示例數據來自https://www.tvmaze.com/api 。創建*./pages/tvshows.js*的文件:

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

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

TvShow.getInitialProps = async function() {
    //contxt是銜接Next.js包裝組件和自定義主鍵的上下文,包含的參數有asPath、pathname、query

    // 發送異步請求
    const res = await fetch('https://api.tvmaze.com/search/shows?q=batman')

    // 從response中異步讀取數據流
    const data = await res.json()

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

    // 返回已獲取的數據
    return {
        shows: data
    }
}

export default TvShow

TvShow組件的作用是異步請求數據並組裝成列表展示。

然後再創建一個查看詳情的頁面——./pages/tv.js,實現過程和上面一樣:

import Layout from '../components/layout'
import fetch from 'isomorphic-unfetch'

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

Tv.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 Tv

按照這個套路可以解決絕大部分數據異步請求的問題。不過如果數據組裝過慢,會出頁面現卡頓的問題,可以通過服務端緩存或異步頁面加載實現,後續的篇幅會介紹。

樣式

源生添加樣式

一個頁面永遠離不開樣式,在Next.js中推薦一種簡介高效的方法——<style jsx>

爲的主頁添加一些樣式:

(
    <Layout>
        <h2>Information</h2>
        <SubLink as="first-post" title="First Post"/>
        <SubLink as="first-post" title="Second Post"/>
        <SubLink as="first-post" title="Third Post"/>
        <style jsx>{`
            h2{
                font-family: "Arial";
            }
        `}</style>
        <style jsx global>{`
            .list{
                list-style: none;
                margin: 5px 0;
            }
        `}</style>
    </Layout>
)

<style jsx>的作用就是爲當前組件聲明樣式,需要注意的是在這個標籤內聲明的樣式只能覆蓋當前組件,子組件是不會出現層疊效果的。而<style jsx global>標籤的效果則是和標準的css層疊效果一致,在這個標籤中聲明的樣式會影響到子組件。

Loader添加載樣式

Next.js可以加載各種樣式文件,下面以Sass/Scss爲例。

首先添加相關依賴:

npm install --save @zeit/next-sass node-sass

在項目根目錄添加next.config.js文件,用於指示Next加載對用的功能:

const withSass = require('@zeit/next-sass')
module.exports = withSass()

現在就可以加載*.scss文件了,添加一個/pages/post.scss*文件:

$font-size: 50px;
.header{
  font-size: $font-size;
  color:red;
}

修改*/pages/post.js*加載樣式:

import {withRouter} from 'next/router'
import Layout from '../components/layout'
//加載樣式
import './post.scss'

const Page = withRouter((props) => (
    <Layout>
        <h3 className="header">Post Page</h3>
        <p>Info:{props.router.query.title}</p>
    </Layout>
))

export default Page

由於是使用的webpackLoader,可以根據需要在next.config.js文件中進行一些相關的設置:

module.exports = withSass({
  cssModules: true,
  cssLoaderOptions: {
    importLoaders: 1,
    localIdentName: "[local]___[hash:base64:5]",
  }
})

然後在組件中直接以對象的方式使用:

import style from './post.scss'
const Page = withRouter((props) => {
    console.log(style)
    return (
        <Layout>
            <h3 className={style.header}>Post Page</h3>
            <p>Info:{props.router.query.title}</p>
        </Layout>
    )
})

更多關於cssLoaderOptions的參數說明可以查看webpack裏css-loader的options說明。除了scss,Next.js還支持css、less、post css的Loader

發佈

在瞭解以上內容之後,已經可以開發一個網站了,接下來介紹如何發佈生產包。

package.json中的“scripts"字段可以設置打包和生產運行方式:

  "scripts": {
    "dev": "node server.js",
    "build": "next build",
    "start": "NODE_ENV=production node server.js"
  }

首先進行打包:

npm run build

打包完畢之後可以啓動生產環境:

npm start

現在用瀏覽器打開http://localhost:3000/ 地址可以發現運行的是生產環境(可以使用React工具查看,也可以打開開發人員模式)。 由於之前了在server.js中引入了Express,所以現在啓動的是一個Express服務器。打包之後的文件都在*./.next* 路徑下,可以僅僅拷貝依賴包(node_module)package.jsonserver.js以及**./.next**來運行生產環境。

除了使用Express這一類第三方nodejs服務器,Next.js還提供了許多其他方式來部署和方法

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