React腳手架之NextJs

仲尼叔叔發表於2020-11-12

1.Next.js簡介

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

它的優點:

  • 完善的React專案架構,搭建輕鬆。比如:Webpack配置,伺服器啟動,路由配置,快取能力,這些在它內部已經完善的為我們搭建完成了。
  • 自帶資料同步策略,解決服務端渲染最大難點。把服務端渲染好的資料,拿到客戶端重用,這個在沒有框架的時候,是非常複雜和困難的。有了Next.js,它為我們提供了非常好的解決方法,讓我們輕鬆的就可以實現這些步驟。
  • 豐富的外掛幫開發人員增加各種功能。每個專案的需求都是不一樣的,包羅永珍。無所不有,它為我們提供了外掛機制,讓我們可以在使用的時候按需使用。你也可以自己寫一個外掛,讓別人來使用。
  • 靈活的配置,讓開發變的更簡單。它提供很多靈活的配置項,可以根據專案要求的不同快速靈活的進行配置。

2.快速建立Next.js

#全域性安裝
npm install -g create-next-app
#npx建立專案
npx install create-next-app demo13
cd demo13
#本地執行
yarn dev

3.專案結構介紹

  • components資料夾:這裡是專門放置自己寫的元件的,這裡的元件不包括頁面,指公用的或者有專門用途的元件。
  • node_modules資料夾:Next專案的所有依賴包都在這裡,一般我們不會修改和編輯這裡的內容。
  • pages資料夾:這裡是放置頁面的,這裡邊的內容會自動生成路由,並在伺服器端渲染,渲染好後進行資料同步。
  • static資料夾: 這個是靜態資料夾,比如專案需要的圖片、圖示和靜態資源都可以放到這裡。
  • .gitignore檔案: 這個主要是控制git提交和上傳檔案的,簡稱就是忽略提交。
  • package.json檔案:定義了專案所需要的檔案和專案的配置資訊(名稱、版本和許可證),最主要的是使用npm install 就可以下載專案所需要的所有包。

4.Page和Component使用

在pages檔案下,建立Test.js。

function Test(){
    return (<button> Test page</button>)
}

export default  Test;

寫完以上的程式碼,Next框架就自動作好了路由,這個也算是Next的一個重要優點,給我們節省了大量的時間。

現在要作一個更深的頁面,比如把有關部落格的介面都放在這樣的路徑下http://localhost:3000/blog/nextBlog,其實只要在pages資料夾下再建立一個新的資料夾blog,然後進入blog資料夾,新建一個nextBlog.js檔案,就可以實現了。

編寫元件也特別方便,比如要建立一個comm元件,直接在components目錄下建立一個檔案comm,然後寫入下面程式碼:

export default ({children})=><button>{children}</button>

元件寫完後需要先引入,比如我們在Index頁面裡進行引入:

import comm from '../components/comm'

使用就非常簡單了,直接寫入標籤就可以。

<comm>按鈕</comm>

5.路由基礎及跳轉

5.1 標籤是導航

首先先刪除index.js中的程式碼,引入以下:

import Link from 'next/link'

然後新建兩個頁面testA.jstestB.js

import Link from 'next/link'

export default ()=>(
    <>
        <div>TestA page .  </div>
        <Link href="/"><a>返回首頁</a></Link>
    </>
)
import Link from 'next/link'

export default ()=>(
    <>
        <div>TestB page .  </div>
        <Link href="/"><a>返回首頁</a></Link>
    </>
)

然後編寫index檔案

import React from 'react'
import Link from 'next/link'


const Home = () => (
  <>
    <div>我是首頁</div>
    <div><Link href="/testA"><a>去testA頁面</a></Link></div>
    <div><Link href="/testB"><a>去testB頁面</a></Link></div>

  </>
)

export default Home

注意:不支援兄弟並列標籤。

5.2 Router模組跳轉

