摘要:今天這篇文章顯然不是討論這兩個(gè)詞語(yǔ)的,我們要嘗試使用最新版,構(gòu)件一個(gè)簡(jiǎn)單的服務(wù)端渲染應(yīng)用。這樣取代了完全由客戶(hù)端渲染前后端分離方式模式。在場(chǎng)景下,我們可以使用自身的完成服務(wù)端初次渲染。這也是它在推出短短時(shí)間以來(lái),便迅速走紅的原因之一。
參加或留意了最近舉行的JSConf CN 2017的同學(xué),想必對(duì) Next.js 不再陌生, Next.js 的作者之一到場(chǎng)進(jìn)行了精彩的演講。其實(shí)在更早些時(shí)候,由 Facebook 舉辦的 React Conf 2017,他就到場(chǎng)并有近40分鐘的分享。但兩次分享帶來(lái)的 demo 都是 hacker news。我觀察 Next.js 時(shí)間較長(zhǎng),看著它從1.x 版本一直到了今天的 3.x,終于決定寫(xiě)一篇入門(mén)級(jí)的新手指導(dǎo)文章。而這篇文章試圖通過(guò)一個(gè)全新的例子,來(lái)讓大家了解 Next.js 到底是如何與 React 配合,達(dá)到服務(wù)端渲染的。
“React universal” 是社區(qū)上形容基于 React 構(gòu)建 web 應(yīng)用,并采用“服務(wù)端渲染”方式的一個(gè)詞語(yǔ)。也許很多人對(duì) “isomorphic” 這個(gè)單詞更加熟悉,其實(shí)這兩個(gè)詞語(yǔ)想要表達(dá)的概念類(lèi)似。今天這篇文章顯然不是討論這兩個(gè)詞語(yǔ)的,我們要嘗試使用最新版 Next.js,構(gòu)件一個(gè)簡(jiǎn)單的服務(wù)端渲染 React 應(yīng)用。最終項(xiàng)目地址可以點(diǎn)擊這里查看。
為何要開(kāi)發(fā) Universal 應(yīng)用?React app 實(shí)現(xiàn)了虛擬 DOM,來(lái)實(shí)現(xiàn)對(duì)真實(shí) DOM 的抽象。這樣的設(shè)計(jì)迅速引領(lǐng)了前端開(kāi)發(fā)浪潮。但是 “Every great thing comes with a price”,虛擬 DOM 同樣帶來(lái)了一些弊端,比如在前后端分離的開(kāi)發(fā)模式下,SEO就成了問(wèn)題;同樣首屏加載時(shí)間變長(zhǎng),各種 loading 消磨人的耐心。就像下面截圖所展現(xiàn)的那樣:
使用 Next.js 實(shí)現(xiàn) UniversalUniversal 應(yīng)用架構(gòu)可以簡(jiǎn)單粗暴先而片面的理解成應(yīng)用將在客戶(hù)端和服務(wù)端共同完成渲染。這樣取代了完全由客戶(hù)端渲染(前后端分離方式)模式。在 React 場(chǎng)景下,我們可以使用 React 自身的 renderToString 完成服務(wù)端初次渲染。但是如果我們每次手動(dòng)來(lái)完成這些過(guò)程,手動(dòng)實(shí)現(xiàn)服務(wù)端繁瑣配置,難免令人頭大心煩。
Next.js 的出現(xiàn),就是為你解決這種惱人的問(wèn)題。我們先來(lái)認(rèn)識(shí)一下它的幾個(gè)原則和思想:
不需要除 Next 之外,多余的配置和安裝(比如 webpack,babel);
使用 Glamor 處理樣式;
自動(dòng)編譯和打包;
熱更新;
方便的靜態(tài)資源管理;
成熟靈活的路由配置,包括路由級(jí)別 prefetching;
Demo:英超聯(lián)賽積分榜其實(shí)關(guān)于更多的 Next.js 設(shè)計(jì)理念我不想再贅述了,讀者都可以在其官網(wǎng)找到豐富的內(nèi)容。下面,我將使用 Football Data API 來(lái)簡(jiǎn)單開(kāi)發(fā)一個(gè)基于 Next.js 的應(yīng)用,這個(gè)應(yīng)用將展現(xiàn)英超聯(lián)賽的實(shí)時(shí)積分榜。同時(shí)包含了簡(jiǎn)單的路由開(kāi)發(fā)和頁(yè)面跳轉(zhuǎn)。
小試牛刀相信所有的開(kāi)發(fā)者都厭惡超長(zhǎng)時(shí)間的安裝和各種依賴(lài)、插件配置。不要擔(dān)心,Next.js 作為一個(gè)獨(dú)立的 npm package 最大限度的替你完成了很多耗時(shí)且無(wú)趣的工作。我們首先需要進(jìn)行安裝:
# Start a new project npm init # Install Next.js npm install next --save
安裝結(jié)束后,我們就可以開(kāi)啟腳本:
"scripts": { "start": "next" },
Next 安裝的同時(shí),也會(huì)安裝 React,所以無(wú)需自己費(fèi)心。接下來(lái)所需要做的很簡(jiǎn)單,就是在根目錄下創(chuàng)建一個(gè) pages 文件夾,并在其下新建一個(gè) index.js 文件:
// ./pages/index.js // Import React import React from "react" // Export an anonymous arrow function // which returns the template export default () => (This is just so easy!
)
好了,現(xiàn)在就可以直接看到結(jié)果:
# Start your app npm start
驗(yàn)證一下它來(lái)自服務(wù)端渲染:
就是這么簡(jiǎn)單,清新。如果我們自己手段實(shí)現(xiàn)這一切的話,除了 NodeJS 的種種繁瑣不說(shuō),webpack 配置,node_modules 依賴(lài),babel插件等等就夠折騰半天的了。
添加 Page Head在 ./pages/index.js 文件內(nèi),我們可以添加頁(yè)面 head 標(biāo)簽、meta 信息、樣式資源等等:
// ./pages/index.js import React from "react" // Import the Head Component import Head from "next/head" export default () => ()League Table This is just so easy!
這個(gè) head 當(dāng)然不是指真實(shí)的 DOM,千萬(wàn)別忘了 React 虛擬 DOM 的概念。其實(shí)這是 Next 提供的 Head 組件,不過(guò)最終一定還是被渲染成為真實(shí)的 head 標(biāo)簽。
發(fā)送 Ajax 請(qǐng)求Next 還提供了 getInitialProps 方法,這個(gè)方法支持異步選項(xiàng),并且是服務(wù)端/客戶(hù)端同構(gòu)的。我們可以使用 async/await 方式,處理異步請(qǐng)求。請(qǐng)看下面的示例:
import React from "react" import Head from "next/head" import axios from "axios"; export default class extends React.Component { // Async operation with getInitialProps static async getInitialProps () { // res is assigned the response once the axios // async get is completed const res = await axios.get("http://api.football-data.org/v1/competitions/426/leagueTable"); // Return properties return {data: res.data} } }
我們使用了 axios 類(lèi)庫(kù)來(lái)發(fā)送 HTTP 請(qǐng)求。網(wǎng)絡(luò)請(qǐng)求是異步的,因此我們需要在未來(lái)某個(gè)合適的時(shí)候(請(qǐng)求結(jié)果返回時(shí))接收數(shù)據(jù)。這里使用先進(jìn)的 async/await,以同步的方式處理,從而避免了回調(diào)嵌套和 promises 鏈。
我們將異步獲得的數(shù)據(jù)返回,它將自動(dòng)掛載在 props 上(注意 getInitialProps 方法名,顧名思義),render 方法里便可以通過(guò) this.props.data 獲取:
import React from "react" import Head from "next/head" import axios from "axios"; export default class extends React.Component { static async getInitialProps () { const res = await axios.get("http://api.football-data.org/v1/competitions/426/leagueTable"); return {data: res.data} } render () { return (......); } }Barclays Premier League
...... {this.props.data.standing.map((standing, i) => { const oddOrNot = i % 2 == 1 ? "pure-table-odd" : ""; return (); })} {standing.position} {standing.points} {standing.goals} {standing.wins} {standing.draws} {standing.losses}
這樣,再訪問(wèn)我們的頁(yè)面,就有了:
路由和頁(yè)面跳轉(zhuǎn)也許你已經(jīng)有所感知:我們已經(jīng)有了最基本的一個(gè)路由。Next 不需要任何額外的路由配置信息,你只需要在 pages 文件夾下新建文件,每一個(gè)文件都將是一個(gè)獨(dú)立的頁(yè)面。
讓我們來(lái)新建一個(gè) team 頁(yè)面吧!新建 ./pages/details.js 文件:
// ./pages/details.js import React from "react" export default () => (Coming soon. . .!
)
我們使用 Next 已經(jīng)準(zhǔn)備好的組件 來(lái)進(jìn)行頁(yè)面跳轉(zhuǎn):
// ./pages/details.js import React from "react" // Import Link from next import Link from "next/link" export default () => ()Coming soon. . .!
Go Home
這個(gè)頁(yè)面不能總是 “Coming soon. . .!” 的信息,我們來(lái)進(jìn)行完善以展示更多內(nèi)容,通過(guò)頁(yè)面 URL 的 query id 變量,我們來(lái)請(qǐng)求并展現(xiàn)當(dāng)前相應(yīng)隊(duì)伍的信息:
import React from "react" import Head from "next/head" import Link from "next/link" import axios from "axios"; export default class extends React.Component { static async getInitialProps ({query}) { // Get id from query const id = query.id; if(!process.browser) { // Still on the server so make a request const res = await axios.get("http://api.football-data.org/v1/competitions/426/leagueTable") return { data: res.data, // Filter and return data based on query standing: res.data.standing.filter(s => s.position == id) } } else { // Not on the server just navigating so use // the cache const bplData = JSON.parse(sessionStorage.getItem("bpl")); // Filter and return data based on query return {standing: bplData.standing.filter(s => s.position == id)} } } componentDidMount () { // Cache data in localStorage if // not already cached if(!sessionStorage.getItem("bpl")) sessionStorage.setItem("bpl", JSON.stringify(this.props.data)) } // . . . render method truncated }
這個(gè)頁(yè)面根據(jù) query 變量,動(dòng)態(tài)展現(xiàn)出球隊(duì)信息。具體來(lái)看,getInitialProps 方法獲取 URL query id,根據(jù) id 篩選出(filter 方法)展示信息。有意思的是,因?yàn)橐恢鼻蜿?duì)的信息比較穩(wěn)定,所以在客戶(hù)端使用了 sessionStorage 進(jìn)行存儲(chǔ)。
完整的 render 方法:
// . . . truncated export default class extends React.Component { // . . . truncated render() { const detailStyle = { ul: { marginTop: "100px" } } return () } }League Table {this.props.standing[0].teamName}
Points: {this.props.standing[0].points}
Home
- Goals: {this.props.standing[0].goals}
- Wins: {this.props.standing[0].wins}
- Losses: {this.props.standing[0].losses}
- Draws: {this.props.standing[0].draws}
- Goals Against: {this.props.standing[0].goalsAgainst}
- Goal Difference: {this.props.standing[0].goalDifference}
- Played: {this.props.standing[0].playedGames}
注意下面截圖中,同一頁(yè)面不同 query 值,分別展示了冠軍?切爾西和曼聯(lián)的信息。
別忘了我們的主頁(yè)(排行榜頁(yè)面)index.js 中,也要使用相應(yīng)的 sessionStorage 邏輯。同時(shí),在 render 方法里加入一條鏈接到詳情頁(yè)的 :
錯(cuò)誤頁(yè)面More...
在 Next 中,我們同樣可以通過(guò) error.js 文件定義錯(cuò)誤頁(yè)面。在 ./pages 下新建 error.js:
// ./pages/_error.js import React from "react" export default class Error extends React.Component { static getInitialProps ({ res, xhr }) { const statusCode = res ? res.statusCode : (xhr ? xhr.status : null) return { statusCode } } render () { return ({ this.props.statusCode ? `An error ${this.props.statusCode} occurred on server` : "An error occurred on client" }
) } }
當(dāng)傳統(tǒng)情況下頁(yè)面404時(shí),得到:
在我們?cè)O(shè)置 _ error.js 之后,便有:
總結(jié)這篇文章實(shí)現(xiàn)了一個(gè)簡(jiǎn)易 demo,只是介紹了最基本的 Next.JS 搭建 React 同構(gòu)應(yīng)用的基本步驟。
想想你是否厭煩了 webpack 惱人的配置?是否對(duì)于 Babel 各種插件云里霧里?
使用 Next.js,簡(jiǎn)單、清新而又設(shè)計(jì)良好。這也是它在推出短短時(shí)間以來(lái),便迅速走紅的原因之一。
除此之外,Next 還有非常多的功能,非常多的先進(jìn)理念可以應(yīng)用。
比如 搭配 prefetch,預(yù)先請(qǐng)求資源;
再如動(dòng)態(tài)加載組件(Next.js 支持 TC39 dynamic import proposal),從而減少首次 bundle size;
雖然它替我們封裝好了 Webpack、Babel 等工具,但是我們又能 customizing,根據(jù)需要自定義。
最后,對(duì)于這些本文章沒(méi)有演示到的功能是否有些手癢?感興趣的讀者可以關(guān)注本文 demo 的Github項(xiàng)目地址,自己手動(dòng)嘗試起來(lái)吧~
本文意譯了Chris Nwamba的:React Universal with Next.js: Server-side React 一文,并對(duì)原文進(jìn)行了升級(jí),兼容了最新的 Next 設(shè)計(jì)。
我的其他關(guān)于 React 文章:
做出Uber移動(dòng)網(wǎng)頁(yè)版還不夠 極致性能打造才見(jiàn)真章
解析Twitter前端架構(gòu) 學(xué)習(xí)復(fù)雜場(chǎng)景數(shù)據(jù)設(shè)計(jì)
React Conf 2017 干貨總結(jié)1: React + ES next = ?
React+Redux打造“NEWS EARLY”單頁(yè)應(yīng)用 一個(gè)項(xiàng)目理解最前沿技術(shù)棧真諦
一個(gè)react+redux工程實(shí)例
Happy Coding!
PS:
作者Github倉(cāng)庫(kù) 和 知乎問(wèn)答鏈接
歡迎各種形式交流。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/51023.html
摘要:今天這篇文章顯然不是討論這兩個(gè)詞語(yǔ)的,我們要嘗試使用最新版,構(gòu)件一個(gè)簡(jiǎn)單的服務(wù)端渲染應(yīng)用。這樣取代了完全由客戶(hù)端渲染前后端分離方式模式。在場(chǎng)景下,我們可以使用自身的完成服務(wù)端初次渲染。這也是它在推出短短時(shí)間以來(lái),便迅速走紅的原因之一。 參加或留意了最近舉行的JSConf CN 2017的同學(xué),想必對(duì) Next.js 不再陌生, Next.js 的作者之一到場(chǎng)進(jìn)行了精彩的演講。其實(shí)在更早...
摘要:更詳細(xì)的內(nèi)容下一章開(kāi)篇深入聊聊前后分離講述關(guān)于我目前在寫(xiě)從零構(gòu)建前后分離項(xiàng)目系列,修正和補(bǔ)充以此為準(zhǔn)不斷更新的項(xiàng)目實(shí)踐地址彩蛋提前預(yù)覽下一章傳送門(mén) 開(kāi)篇 : 縱觀WEB歷史演變 在校學(xué)習(xí)和幾年工作工作中不知不覺(jué)經(jīng)歷了一半的 WEB 歷史演變、對(duì)近幾年的發(fā)展比較了解,結(jié)合經(jīng)驗(yàn)聊聊 WEB 發(fā)展歷史。 演變不易,但也是必然,因?yàn)闉槿耸冀K要進(jìn)步。 WEB 的發(fā)展史 一、開(kāi)山鼻祖 - 石器時(shí)代...
摘要:更詳細(xì)的內(nèi)容下一章開(kāi)篇深入聊聊前后分離講述關(guān)于我目前在寫(xiě)從零構(gòu)建前后分離項(xiàng)目系列,修正和補(bǔ)充以此為準(zhǔn)不斷更新的項(xiàng)目實(shí)踐地址彩蛋提前預(yù)覽下一章傳送門(mén) 開(kāi)篇 : 縱觀WEB歷史演變 在校學(xué)習(xí)和幾年工作工作中不知不覺(jué)經(jīng)歷了一半的 WEB 歷史演變、對(duì)近幾年的發(fā)展比較了解,結(jié)合經(jīng)驗(yàn)聊聊 WEB 發(fā)展歷史。 演變不易,但也是必然,因?yàn)闉槿耸冀K要進(jìn)步。 WEB 的發(fā)展史 一、開(kāi)山鼻祖 - 石器時(shí)代...
摘要:前端日?qǐng)?bào)精選開(kāi)發(fā)常見(jiàn)問(wèn)題集錦前端碼農(nóng)的自我修養(yǎng)虛擬內(nèi)部是如何工作的譯知乎專(zhuān)欄并不慢,只是你使用姿勢(shì)不對(duì)一份優(yōu)化指南掘金老司機(jī)帶你秒懂內(nèi)存管理第一部中文免費(fèi)公開(kāi)課前端面試的大關(guān)鍵點(diǎn),你到了嗎知乎專(zhuān)欄高效開(kāi)發(fā)與設(shè)計(jì)姐的圖片二三 2017-07-19 前端日?qǐng)?bào) 精選 VueJS 開(kāi)發(fā)常見(jiàn)問(wèn)題集錦 - 前端碼農(nóng)的自我修養(yǎng) - SegmentFault虛擬 DOM 內(nèi)部是如何工作的?[譯]Hig...
摘要:寫(xiě)在最前原文首發(fā)于作者的知乎專(zhuān)欄中間件思想遇見(jiàn)的靈感附,感興趣的同學(xué)可以知乎關(guān)注,進(jìn)行交流。其中,最重要的一個(gè)便是對(duì)多線程的支持。在中提出了工作線程的概念,并且規(guī)范出的三大主要特征能夠長(zhǎng)時(shí)間運(yùn)行響應(yīng)理想的啟動(dòng)性能以及理想的內(nèi)存消耗。 寫(xiě)在最前 原文首發(fā)于作者的知乎專(zhuān)欄:React Redux 中間件思想遇見(jiàn) Web Worker 的靈感(附demo),感興趣的同學(xué)可以知乎關(guān)注,進(jìn)行交流...
閱讀 5076·2023-04-25 19:30
閱讀 2173·2023-04-25 15:09
閱讀 2618·2021-11-16 11:45
閱讀 2171·2021-11-15 18:07
閱讀 1458·2021-11-11 17:22
閱讀 2115·2021-11-04 16:06
閱讀 3576·2021-10-20 13:47
閱讀 3036·2021-09-22 16:03