Next.js 入门超详解教程
简介
最近才学习了 React,现在要接触服务端渲染,趁热打铁把 Next 学一下,关于 Next.js,可以移步到知乎讨论:
知乎:关于 Next.js 的讨论
初识Next.js
安装
- Next.js 支持 Windows、Mac 和 Linux系统,均可安装,但是前提是你已经安装了 Node.js
- 创建示例项目的过程如下:
mkdir hello-next cd hello-next npm init -y npm install --save react react-dom next mkdir pages
使用
- 打开
hello-next/package.json
,替换scripts
:"scripts": { "dev": "next", "build": "next build", "start": "next start" }
- 启动
在浏览器中打开 http://localhost:3000,你会看到页面显示 404 | This page could not be found.npm run dev
- 创建你的第一个页面
- 创建
pages/index.js
,并输入:const Index = () => ( <div> <p>Hello Next.js</p> </div> ); export default Index;
- 再次输入
npm run dev
,就能看到效果了 - 上述案例中,我们在
pages/index.js
模块中默认(default)导出了一个简单的 React 组件
- 创建
- 试错
- 尝试着错一次:将
pages/index.js
改为:const Index = () => ( <div> <p>Hello Next.js </div> ); export default Index;
- 重新启动,浏览器显示:
- 一般情况下,Next.js 将跟踪此类错误并在浏览器中显示,这便于我们快速发现错误,而你修改代码并保存后,页面将立即出现对应结果,而不会重新加载整个页面,这是通过 webpack 的 模块热替换 实现的,Next 默认支持这个功能
- 尝试着错一次:将
页面间导航
Introduction
- 我们的应用程序虽然很简单,只有一个页面,但是我们可以添加任意多个页面,例如:
- 创建
pages/about.js
来新建 “About” 页面const About = () => ( <div> <p>About Page</p> </div> ); export default About;
- 修改
pages/index.js
:const Index = () => ( <div> <p>Hello Next.js</p> <a href="http://localhost:3000/about">This is a link to About-Page</a> </div> ); export default Index;
- 之后我们可以通过 http://localhost:3000/about 来访问该页面
- 之后,我们需要连接两个页面,首先想到的是可以用一个 HTML 的
<a />
标签实现,但是结果就是:浏览器会向服务器请求下一页并刷新当前页面,也就是这样做并不会执行客户端导航
- 创建
- 为了支持浏览器端导航,我们需要使用 Next.js 提供的
Link
组件,这个组件是通过next/link
导出的,接下来我们将使用它 - 我们需要准备一个简单的 Next.js 应用课程,请在
hello-next
下输入:git clone https://github.com/zeit/next-learn-demo.git
- 现在我们进入
hello-next/next-learn-demo/1-navigate-between-pages
启动程序:cd next-learn-demo/1-navigate-between-pages npm install npm run dev
- 打开 http://localhost:3000/ 访问该程序
使用Link组件
注意,接下来的操作均在
hello-next/next-learn-demo/1-navigate-between-pages
下完成的
- 在
pages/index.js
中添加:import Link from 'next/link'; export default function Index() { return ( <div> <p>Hello next.js</p> <Link href="/about"> <a>About Page</a> </Link> </div> ) }
- 在这里,我们将
next/link
导入为Link
,并按照如下的方式使用:<Link href="/about"> <a>About Page</a> </Link>
- 访问 3000 端口可查看结果
- 在这里,我们将
- 这次点击链接同样会导航到 “About” 页面,这是客户端导航,操作在浏览器中进行,而不向浏览器发送请求,你可以通过打开浏览器的 网络请求检查器(network request inspector) 来验证这一点
- 后退按钮:
当你点击链接,再点击后退时,依然会切换到历史记录的上一页,也就是next/link
为你完成了所有location.history
的操作
添加链接道具
- 或许你需要在连接中添加属性或道具,比如你需要向链接中添加
title
属性,我们可以这样添加它:
查看元素,可以看到结果如下:<Link href="/about"> <a title="About-Pages">About Page</a> </Link>
- 切记不可添加到错误的地方去,若写成如下:
则会在控制台中报错:<Link href="/about" title="About-Pages"> <a>About Page</a> </Link>
- 实际上,
Link
组件上的标题道具无效,是因为 Link 只是一个包装器组件,只接收href
和一些类似的道具。如果需要向其添加道具,则需要将道具添加到其子项,这种情况下,Link
组件的子代是锚标记
使用共享组件
Introduction
- 我们可以通过导出 React 组件并将该组件放在
pages
目录中来创建页面,每个页面的 URL 都是基于文件名的,由于导出的页面是 JavaScript 模块,因此我们也可以将其他 JavaScript 组件导入其中 - 我们将创建一个公共的 Header 组件并将其用于多个页面,最后我们将研究实现 Layout 组件,并了解它如何帮我们定义多个页面的外观
运行
- 我们之前已经安装过了
next-learn-demo
,这里直接使用:- 进入
hello-next/next-learn-demo/2-using-shared-components
,之后我们的操作也会在此目录下
- 进入
- 运行:
npm install npm run dev
创建标题组件
- 下面创建一个 Header 组件,创建
2-using-shared-components/components/Header.js
:import Link from 'next/link'; const linkStyle = { marginRight: 15, }; const Header = () => ( <div> <Link href="/"> <a style={marginRight}>Home</a> </Link> <Link href="/about"> <a style={marginRight}>About</a> </Link> </div> ); export default Header;
- 现在,导入 Header 组件并在页面中使用它:
- 将
index.js
修改为:import Header from '../components/Header'; export default function Index() { return ( <div> <Header /> <p>Hello Next.js</p> </div> ) }
- 将
about.js
修改为:import Header from '../components/Header'; export default function Index() { return ( <div> <Header /> <p>This is the About Page</p> </div> ) }
- 启动之后可以查看结果
- 将
- 试错:现在将
components
目录改名为comps
,报错如下:- 我们不需要将我们的组件放在一个特殊的目录里,也就是说,该组件目录名称可以取为任何,实际上,唯一特殊的目录是
/pages
和/public
,你甚至可以在/pages
里面创建组件.
- 我们不需要将我们的组件放在一个特殊的目录里,也就是说,该组件目录名称可以取为任何,实际上,唯一特殊的目录是
布局组件
本节依然是在
2-using-shared-components/
下完成的
- 我们将创建 Layout 组件,以实现各页面上的通用样式,在
components/MyLayout.js
中输入:import Header from './Header'; const layoutStyle = { margin: 20, padding: 20, border: '1px solid #DDD' }; const Layout = props => ( <div style={layoutStyle}> <Header /> {props.children} </div> ); export default Layout;
- 完成操作后,我们可以在页面中使用以下布局:
- 在
pages/index.js
中输入:import Layout from '../components/MyLayout'; export default function Index() { return ( <Layout> <p>Hello Next.js</p> </Layout> ) }
- 在
pages/about.js
中输入:import Layout from '../components/MyLayout'; export default function About() { return ( <Layout> <p>This is the about page</p> </Layout> ) }
- 启动,查看样式
- 在
- 试错:将
MyLayout.js
中的{props.children}
删除,再启动,观察结果:- 页面上只保留了
Header
的内容,其他的均消失了
- 页面上只保留了
渲染子组件
- 前一个试错中,我们删除了
{props.children}
,则Layout
无法呈现我们放入Layout
元素内的内容,如下所示:
但这只是创建布局组件的一种方法,以下是其他方法。export default function About() { return ( <Layout> <p>This is the about page</p> </Layout> ); }
- 方法一:布局为高阶组件
// components/MyLayout.js import Header from './Header'; const layoutStyle = { margin: 20, padding: 20, border: '1px solid #DDD' }; const withLayout = Page => { return () => ( <div style={layoutStyle}> <Header /> <Page /> </div> ); }; export default withLayout;
// pages/index.js import withLayout from '../components/MyLayout'; const Page = () => <p>Hello Next.js</p>; export default withLayout(Page);
// pages/about.js import withLayout from '../components/MyLayout'; const Page = () => <p>This is the about page</p>; export default withLayout(Page);
- 方法二:页面内容作为道具
// components/MyLayout.js import Header from './Header'; const layoutStyle = { margin: 20, padding: 20, border: '1px solid #DDD' }; const Layout = props => ( <div style={layoutStyle}> <Header /> {props.content} </div> ); export default Layout;
// pages/index.js import Layout from '../components/MyLayout.js'; const indexPageContent = <p>Hello Next.js</p>; export default function Index() { return <Layout content={indexPageContent} />; }
// pages/about.js import Layout from '../components/MyLayout.js'; const aboutPageContent = <p>This is the about page</p>; export default function About() { return <Layout content={aboutPageContent} />; }
创建动态页面
Introduction
- 之前通过使用组件,我们创建了包含了多个页面的小案例,之前为了创建一个页面,我们必须新建一个文件作为模块导出,但是在一个真正的应用程序中,我们还需动态地创建页面以显示动态内容,接下来我们会使用 查询字符串 来实现这一点
- 我们将创建一个简单的博客应用,它在主页上展示一个所有文章的列表,展示如下:
- 主页有文章列表
- 点击某标题的链接,会出现对应的文章
- 主页有文章列表
安装设置
- 我们仍然使用之前安装过的
next-learn-demo
,进入next-learn-demo/3-create-dynamic-pages
,接下来的一切也将在这个目录下完成 - 运行
npm install npm run dev
添加文章列表
- 首先,我们在文章主页添加标题列表,如下:
import Link from 'next/link'; import Layout from '../components/MyLayout.js'; const PostLink = props => ( <li> <Link href={`/post?title=${props.title}`}> <a>{props.title}</a> </Link> </li> ); export default function Blog() { return ( <Layout> <h1>My Blog</h1> <ul> <PostLink title="Hello Next.js" /> <PostLink title="Learn Next.js is awesome" /> <PostLink title="Deploy apps with Zeit" /> </ul> </Layout> ) }
通过查询字符串传递数据
- 我们将通过查询字符串作为参数(也称查询参数)并传递数据,如:
// pages/index.js const PostLink = props => ( <li> <Link href={`/post?title=${props.title}`}> <a>{props.title}</a> </Link> </li> );
- 此例中,查询参数是
title
,我们使用PostLink
来执行的操作 - 你也可以检查
Link
组件的href
属性,以此类推,你可以使用查询字符串传递任何类型的数据
- 此例中,查询参数是
创建Post
页面
- 现在我们需要创建 post 页面来显示博客文章,为此,我们需要从查询字符串中获得标题,
- 创建
pages/post.js
文件:import { useRouter } from 'next/router'; import Layout from '../components/MyLayout'; const Page = () => { const router = useRouter(); return ( <Layout> <h1>{router.query.title}</h1> <p>This is the blog post content.</p> </Layout> ); }; export default Page;
- 启动项目,并点击三个标题链接
- 上面的运作过程如下:
- 首先从
next/router
导入并使用useRouter
函数,该函数返回 Next.js 的是router
对象 - 使用路由器(router)中的
query
对象,该对象保存了所有查询参数 - 然后,使用
router.query.title
获取标题
- 首先从
- useRouter 函数的介绍:
- useRouter 允许你访问页面中的 router 对象,它是一个 React Hook,能与功能组件协同合工作
- 之前的示例中,useRouter 函数被放到预添加的页面组件中,而下面示例中,useRouter 函数在
Content
组件中,预添加的组件是Page
,但是功能不变import { useRouter } from 'next/router'; import Layout from '../components/MyLayout'; const Content = () => { const router = useRouter(); return ( <> <h1>{router.query.title}</h1> <p>This is the blog post content.</p> </> ); }; const Page = () => ( <Layout> <Content /> </Layout> ); export default Page;
使用动态路由清理URL
Introduction
请确保你正在使用是 Next.js 9 或更高版本
接下来的操作都会在next-learn-demo/4-clean-urls
中进行,请调至指定目录
- 我们已经知道了如何使用查询字符串创建动态页面,指向我们的某个博客文章的链接如:
而此链接要表达的却是:http:// localhost:3000 / post?title = Hello%20Next.js
http:// localhost:3000 / p / hello-nextjs
动态路由
启动项目,在
4-clean-urls/
下输入:npm install npm run dev
我们将使用 Next.js 的 动态路由 功能,它允许你处理
/pages
动态路由现在我们将创建新页面,并命名为
pages/p/[id].js
,这也是我们创建的第一个动态路由,步骤如下:- 首先,在
/pages
内添加文件夹/p
- 然后,你需要在
/p
文件夹中创建[id].js
,并在这些 js 文件中添加如下内容:import { useRouter } from 'next/router'; import Layout from '../../components/MyLayout'; export default function Post() { const router = useRouter(); return ( <Layout> <h1>{router.query.id}</h1> <p>This is the blog post content.</p> </Layout> ); };
- 首先,在
前一页是特殊的,它不会处理
/about
等静态路由,而是会处理p/
之后的路由,例如,此页面将处理/p/hello-next.js
,而/p/post-1/another
页面名称中的带有方括号(
[]
)使其成为动态路由,你不能使页面名称的一部分成为动态名称,而只能使全名成为动态名称,例如,支持/pages/p/[id].js
,但不支持/pages/p/post-[id].js
创建动态路线时,我们在方括号([])之间添加了 id,这是页面接受到查询参数的名称,因此对于
/p/hello-nextjs
,该query
对象将具有{ id: 'hello-nextjs' }
,我们可以使用 useRouter() 进行访问现在,我们新的动态路由添加多个链接,修改
pages/index.js
:// pages/index.js import Layout from '../components/MyLayout'; import Link from 'next/link'; const PostLink = props => ( <li> <Link href="/p/[id]" as={`/p/${props.id}`}> <a>{props.id}</a> </Link> </li> ) export default function Blog() { return ( <Layout> <h1>My Blog</h1> <ul> <PostLink id="Hello-Next.js" /> <PostLink id="Learn-Next.js" /> <PostLink id="Deploy-Next.js" /> </ul> </Layout> ) }
- 着重看看以下内容:
const PostLink = props => ( <li> <Link href="/p/[id]" as={`/p/${props.id}`}> <a>{props.id}</a> </Link> </li> )
- 在
<Link>
元素中,href
代表的是该页面在pages
文件夹中的路径,而as
代表的是该页面在浏览器中的 URL 路径
- 在
- 现在,你可以重新启动项目,注意观察 URL 的变化!
- 着重看看以下内容:
动态路由可以很好地和浏览器历史记录配合使用,而我们要做的就是将
as
添加到链接组件中
为页面获取数据
Introduction
接下来的操作都会在
next-learn-demo/6-fetching-data
中进行,请调至指定目录
- 现在我们已经能创建一个相对完整的 Next.js 应用,但还没有解决的是:如何从远程数据源中获取数据?,Next.js 提供了一个标准 API 来获取页面所需的数据,即
getInitialProps 异步函数
getInitialProps
只能添加到页面导出的默认组件中,在其他组件中是不会起作用的,它可以从远程数据源为指定页面获取数据,并将这些数据通过 props 传递到我们的页面,它会同时在客户端和服务器上工作,因为它在两个环境中都会被调用- 我们将利用
getInitialProps
构建一个应用程序来显示有关 Batman TV Shows 的信息,利用的是公开的 TVmaze API - 在即将演示的示例中,我们的主页上有一个文章列表,现在我们来展示
Batman TV shows
的节目列表,我们将从远程服务器上获取这些节目列表,而不是硬编码- 在这个示例中,我们使用的是 TVMaze API 来获取 TV shows 节目列表,这是一个搜索电视节目的 API
安装设置
- 进入
next-learn-demo/6-fetching-data
,输入:npm install npm run dev
- 在浏览器中打开 http://localhost:3000/ 查看项目
获取 Batman Shows 的数据
- 首先,我们需要安装 isomorphic-unfetch,这是我们用来获取数据的工具库,这是浏览器的 fetch API 的一个简单实现,但在客户端和服务器环境中都可以使用
npm install --save isomorphic-unfetch
- 将
pages/index.js
替换为以下内容:// pages/index.js import Layout from '../components/MyLayout'; import Link from 'next/link'; import fetch from 'isomorphic-unfetch'; const Index = props => ( <Layout> <h1>Batman TV Shows</h1> <ul> { props.shows.map(show => ( <li key={show.id}> <Link href="/p/[id]" as={`/p/${show.id}`}> <a>{show.name}</a> </Link> </li> )) } </ul> </Layout> ); Index.getInitialProps = async function() { const res = await fetch('https://api.tvmaze.com/search/shows?q=batman'); const data = await res.json(); console.log(`Show data fetched. Count: ${data.length}`); return { shows: data.map(entry => entry.show) }; }; export default Index;
- 我们着重分析下面这部分:
Index.getInitialProps = async function() { const res = await fetch('https://api.tvmaze.com/search/shows?q=batman'); const data = await res.json(); console.log(`Show data fetched. Count: ${data.length}`); return { shows: data.map(entry => entry.show) }; };
- 这是一个静态异步函数,可以添加到程序的任何页面中,使用此函数,我们就可以获取数据并作为 props 传递给我们的页面
- 以下便是我们的抓取结果,数据被抓取后,将会作为 props 的 ‘show’ 属性传递我们的页面中
- 注意,我们之前有一行用于打印信息的代码:
console.log(`Show data fetched. Count: ${data.length}`);
- 那么到底是在服务器端输出呢,还是在浏览器端的控制台输出呢,现在刷新一下浏览器,会发现之后服务端的控制台显示
- 在这种情况下,消息只会在服务端输出,因为我们的页面是在服务端绘制的,所以,我们在服务端已经有了数据,没有必要在客户端再次获取这些数据
- 那么到底是在服务器端输出呢,还是在浏览器端的控制台输出呢,现在刷新一下浏览器,会发现之后服务端的控制台显示
实现 Post 页面
- 现在让我们把 TV show 的详细信息添加到 post 中:将
pages/p/[id].js
替换为以下内容:// pages/p/[id].js import Layout from '../../components/MyLayout'; import fetch from 'isomorphic-unfetch'; const Post = props => ( <Layout> <h1>{props.show.name}</h1> <p>{ props.show.summary.replace(/<[/]?[pb]>/g), '' }</p> </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;
- 注意该页的
getInitialProps
: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 }; };
- 该函数的第一个参数是 context 对象,此对象包含一个
query
对象,我们用context.query
来获取信息,即id
对象,并使其在 TVMaze API 中获取电视节目数据
- 注意该页的
- 在这个
getInitialProps
函数中,我们添加了一个console.log
来打印节目的标题,现在我们看看它将打印到哪里- 打开服务器和客户端的控制台,然后启动项目,访问 3000 端口
- 单击第一个 Batman show 的标题
- 结果是:会在客户端的控制台输出
- 与之前不同的是,我们这次只能在客户端看到消息,这是因为我们通过客户端导航到了 post 页面
- 当我们单击链接时,由于该链接是被 Next.js 的
<Link>
组件包装过的组件,所以页面转换将在浏览器中进行,而不会想服务器发起请求 - 但是,如果你直接访问的是 post 页面,而不是点击链接(例如,你直接访问 http://localhost:3000/p/975 ),消息会被打印在服务端,而不是客户端
为组件添加样式
Introduction
接下来的操作都会在
next-learn-demo/7-styling-components
中进行,请调至指定目录
- 对于 React,我们可以使用许多不同的技术来设置样式,这些技术可以分为两大类:
- 传统的基于 CSS 文件的样式设计(包括 SASS、PostCSS 等)
- CSS in JS
- 传统的基于 CSS 文件的样式设计(尤其是 SSR)需要考虑一堆的实际问题,因此我们在为 Mext.js 设置样式时避免使用这种方法,相反,我们会在 JS 中使用 CSS,你可以使用它来设置某单个组件的样式,而不是导入 CSS 文件
- 为此,我们需要认识一个新的框架:styled-jsx,这是Next.js 预装了一个 CSS in JS 框架,它允许你为组件编写熟悉的 CSS 规则,这些 CSS 规则对组件以外的任何内容(当然也包括子组件)都没有影响,也就是说,你的 CSS 规则是有作用域的
安装设置
- 进入
next-learn-demo/7-styling-components
- 输入
npm install npm run dev
- 启动项目,访问应用
设置主页的样式
现在我们在主页中添加一些样式,进入
pages/index.js
:// pages/index.js import Layout from '../components/MyLayout'; 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 ( <Lauout> <h1>My Blog</h1> <ul> { getPosts().map(post => ( <li> <Link href="/p/[id]" as={`/p/${post.id}`}> <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: red; } a:hover { opacity: 0.6; } `}</style> </Lauout> ) }
- 那个
<style jsx>
元素,就是我们编写 CSS 规则的地方 - 现在可以启动项目,查看结果
- 那个
上面的代码中,我们没有直接在
<style>
标签内编写样式代码,而是写在模板字符串({``}
)里面的,Next.js 默认支持 babel 语法,而styled-jsx
可以看做 babel 的一个插件,它将解析所有 CSS 并将其应用于构建工程
CSS样式和嵌套组件
现在我们将会对主页做一些更改,我们将会像这样隔离 Link 组件:
import Layout from '../components/MyLayout'; 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' } ]; } const PostLink = ({ post }) => ( <li> <Link href="/p/[id]" as={`/p/${post.id}`}> <a>{post.title}</a> </Link> </li> ); export default function Blog() { return ( <Layout> <h1>My Blog</h1> <ul> {getPosts().map(post => ( <PostLink key={post.id} post={post} /> ))} </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> ); }
- 运行后发现:h1 的样式还在,但是链接已经失效了,如下:
- 运行后发现:h1 的样式还在,但是链接已经失效了,如下:
上述结果显示:CSS 样式规则对子组件中的元素没有影响,
styled-jsx
这个特性可以帮助你管理大型的应用程序的样式,在这种情况下,我们需要直接设置子组件的样式,在我们的示例中,我们需要对 Link 组件执行以下操作:const PostLink = ({ post }) => ( <li> <Link href="/p/[id]" as={`/p/${post.id}`}> <a>{post.title}</a> </Link> <style jsx>{` li { list-style: none; margin: 5px 0; } a { text-decoration: none; color: blue; font-family: 'Arial'; } a:hover { opacity: 0.6; } `}</style> </li> );
- 拓展了解:全局选择器
全局样式
有时我们确实需要更改子组件内部的样式,尤其是在使用支持 React 的 markdown 时
- 我们需要安装
react-markdown
npm install --save react-markdown
- 我们需要安装
下面就是全局样式派上用场的地方,现在就来试试利用
styled-jsx
添加一些全局样式,打开pages/p/[id].js
:import { useRouter } from 'next/router'; import Markdown from 'react-markdown'; import Layout from '../../components/MyLayout'; export default () => { const router = useRouter(); return ( <Layout> <h1>{router.query.id}</h1> <div className="markdown"> <Markdown source={` This is our blog post. Yes. We can have a [link](/link). And we can have a title as well. ### This is a titlr. And here's the content. `} /> </div> <style jsx global>{` .markdown { font-family: 'Arial'; } .markdown a { text-decoration: none; color: red; } .markdown a:hover { opacity: 0.6; } .markdown h3 { margin: 0; padding: 0; text-transform: uppercase; } `}</style> </Layout> ); };
- 注意,markdown 语法部分的缩进不可修改!!!
- 我们定义的 style 作用于整个
<div>
标签部分,也就是作用于全局,虽然这样很方便,但是还是建议写带有作用域的样式 - 尽管如此,这依然是一个比普通样式标签更好的解决方案,使用 styled-jsx 时,所有必要的特定于浏览器厂商前缀和 CSS 校验都通过了一个 Babel 插件完成了,这并不会导致额外的开销
部署 Next.js 应用程序
Introduction
接下来的操作都会在
next-learn-demo/8-deploying
中进行,请调至指定目录
- ZEIT Now 是将应用程序部署到生产环境的最简单和可扩展的方法,但是,你可以部署 Next.js 应用程序,而且它相对来讲比较简单
- 现在我们将部署 Next.js 应用程序
- 进入
next-learn-demo/8-deploying
,输入命令:npm install npm run dev
部署到 ZEIT Now
- 我们来看一下
package.json
文件的script
配置段:"scripts": { "dev": "next", "build": "next build", "start": "next start" },
- 首先,我们需要为生产环境编译我们的 Next.js 应用程序,它将生成一组优化的用于生产环境的代码:
npm run build
- 然后,你需要启动 Next.js 应用程序并监听某个端口,此服务器将执行服务器端渲染并返回静态页面(使用上述命令编译)
npm run start
- 现在可以查看效果:http://localhost:3000
运行两个实例
- 现在我们将为我们的应用程序启动两个实例,通常这样是为了横向扩展我们的应用程序
- 首先,将
package.json
中的script
配置端替换为以下的内容:"scripts": { "dev": "next", "build": "next build", "start": "next start -p %PORT%" }
- 我们更改了
start
脚本,它卸载接受一个代表端口号的参数来启动应用程序 - 注意,
%PORT%
是对于 Windows 来说的,Linux 对应的是$PORT
- 我们更改了
- 现在构建我们的应用程序:
npm run build
- 现在我们需要安装一个新的包,即:
cross-env
,我们在全局环境下安装:npm install cross-env -g
- 同时打开两个终端,分别在终端中输入:
cross-env PORT=8000 npm start cross-env PORT=9000 npm start
- 在 Linux 上,则直接打开两个命令行终端,并分别在每个命令行终端上运行:
PORT=8000 npm start PORT=9000 npm start
- 在 Linux 上,则直接打开两个命令行终端,并分别在每个命令行终端上运行:
- 现在,在浏览器分别查看:http://localhost:8000 和 http://localhost:9000
- 可以得到的结果是,你只需要对应用程序构建一次。然后就可以在任意多个端口上启动它
结语
求进之路,持之谦卑,此教程是从官网理解翻译来的,增删了一些知识,我不在乎别人怎么评价,我已经进步了…
本作品采用《CC 协议》,转载必须注明作者和本文链接
稍微看了下,还没有弄 node 服务端吧
@likunyan 加油 :muscle: