Next.js 是一个轻量级的 React 服务端渲染应用框架。本教程演示使用Next.js制作一个显示比特币汇率的网站。详细的Next.js信息请访问https://nextjs.org/。
安装
本文假设你使用的是windows操作系统,已经安装npm。npm的安装方法,请自行bing搜索。
我使用的是visual studio code
环境。命令行在该环境的terminal
下输入、运行。当然你也可以使用其它环境如windows自带的powershell,使用写字板编辑代码。
新建一个名为BitzPrice
目录,并进入该目录。
C:\Users\HP\projects>mkdir bitzprice
C:\Users\HP\projects>cd bitzprice
查看npm
版本,确认你已经安装:
PS C:\Users\HP\projects\bitzprices> npm --version
6.12.1
初始化:
PS C:\Users\HP\projects\bitzprices> npm init -y
Wrote to C:\Users\HP\projects\bitzprices\package.json:
{
"name": "bitzprices",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
安装next、react和react-dom。某强国访问境外网站非常慢,建议使用国内镜像加速,当然也可以不用。
PS C:\Users\HP\projects\bitzprices> npm config set registry https://registry.npm.taobao.org
PS C:\Users\HP\projects\bitzprices> npm install next react react-dom
npm WARN deprecated core-js@2.6.10: core-js@<3.0 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual
version of core-js@3.
npm WARN deprecated fsevents@1.2.9: One of your dependencies needs to upgrade to fsevents
v2: 1) Proper nodejs v10+ support 2) No more fetching binaries from AWS, smaller package size
...
+ react-dom@16.12.0
+ react@16.12.0
+ next@9.1.4
added 760 packages from 356 contributors in 70.692s
修改package.json
现在目录下应该有package.json
文件了。修改这个文件,将scripts
部分修改为:
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
建立index页面
新建pages
目录放置页面,在pages
目录中新建Index.js
文件,输入以下内容:
export default () => (<div>Hello Next.js</div>)
运行服务器
在terminal
输入:
C:\Users\Administrator\projects\bitzprice> npm run dev
> bitzprices@1.0.0 dev C:\Users\HP\projects\bitzprices
> next
[ wait ] starting the development server ...
[ info ] waiting on http://localhost:3000 ...
[ ready ] compiled successfully - ready on http://localhost:3000
现在浏览器访问http://localhost:3000
,应该可以看到以下画面:
新建about页面
在pages
中新建文件About.js
,输入以下内容:
export default () => (<div>About BitzPrices</div>)
在浏览器输入http://localhost:3000/about
,会看到以下内容:
让代码更React化
Index.js
修改成下面的样子:
const Index = () => (
<div>
<h1>Hello BitzPrices</h1>
</div>
);
export default Index;
现在访问页面,应该是这样:
About.js
也做类似修改:
const About = () => (
<div>
<h1>About BitzPrices</h1>
<p>查看比特币汇率的应用。</p>
</div>
);
export default About;
About页面:
添加导航
我们使用next
的Link
作为导航。修改Index.js
,内容如下:
import Link from 'next/link';
const Index = () => (
<div>
<ul>
<li><Link href="/"><a>主页</a></Link></li>
<li><Link href="/about"><a>关于</a></Link></li>
</ul>
<h1>Hello BitzPrices</h1>
</div>
);
export default Index;
修改About.js
内容如下:
import Link from 'next/link';
const About = () => (
<div>
<ul>
<li><Link href="/"><a>主页</a></Link></li>
<li><Link href="/about"><a>关于</a></Link></li>
</ul>
<h1>关于BitzPrices</h1>
<p>查看比特币汇率的应用。</p>
</div>
);
export default About;
刷新浏览器页面,应该能够使用导航了。
将导航栏放入部件中
如果要创建多个页面,每个页面中都输入相同的代码显然太麻烦。我们可以将导航栏放入单独的部件,使用导航的页面只需要引入这个部件。
在bitzprices
目录下新建components
目录,在这个目录中新建Navbar.js
文件,内容如下:
import Link from 'next/link';
const Navbar = () => (
<ul>
<li><Link href="/"><a>主页</a></Link></li>
<li><Link href="/about"><a>关于</a></Link></li>
</ul>
);
export default Navbar;
Index.js
现在只需要引入Navbar
部件并使用它:
import Navbar from '../components/Navbar';
const Index = () => (
<div>
<Navbar />
<h1>欢迎访问 BitzPrices</h1>
</div>
);
export default Index;
About.js
也做相同修改。
使用样式表
我们在Navbar.js
中展示样式表的使用,在文件中添加样式表,代码如下:
import Link from 'next/link';
const Navbar = () => (
<div>
<ul>
<li><Link href="/"><a>主页</a></Link></li>
<li><Link href="/about"><a>关于</a></Link></li>
</ul>
<style jsx>{`
ul {
background: #333;
color: #fff;
list-style: none;
display: flex;
}
ul li {
font-size: 18px;
margin: 20px 20px 20px;
}
ul li a{
color: #fff;
text-decoration: none;
}
`}</style>
</div>
);
export default Navbar;
现在的页面如下:
这个样式表只对Navbar
这个部件有效,不影响其它地方的<ul>
。
使用布局
在components
目录下新建Layout.js
文件,内容如下:
import Navbar from './Navbar';
const Layout = (props) => (
<div>
<Navbar />
{props.children}
</div>
);
export default Layout;
我们对Index.js
做如下修改:
import Layout from '../components/Layout';
const Index = () => (
<Layout>
<div>
<h1>欢迎访问 BitzPrices</h1>
</div>
</Layout>
);
export default Index;
About.js
的修改也一样:
import Layout from '../components/Layout';
const About = () => (
<Layout>
<div>
<h1>关于BitzPrices</h1>
<p>查看比特币汇率的应用。</p>
</div>
</Layout>
);
export default About;
页面不会有任何变化,但我们现在已经使用布局了。
添加页面标题及样式表引用
修改Layout.js
,引入Head
,在Head
中添加页面标题和样式表的引用(我们使用bootswatch
的cerulean样式)。Layout.js
的内容:
import Head from 'next/head';
import Navbar from './Navbar';
const Layout = (props) => (
<div>
<Head>
<title>BitzPrices</title>
<link rel="stylesheet"
href="https://bootswatch.com/4/cerulean/bootstrap.min.css" />
</Head>
<Navbar />
{props.children}
</div>
);
export default Layout;
刷新页面,
我们看到页面正文部分样式改变了,导航部分没有变。这是因为我们在Navbar.js
中使用了样式表。为了应用cerulean
样式,我们用cerulean
的Navbar
的源码代替Navbar.js
的内容,注意要将所有的class=
替换为className=
,导航菜单内容及链接地址也做修改:
const Navbar = () => (
<nav className="navbar navbar-expand-lg navbar-dark bg-dark">
<a className="navbar-brand" href="/">BitzPrices</a>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarColor02" aria-controls="navbarColor02" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarColor02">
<ul className="navbar-nav mr-auto">
<li className="nav-item active">
<a className="nav-link" href="/">主页<span className="sr-only">(current)</span></a>
</li>
<li className="nav-item">
<a className="nav-link" href="/about">关于</a>
</li>
</ul>
<form className="form-inline my-2 my-lg-0">
<input className="form-control mr-sm-2" type="text" placeholder="Search"/>
<button className="btn btn-secondary my-2 my-sm-0" type="submit">Search</button>`
</form>
</div>
</nav>
);
export default Navbar;
将页面内容放入容器
修改Layout.js
,将{props.children}
放入div
中:
<div className="container">
{props.children}
</div>
通过API获取数据
我们要获取的数据来自https://www.coindesk.com/,地址为https://api.coindesk.com/v1/bpi/currentprice.json,通过这个地址获取的json数据为:
{
"time":{
"updated":"Dec 8, 2019 02:06:00 UTC",
"updatedISO":"2019-12-08T02:06:00+00:00",
"updateduk":"Dec 8, 2019 at 02:06 GMT"
},
"disclaimer":"This data was produced from the CoinDesk Bitcoin Price Index (USD). Non-USD currency data converted using hourly conversion rate from openexchangerates.org",
"chartName":"Bitcoin",
"bpi":{
"USD":{"code":"USD","symbol":"$","rate":"7,454.3000","description":"United States Dollar","rate_float":7454.3},
"GBP":{"code":"GBP","symbol":"£","rate":"5,674.7126","description":"British Pound Sterling","rate_float":5674.7126},
"EUR":{"code":"EUR","symbol":"€","rate":"6,740.6328","description":"Euro","rate_float":6740.6328}
}
}
获取数据,我们需要用到isomorphic-unfetch
。安装:
PS C:\Users\HP\projects\bitzprices> npm install isomorphic-unfetch
修改Index.js
如下:
import Fetch from 'isomorphic-unfetch';
import Layout from '../components/Layout';
const Index = (props) => (
<Layout>
<div>
<h1>欢迎访问 BitzPrices</h1>
{props.bpi.time.updated}
</div>
</Layout>
);
Index.getInitialProps = async function() {
const res = await Fetch(
'https://api.coindesk.com/v1/bpi/currentprice.json'
);
const data = await res.json();
return {
bpi: data
}
}
export default Index;
刷新页面:
将Prices放入单独的部件
在components
目录下,新建Prices.js
文件,内容如下:
class Prices extends React.Component {
state = {
currency: 'USD'
}
render() {
return (<div>
Prices
</div>);
}
}
export default Prices;
修改Index.js
内容,
...
const Index = (props) => (
<Layout>
<div>
<h1>欢迎访问 BitzPrices</h1>
<Prices bpi={props.bpi} />
</div>
</Layout>
);
...
现在主页应该是这样:
显示汇率
修改Index.js
内容:
import Fetch from 'isomorphic-unfetch';
import Layout from '../components/Layout';
import Prices from '../components/Prices';
const Index = (props) => (
<Layout>
<div>
<h1>欢迎访问 BitzPrices</h1>
<p>当前的比特币汇率:</p>
<Prices bpi={props.bpi} />
</div>
</Layout>
);
Index.getInitialProps = async function() {
const res = await Fetch(
'https://api.coindesk.com/v1/bpi/currentprice.json'
);
const data = await res.json();
return {
bpi: data
}
}
export default Index;
修改Prices.js
内容:
class Prices extends React.Component {
state = {
currency: 'USD'
}
render() {
return (<div>
<ul className="list-group">
<li className="list-group-item">
Bitcoin rate for {this.props.bpi.bpi.USD.description}:<span className="badge badge-primary">{this.props.bpi.bpi.USD.code}</span><strong>{this.props.bpi.bpi.USD.rate}</strong>
</li>
</ul>
</div>);
}
}
export default Prices;
刷新页面:
获取其它币种汇率
修改Prices.js
,增加获取其它币种的数据:
class Prices extends React.Component {
state = {
currency: 'EUR'
}
render() {
let list = '';
if (this.state.currency === 'USD') {
list = <li className="list-group-item"> Bitcoin rate for {this.props.bpi.bpi.USD.description}:<span className="badge badge-primary">{this.props.bpi.bpi.USD.code}</span><strong>{this.props.bpi.bpi.USD.rate}</strong></li>
} else if (this.state.currency === 'GBP') {
list = <li className="list-group-item"> Bitcoin rate for {this.props.bpi.bpi.GBP.description}:<span className="badge badge-primary">{this.props.bpi.bpi.GBP.code}</span><strong>{this.props.bpi.bpi.GBP.rate}</strong></li>
} else if (this.state.currency === 'EUR') {
list = <li className="list-group-item"> Bitcoin rate for {this.props.bpi.bpi.EUR.description}:<span className="badge badge-primary">{this.props.bpi.bpi.EUR.code}</span><strong>{this.props.bpi.bpi.EUR.rate}</strong></li>
}
return (<div>
<ul className="list-group">
{list}
</ul>
</div>);
}
}
export default Prices;
现在,因为currency
的值为EUR
,刷新页面,应该能够看到如下画面:
动态选择要查询的币种
修改Prices.js
,
class Prices extends React.Component {
state = {
currency: 'USD'
}
render() {
let list = '';
if (this.state.currency === 'USD') {
list = <li className="list-group-item"> Bitcoin rate for {this.props.bpi.bpi.USD.description}:<span className="badge badge-primary">{this.props.bpi.bpi.USD.code}</span><strong>{this.props.bpi.bpi.USD.rate}</strong></li>
} else if (this.state.currency === 'GBP') {
list = <li className="list-group-item"> Bitcoin rate for {this.props.bpi.bpi.GBP.description}:<span className="badge badge-primary">{this.props.bpi.bpi.GBP.code}</span><strong>{this.props.bpi.bpi.GBP.rate}</strong></li>
} else if (this.state.currency === 'EUR') {
list = <li className="list-group-item"> Bitcoin rate for {this.props.bpi.bpi.EUR.description}:<span className="badge badge-primary">{this.props.bpi.bpi.EUR.code}</span><strong>{this.props.bpi.bpi.EUR.rate}</strong></li>
}
return (<div>
<select onChange={e => this.setState({currency: e.target.value})} className="form-control">
<option value="USD">USD</option>
<option value="GBP">GBP</option>
<option value="EUR">EUR</option>
</select>
<ul className="list-group">
{list}
</ul>
<br/>
</div>);
}
}
export default Prices;
现在可以选择要查询的币种了:
本文章内容参考了Traversy Media在Youtube上的视频。