在index.js中,新增以下:

 <div>
    <button onClick={()=>{Router.push('/testA')}}>去testA頁面</button>
  </div>

6.路由跳轉傳值和接收引數

6.1 query傳遞引數及接收
<div><Link href="/testA?name=karma"><a>Link跳轉去testA頁面</a></Link></div>
//或
<div><Link href={{pathname: '/testA', query: {name: 'karma'}}}><a>Link跳轉去testA頁面</a></Link></div>

然後在testA.js檔案中接收該引數:

import React from 'react'
import Link from 'next/link'
import {withRouter} from "next/router";

const testA = ({router}) => (
    <>
        <div>testA page . {router.query.name}</div>
        <Link href="/"><a>返回首頁</a></Link>
    </>
)
//withRouter是Next.js框架的高階元件,用來處理路由用的
export default withRouter(testA)
6.2 Router程式設計式傳遞引數
<button onClick={()=>{Router.push('/testA?name=karma')}}>去testA頁面</button>
#或者
<button onClick={()=>{Router.push({
      pathname:'/testA',
      query:{
        name:'karma'
      }
    })}>去testA頁面</button>

7.路由-六個鉤子事件

routerChangeStart路由發生變化
   Router.events.on('routeChangeStart',(...args)=>{
    console.log('1.routeChangeStart->路由開始變化,引數為:',...args)
  })
routerChangeComplete路由結束變化時
  Router.events.on('routeChangeComplete',(...args)=>{
    console.log('routeChangeComplete->路由結束變化,引數為:',...args)
  })
beforeHistoryChange瀏覽器history觸發前
  Router.events.on('beforeHistoryChange',(...args)=>{
    console.log('3,beforeHistoryChange->在改變瀏覽器 history之前觸發,引數為:',...args)
  })
routeChangeError路由跳轉發生錯誤時
 Router.events.on('routeChangeError',(...args)=>{
    console.log('4,routeChangeError->跳轉發生錯誤,引數為:',...args)
  })

需要注意的是404找不到路由頁面不算錯誤。

轉變成hash路由模式

還有兩種事件,都是針對hash的,所以現在要轉變成hash模式。hash模式下的兩個事件hashChangeStart和`hashChangeComplete。

  Router.events.on('hashChangeStart',(...args)=>{
    console.log('5,hashChangeStart->hash跳轉開始時執行,引數為:',...args)
  })

  Router.events.on('hashChangeComplete',(...args)=>{
    console.log('6,hashChangeComplete->hash跳轉完成時,引數為:',...args)
  })

在下面的jsx語法部分,再增加一個連結,使用hash來進行跳轉,程式碼如下:

<div>
    <Link href="#test"><a>test</a></Link>
</div>

8.在getInitialProps中使用Axios獲取遠端資料

Next.js框架中提供了getInitialProps靜態方法用來獲取遠端資料,這個是框架的約定,所以你也只能在這個方法裡獲取遠端資料。

安裝Axios:

yarn add axios

在testA.js中引入Axios

import Axios from 'axios'

然後使用getInitialProps獲取遠端資料並渲染頁面

import React from 'react'
import Link from 'next/link'
import {withRouter} from "next/router";
import Axios from "axios";

const testA = ({router, list}) => (
    <>
        <div>testA page . {router.query.name} {list}</div>
        <Link href="/"><a>返回首頁</a></Link>
    </>
)
testA.getInitialProps = async () => {
    const promise = new Promise((resolve => {
        Axios('https://www.easy-mock.com/mock/5f96cc6134c55d14fda96ea1/example/query').then((res) => {
            console.log('遠端資料結果:', res)
            resolve(res.data.data)
        })

    }))
    return await promise
}
//withRouter是Next.js框架的高階元件,用來處理路由用的
export default withRouter(testA)

9.使用style JSX編寫頁面的css樣式

在pages檔案下,建立testC.js.

function testC(){
    return (
        <>
            <div>testC</div>
        <style jsx>
          {`
              div{color:blue;}
          `}
  			</style>
        </>
    )
}
export default testC

通過自定義類名載入css

function testC(){
    return (
        <>
            <div>testC</div>
       			<div className="karma">karma</div>
        		<style jsx>
              {`
                  div{color:blue;},
									.karma{color:red}
              `}
  					</style>
        </>
    )
}
export default testC

動態顯示樣式

import {useState} from "react";

function testC() {
    const [color, setColor] = useState('blue')
    const changeColor = () => {
        setColor(color === 'blue' ? 'red' : 'blue')
    }

    return (
        <>
            <div>testC</div>
            <div>
                <button onClick={changeColor}>改變顏色</button>
            </div>
            <div className="karma">karma</div>
            {/*Style JSX語法*/}
            <style jsx>
                {`
                    div{color:${color};},
                `}
            </style>
        </>
    )
}

export default testC

10.Lazy Loading 實現模組懶載入

懶載入模組

安裝moment

yarn add moment

然後在pages資料夾下,新建立一個time.js檔案,並使用剛才的moment庫來格式化時間,程式碼如下:

import React, {useState} from 'react'
import moment from 'moment'

function Time(){

    const [nowTime,setTime] = useState(Date.now())

    const changeTime=()=>{
        setTime(moment(Date.now()).format())
    }
    return (
        <>
            <div>顯示時間為:{nowTime}</div>
            <div><button onClick={changeTime}>改變時間格式</button></div>
        </>
    )
}
export default Time

這個案例,存在著一個潛在的風險,就是如何有半數以上頁面使用了這個momnet的庫,那它就會以公共庫的形式進行打包釋出,就算專案第一個頁面不使用moment也會進行載入,這就是資源浪費,對於我這樣有程式碼潔癖的良好程式設計師是絕對不允許的。下面我們就通過Lazy Loading來進行改造程式碼。

import React, {useState} from 'react'
//刪除import moment
function Time(){

    const [nowTime,setTime] = useState(Date.now())

    const changeTime= async ()=>{ //把方法變成非同步模式
        const moment = await import('moment') //等待moment載入完成
        setTime(moment.default(Date.now()).format()) //注意使用defalut
    }
    return (
        <>
            <div>顯示時間為:{nowTime}</div>
            <div><button onClick={changeTime}>改變時間格式</button></div>
        </>
    )
}
export default Time
懶載入自定義元件

先寫一個最簡單的元件,在components資料夾下建立一個one.js檔案,然後編寫如下程式碼:

export default ()=><div>Lazy Loading Component</div>

有了自定義元件後,先要在懶載入這個元件的檔案中引入dynamic,我們這個就在上邊新建的time.js檔案中編寫了。

import dynamic from 'next/dynamic'

引入後就可以懶載入自定義模組了,程式碼如下:

import React, {useState} from 'react'
import dynamic from 'next/dynamic'

const One = dynamic(import('../components/one'))

function Time(){

    const [nowTime,setTime] = useState(Date.now())

    const changeTime= async ()=>{
        const moment = await import('moment')

        setTime(moment.default(Date.now()).format())
    }
    return (
        <>
            <div>顯示時間為:{nowTime}</div>
            <One/>
            <div><button onClick={changeTime}>改變時間格式</button></div>
        </>
    )
}
export default Time

11.自定義Head更加友好的SEO操作

一般有兩種方式。

方法1:在各頁面加上標籤

先在/pages資料夾下面建立一個header.js檔案,然後寫一個最簡單的Hooks頁面,程式碼如下:

function Header(){ 
    return (<div>JSPang.com</div>)
}
export default Header

寫完後到瀏覽器中預覽一下,可以發現title部分並沒有任何內容,顯示的是localhost:3000/header,接下來就自定義下<Head>。自定義需要先進行引入next/head

import Head from 'next/head'

引入後你就可以寫一些列的頭部標籤了,全部程式碼如下:

import Head from 'next/head'
function Header(){ 
    return (
        <>
            <Head>
                <title>Header</title>
                <meta charSet='utf-8' />
            </Head>
            <div>Karma520.com</div>

        </> 
    )
}
export default Header

這時候再開啟瀏覽器預覽,你發現已經有了title

方法2:全域性定義

比如在components資料夾下面新建立一個myheader.js,然後寫入下面的程式碼:

import Head from 'next/head'

const MyHeader = ()=>{
    return (
        <>
            <Head>
                <title> Karma520.com </title>   
            </Head>
        </>
    )
}

export default MyHeader

把剛才編寫的header.js頁面改寫一下,引入自定義的myheader,在頁面裡進行使用。

import Myheader from '../components/myheader'
function Header(){ 
    return (
        <>
            <Myheader />
            <div>Karma520.com</div>

        </> 
    )
}
export default Header

12.Next.js腳手架中使用Ant Design UI

Next.js預設是不支援CSS檔案的,它用的是style jsx,也就是說它是不支援直接用import進行引入css的。

在static下新建一個test.css檔案,寫入一些CSS Style

body{
    color:green;
}

然後用importheader.js裡引入。

import '../static/test.css'

寫完到瀏覽器中進行預覽,沒有任何輸出結果而且報錯了。這說明Next.js預設是不支援CSS樣式引入的,要進行一些必要的設定,才可以完成。

開始進行配置,讓Next.js支援CSS檔案

先用yarn命令來安裝@zeit/next-css包,它的主要功能就是讓Next.js可以載入CSS檔案。

yarn add @zeit/next-css

包安裝好以後就可以進行配置檔案的編寫了,建立一個next.config.js.這個就是Next.js的總配置檔案(如果感興趣可以自學一下)。

const withCss = require('@zeit/next-css')

if(typeof require !== 'undefined'){
    require.extensions['.css']=file=>{}
}

module.exports = withCss({})

重啟服務可以讓配置生效,這時候你到瀏覽器中可以發現CSS檔案已經生效了,字變成了綠色。

按需載入Ant Design

載入Ant Design在我們打包的時候會把Ant Design的所有包都打包進來,這樣就會產生效能問題,讓專案載入變的非常慢。

** 先來安裝Ant Design庫 **

直接使用yarn來安裝就可以。

yarn add antd

** 安裝和配置babel-plugin-import 外掛 **

yarn add babel-plugin-import

然後在專案根目錄建立.babelrc檔案,然後寫入如下配置檔案。

{
    "presets":["next/babel"],  //Next.js的總配置檔案,相當於繼承了它本身的所有配置
    "plugins":[     //增加新的外掛,這個外掛就是讓antd可以按需引入,包括CSS
        [
            "import",
            {
                "libraryName":"antd",
                "style":"css"
            }
        ]
    ]
}

配置好了以後,webpack就不會預設把整個Ant Design的包都進行打包到生產環境了,而是我們使用那個元件就打包那個元件,同樣CSS也是按需打包的。

然後在header.js裡,引入`元件,並進行使用。

import Myheader from '../components/myheader'
import {Button} from 'antd'


import '../static/test.css'
function Header(){ 
    return (
        <>
            <Myheader />
            <div>Karma520.com</div>
            <div><Button>按鈕</Button></div>

        </> 
    )
}
export default Header

執行,看效果。

13.Next打包CSS問題

打包 :yarn build

然後在終端裡執行一下yarn build,如果這時候報錯,其實是我們在加入Ant Design的樣式時產生的,這個已經在Ant Design的Github上被提出了,但目前還沒有被修改,你可以改完全域性引入CSS解決問題。

在page目錄下,修改_app.js檔案。

import App from 'next/app'

import 'antd/dist/antd.css'

export default App

並且註釋.babelrc中該行配置 “style”:“css”,然後執行命令。

個人部落格:Karma‘s Blog
原始碼地址:傳送門

相關文章