我們為開源的Next.js感到非常自豪,它是一個服務器渲染的通用JavaScript webapps的小型框架,建立在React,Webpack和Babel的基礎之上。
要開始使用它,在一個新的目錄中運行: package.json
$ npm install next --save
$ mkdir pages
填充pages/index.js:
import React from 'react'
export default () => <div>Hello world!</div>
package.json像這樣添加一個腳本:
{
"scripts": {
"dev": "next"
}
}
并運行:
$ npm run dev
這篇博客文章將涉及項目的理念和設計決策。
要學習如何使用Next.js,請參閱自述文件,您可以在幾分鐘內了解該工具的全部功能。
首先我們將深入到項目的背景,然后描述6個基本原則:
- 零設置。使用文件系統作為API
- 只有JavaScript。一切都是一個功能
- 自動服務器呈現和代碼分割
- 數據獲取取決于開發者
- 預期是表現的關鍵
- 簡單部署
背景
多年來,我們一直在追求通用JavaScript應用程序的愿景。
Node.js引導了客戶端和服務器之間的代碼共享,拓寬了世界各地許多開發者的貢獻面。
許多嘗試都是為了在Node上開發應用程序和網站而設計的。許多模板語言和框架出現了......但是前端和后端之間的技術鴻溝依然存在。
例如,如果你選擇了Express和Jade,一些HTML將被服務器渲染,然后一個不同的代碼庫 (由jQuery或類似的庫支持)將接管。
這種情況實際上并不比PHP的好。在許多方面,PHP實際上更適合于“服務器呈現HTML”作業。在異步/等待之前,很難在JS中查詢數據服務。將錯誤限制在請求/響應的范圍之內也是非常困難的。
然而,從那以后,顯著的概念上的變化使我們能夠縮小這個差距。其中最重要的是引入了純渲染函數,該函數根據當時的可用數據返回UI的表示形式。
這個模型(被React普及)是非常重要的,但是這與大多數模板系統的工作原理沒有什么不同。另一個關鍵概念是組件生命周期。
生命周期鉤子允許我們處理源自服務器的一些渲染的延續。例如,您可以從數據的靜態表示開始,訂閱來自服務器的實時更新,并隨時間變化。或者也許它保持不變。
Next.js是我們如何推動這一愿景的。
零設置。使用文件系統作為API
工具對文件系統中的項目結構做了一些假設。
例如,我們通常通過創建一個新的目錄,放置一個package.json內部,然后安裝模塊來啟動一個Node.js項目./node_modules。
Next.js通過引入pages 頂級組件所在的子目錄來擴展該結構。
例如,您可以使用以下命令來填充pages/index.js路線的哪些地圖/:
import React from 'react'
export default () => <marquee>Hello world</marquee>
然后pages/about.js映射到: /about
import React from 'react'
export default () => <h1>About us</h1>
我們相信這是一個很好的默認開始,并允許一個項目的快速探索。當需要更高級的路由時,我們將允許開發人員攔截請求并采取控制。
所有需要開始工作的項目是運行:
$ next
沒有配置,除非需要。自動熱碼重新加載,錯誤報告,源地圖,舊版瀏覽器的轉換。
只有JavaScript。一切都是一個功能
Next.js中的每個路由只是一個ES6模塊,用于導出一個擴展的函數或類React.Component。
這種方法與類似模型相比的優點是整個系統仍然是高度可組合和可測試的。例如,一個組件可以被直接渲染,或者被另一個頂層組件導入和渲染。
組件也可以引入對頁面的更改: <head>
import React from 'react'
import Head from 'next/head'
export default () => (
<div>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<h1>Hi. I'm mobile-ready!</h1>
</div>
)
此外,不需要包裝或轉換,使這個系統完全可測試。您的測試套件可以簡單地導入和淺顯渲染您的路線。
我們也決定采用CSS-in-JS。我們使用優秀的glamor庫,給我們完全不受限制的CSS的權力,而不需要CSS解析和編譯:
import React from 'react'
import css from 'next/css'
export default () => <p className={style}>Hi there!</p>
const style = css({
color: 'red',
':hover': {
color: 'blue'
},
'@media (max-width: 500px)': {
color: 'rebeccapurple'
}
})
我們認為這個模型提供了卓越的性能,可組合性和與服務器渲染流水線的集成。
自動服務器呈現和代碼分割
迄今為止,兩項任務同時非常困難和非常可取:
服務器渲染
- 將應用程序的構建分割成更小的包
- 使用Next.js,每個內部組件pages/都會自動獲取服務器并且內聯腳本。
當通過或路由器動態加載組件時,我們獲取一個基于JSON的頁面表示,同樣包含它的腳本。 <Link prefetch />
這意味著某個頁面可能有一個廣泛的導入列表:
import React from 'react'
import d3 from 'd3'
import jQuery from 'jquery'
...不影響其他頁面的性能。
這個細節對于那些在技術和業務需求截然不同的組件上進行協作的大型團隊來說特別有用。團隊或個人的表現處罰不會影響組織的其余部分。
數據獲取取決于開發者
靜態JSX的服務器渲染是一個重要的成就,但真實世界的應用程序處理來自不同的API調用和網絡請求的動態數據。
Next.js對React組件合同做了非常重要的擴展:getInitialProps。
提取一些數據的頁面如下所示:
import React from 'react'
import 'isomorphic-fetch'
export default class extends React.Component {
static async getInitialProps () {
const res = await fetch('https://api.company.com/user/123')
const data = await res.json()
return { username: data.profile.username }
}
}
我們對于什么樣的功能(像異步/等待)的立場可以概括為:我們的目標是V8的功能。由于我們的目標是在服務器和客戶端之間進行代碼共享,所以在執行Node上的代碼以及在Chrome或Brave上開發時,這給我們帶來了很好的性能。
正如你所看到的那樣,契約是非常簡單而且不可選擇的:必須返回一個解析成JavaScript的對象,然后填充組件。 getInitialPropsPromise props
這使得Next.js在REST API,GraphQL甚至是全局狀態管理庫Redux上都能很好地發揮作用,在我們的wiki上你可以找到它的一個例子。
同樣的方法允許加載不同的數據,這取決于組件是通過服務器呈現的還是通過客戶端路由動態呈現的:
static async getInitialProps ({ res }) {
return res
? { userAgent: res.headers['user-agent'] }
: { userAgent: navigator.userAgent }
}