我们为开源的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 }
}