歡迎關注個人公衆號睿Talk
,獲取我最新的文章:javascript
當使用 React 開發系統的時候,經常須要配置不少繁瑣的參數,如 Webpack 配置、Router 配置和服務器配置等。若是須要作 SEO,要考慮的事情就更多了,怎麼讓服務端渲染和客戶端渲染保持一致是一件很麻煩的事情,須要引入不少第三方庫。針對這些問題,Next.js提供了一個很好的解決方案,使開發人員能夠將精力放在業務上,從繁瑣的配置中解放出來。下面咱們一塊兒來看看它的一些特性。前端
Next.js 具備如下幾點特性:java
執行如下命令,開始 Next.js 之旅:react
mkdir hello-next cd hello-next npm init -y npm install --save react react-dom next mkdir pages
在package.json
中輸入如下內容:express
{ "scripts": { "dev": "next", "build": "next build", "start": "next start" } }
在 pages 文件夾下,新建一個文件 index.js
:npm
const Index = () => ( <div> <p>Hello Next.js</p> </div> ) export default Index
在控制檯輸入npm run dev
,這時候在瀏覽器輸入http://localhost:3000
,就能看到效果了。json
Next.js 沒有路由配置文件,路由的規則跟 PHP 有點像。只要在 pages 文件夾下建立的文件,都會默認生成以文件名命名的路由。咱們新增一個文件看效果pages/about.js
segmentfault
export default function About() { return ( <div> <p>This is the about page</p> </div> ) }
在瀏覽器輸入http://localhost:3000/about
,就能看到相應頁面了。api
若是須要進行頁面導航,就要藉助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
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
裏面取。
對於頁面樣式,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
文件夾,全部靜態頁面都在裏面。
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 和組件懶加載等,大大提高了開發效率。
更詳細的使用介紹請看官方文檔。