在Next.js中构建一个天气预报应用程序
在本教程中,我们将在Next.js中构建一个天气应用程序,展示如何通过外部API从世界各地的城市抓取实时网络数据。我们还将使用API来查询每日天气预报。
本教程中我们将使用的API是Openweather API。
前提条件
要跟上本教程,必须具备以下条件。
- 对React有基本了解,并想学习如何用Next.js制作一个服务器端渲染的React应用。
- 安装了一个代码编辑器。
在本教程结束时,你应该能够。
- 知道Next.js是什么。
- 知道何时使用它。
- 知道Next.js相对于React.js的主要优势。
- 安装Next.js。
- 能够使用Next.js创建一个应用程序。
- 获取外部API。
概述
- 简介
- 什么是Next.js
- 你为什么要使用它
- 对比Next和React
- 构建一个天气预报应用程序
什么是Next.js?
Next.js是一个建立在Node.js之上的开源开发框架,能够在基于React的网络应用中进行服务器端渲染和生成静态网站。
它是一个JavaScript框架,可以让你用React创建真正快速和用户友好的静态网络应用。它是创建你未来网站的一个神奇的工具。它有很多奇妙的功能和好处,可以让Next.js成为你开发下一个网络应用的首选。
你为什么要使用它?
这个框架有各种优势,对于客户应用和开发(前端)团队都是如此。让我们来看看其中的一些好处。
在服务器上进行渲染(SSR)
Next.js为React组件配备了一个独特的服务器端渲染(SSR)解决方案。它确保开发人员能够使用Next在服务器上显示他们的代码。
然而,在Next.js之前,开发人员必须处理大量的修补工作,如缓存、服务器上的负载、内容和应用程序的架构等常见问题。
改进性能
由于Next.js限制了浏览器一次性下载和执行大量的JavaScript代码,它可以大大改善首次绘制时间(TTFD)等指标。
TTFD决定了用户在其屏幕上看到最开始的内容需要多长时间,理想情况下,这应该少于一秒钟。
加强搜索引擎优化
我们都知道这一点已经涵盖了很多,但同样值得注意的是,使用服务器端渲染来代替客户端渲染的JavaScript可以大大增加你的搜索引擎曝光率。
你可以设计一个具有你所需要的所有功能和互动性的网络应用程序,同时仍然享受静态网站的SEO优势。这也将为你提供一个显著的竞争优势。
静态网站的产生
假设你经营一个博客网站,这种类型的网站一旦推出,其内容就很少改变。因此,不需要来自客户端或服务器的数据。一切都在HTML和CSS页面上。
因此,浏览器只需要解析它们,并可能需要少量的javascript。
静态网站是这种类型的网站的名称。因为它们需要履行的职责较少,所以速度极快。我们可以在构建时用Next.js制作静态HTML,而不再需要担心这个问题。
对比Next.js和React
由于Next.js是建立在React框架之上的,我们将根据来理解它们。
- 特点
- 编码的速度
- 性能
- 文档
- 社区
让我们开始吧!
特点
在Next.js中,我们利用React来协助我们创建单页应用程序。
它具有以下特点。
- 在服务器上进行渲染(SSR)
- 导出静态数据(SSG)
- 预渲染
- 自动完成构建大小的优化。
- 更快地编译代码
所有这些功能将协助你开发一个功能性的、可随时使用的应用程序。
React可以简单地进行扩展,使用Redux等框架整合路由和状态管理模式等功能。React是一个简单的框架,几乎可以适用于任何项目。
编码的速度
我们都知道,在React中,你必须首先创建一个组件,然后将其添加到路由器中,为我们的React项目创建页面。
与React不同,要为Next.js项目制作一个页面,我们只需将其放在pages 文件夹中,并将其链接到所需的标题组件上。
很简单吧?
Next.js有助于简化你的生活,减少你写的代码量,使项目更容易操作。
使用React也有好处。例如,Create React App ,在设置和定制你的开发环境时,这个工具可以为你节省时间和精力。你所需要做的就是运行该命令,以获得所有你需要的工具,使你的React项目启动和运行。
性能
因为静态网站和服务器端渲染功能使Next.js应用程序真的很快,不仅如此,它们在默认情况下工作,这要归功于性能增强技术,如图像优化和许多其他技术。
因此,当你使用Next.js时,你会得到自动服务器渲染和代码拆分(这将提高性能)。此外,SSR(服务器端渲染)将大大增加你的应用程序的性能。
对于React,有几件事让它不值得讨论。它只支持开箱即用的客户端渲染,如果你要构建一个高性能的应用程序,这是不足够的。
文档
这一点是不言而喻的,但它经常被忽视。虽然某些框架的主页设计可能很吸引眼球,但除了阅读文档外,你仍然需要额外的教程,甚至是文章来入门。
对于文档,React.js和Next.js都提供了惊人的选择。
Next.js包括一套 "边做边学 "的文档,引导你完成组件开发和路由等任务。React也提供了一个类似的设置,有多个涵盖基本原理的教程被展示。
你可以查看他们的官方文档(包括React和Next.js),以便更好地理解。
在我看来,Next.js只在性能上比React得分高,其他都是平分秋色。
既然我们已经看了Next.js以及为什么你应该使用它,让我们继续使用Next构建一个天气预报应用程序。
要求
前往openweathermap.org/api注册,以便访问API。点击API密钥,你应该看到类似这样的东西。

你可以选择生成你的密钥(建议这样做)。
建立一个天气预报应用程序
创建Next项目时,第一件事是安装Next.js,方法是打开我们的终端,用命令导航到你要安装的文件夹。
npm create-next-app next-weather-build
为了方便起见,我们将使用一个包含JSON文件的内置前端。
npm install
这将为该项目安装所有的节点模块。
npm install dotenv
我们将使用dotenv作为我们的环境变量,这是我们的API密钥。
npm install moment
这使我们能够格式化日期。
npm moment-timezone
npm install sass
这让我们可以编译我们的样式。
现在让我们通过运行这个命令来启动我们的项目。
npm run dev
这将启动我们的下一个项目。如果你在加载SWC ,你可以禁用它并切换到babel,方法是在你的项目文件夹中创建一个名为.babelrc 的文件并添加以下代码。
{
"presets": ["next/babel"]
}
重新运行服务器,这应该可以解决这个问题,一旦你启动服务器,点击localhost链接,你应该看到如下图所示的东西。

我们要做的第一件事是我们的Searchbox.js 。我们要确保每当我们输入一个城市,它就会给我们带来一个与我们的城市相匹配的结果,如果不是,它就应该告诉我们没有结果。
为了做到这一点,我们首先需要从lib 文件夹下的JSON文件中导入城市列表。此外,我们还必须从next 中导入link 。
import cities from "../lib/city.list.json";
import Link from "next/link";
import Router from "next/router";
然后,添加以下代码。
export default function SearchBox({ placeholder }) {
const [query, setQuery] = React.useState("");
const [results, setResults] = React.useState([]);
const onChange = (e) => {
const { value } = e.target;
setQuery(value);
};
我们在这里所做的是。
- 创建一个名为
query的状态变量,同时将我们的变量(react.usestate)状态设置为空字符串。我们要做的是;当我们输入一个值时,我们希望这个值能被保存到我们的query。 - 我们还创建了一个名为
results的新状态变量,并给它一个空字符串。这将负责显示我们的匹配城市。 const OnChange应该被连接到输入端,所以当我们在输入端输入时,我们会得到一个事件,这个事件会给我们一个值。我们还得到了我们的value,并将其设置为我们的query。- 现在,我们必须使用我们的
query,并根据我们的城市数据进行搜索,看看它是否与我们的任何城市相匹配。
让我们在我们的setQuery (值)之后,在闭合的大括号之前粘贴下面的代码。
let matchingCities = [];
if (value.length > 3) {
for (let city of cities) {
if (matchingCities.length >= 5) {
break;
}
const match = city.name.toLowerCase().startsWith(value.toLowerCase());
if (match) {
const cityData = {
...city,
slug: `${city.name.toLowerCase().replace(/ /g, "-")}-${city.id}`,
};
matchingCities.push(cityData);
}
}
}
return setResults(matchingCities);
所以,我们在上面的代码中所做的是;如果我们的输入超过3个字符,它应该映射到城市,并带出与我们的值相匹配的结果,使我们不需要进行不必要的搜索。
我们还创建了一个蛞蝓,以创建一个独特的页面名称,并通过我们的结果显示它。我们可以使用我们的蛞蝓来找出区域,所以当我们为每个城市创建我们的天气页面时,我们知道我们是通过我们的结果指的是哪个区域。
让我们把我们的值添加到我们的输入标签。
value = {query}onChange={onChange}
让我们也呼出我们的query ,因为这使得我们的匹配城市可以通过结果进行访问。
现在让我们添加一些条件逻辑来映射我们的结果,使用city.slug 作为我们的关键。
就在我们的input 标签之后,写下以下代码。
{
query.length > 3 && (
<ul>
{results.length > 0 ? (
results.map((city) => (
<li key={city.slug}>
<Link href={`/location/${city.slug}`}>
<a>
{city.name}
{city.state ? `, ${city.state}` : ""} <span>({city.country})</span>
</a>
</Link>
</li>
))
) : (
<li className="search__no-results">NO results</li>
)}
</ul>
);
}

显示我们的匹配城市
让我们导航到pages 文件夹下一个名为location 的文件夹,找到名为[city].js 的文件,如图所示。
import cities from "../../lib/city.list.json";
import moment from "moment-timezone";
import Link from "next/link";
import Head from "next/head";
我们还将导入我们的今天的天气和每小时的天气,我们很快就会进行这项工作。
import TodaysWeather from "../../components/TodaysWeather";
import HourlyWeather from "../../components/HourlyWeather";
我们希望这个页面能够访问我们要做的任何一种动态lug。这就是Next.js出现的地方。
就像我们之前学到的,Next.js是服务器端渲染的,这意味着--我们可以在页面加载之前抓取数据,而不是像使用react那样,我们必须在页面加载之前等待抓取数据。
我们将使用getserversideprops ,然后传入,所以让我们粘贴这个。
export async function getServerSideProps(context) {
const city = getCityId(context.params.city);
if (!city) {
return {
notFound: true,
};
}
}
我们在这里所做的是,我们使用一个函数getserversideprops 来获取servicesideprops,因为我们的数据可能会改变(我们的数据是实时天气数据,可能会改变)。如果我们的数据没有变化,我们会使用getstaticprops 。
我们要做的下一件事是利用我们的环境变量来链接外部API。
让我们首先创建一个.env 文件,将其命名为.env.local 。
在这里,我们将创建一个API密钥,并使其等同于我们开放的天气地图的API密钥,像这样。
API_KEY =78b705a31f5b7bebcfe38a2624152e8d
请确保这是你输入的API密钥。接下来,我们到我们的next.config.js ,粘贴这个。
require("dotenv").config();
module.exports = {
reactStrictMode: true,
images: {
domains: ["openweathermap.org"],
},
};
我们粘贴的代码还允许我们使用openweather图像图标,我们将在显示天气数据时使用。
现在,让我们回到我们的[city].js ,在最后一个大括号结束前的条件语句下面粘贴以下代码。
const res = await fetch(
`https://api.openweathermap.org/data/2.5/onecall?lat=${city.coord.lat}&lon=${city.coord.lon}&appid=${process.env.API_KEY}&exclude=minutely&units=metric`
);
const data = await res.json();
if (!data) {
return {
notFound: true,
};
}
const hourlyWeather = getHourlyWeather(data.hourly, data.timezone);
const weeklyWeather = data.daily;
return {
props: {
city: city,
timezone: data.timezone,
currentWeather: data.current,
hourlyWeather: hourlyWeather,
weeklyWeather: weeklyWeather,
}, // will be passed to the page component as props
};
这里,我们正在请求我们的API并获取数据。请注意,我们将单位设置为摄氏度,而且我们还排除了一些数据,如minute 数据。我们还写了一个条件语句来检查数据是否可用,如果找到的话,它将返回我们的城市数据作为道具。
现在,让我们粘贴以下代码。
const getCityId = (param) => {
const cityParam = param.trim();
// get the id of the city
const splitCity = cityParam.split("-");
const id = splitCity[splitCity.length - 1];
if (!id) {
return null;
}
const city = cities.find((city) => city.id.toString() == id);
if (city) {
return city;
} else {
return null;
}
};
这将帮助我们获得我们城市的id ,如果没有,则返回null (没有收集数据)。为了得到我们每小时的天气数据,让我们粘贴这个代码。
const getHourlyWeather = (hourlyData, timezone) => {
const endOfDay = moment().tz(timezone).endOf("day").valueOf();
const eodTimeStamp = Math.floor(endOfDay / 1000);
const todaysData = hourlyData.filter((data) => data.dt < eodTimeStamp);
return todaysData;
};
在这里,我们写一个函数来获取每小时的数据。
创建一个名为moments 的变量,帮助我们在传递时区信息时获得当前时间。我们创建了变量todaysData ,用响应值除以1000 ,因为openweather返回的是毫秒值(我们希望它是以秒为单位)。
现在,让我们来访问我们的数据。在我们的函数City ,在括号内,添加这些道具。
{
city,
weather,
currentWeather,
hourlyWeather,
weeklyWeather,
timezone,
}
然后清除我们在它下面返回的内容,并粘贴这个。
<div>
<Head>
<title>{city.name} Weather - Next Weather App</title>
</Head>
<div className="page-wrapper">
<div className="container">
<Link href="/">
<a className="back-link">← Home</a>
</Link>
<SearchBox placeholder="Search for a location" />
<TodaysWeather
city={city}
weather={weeklyWeather[0]}
timezone={timezone}
/>
<HourlyWeather hourlyWeather={hourlyWeather} timezone={timezone} />
</div>
</div>
</div>
在这里,我们将显示每小时的天气和今天的天气,将它们传递给它们的组件,这样我们就可以访问数据。
链接和搜索只是让我们回去或搜索另一个城市。
显示天气
让我们导航到名为components 的文件夹,点击todaysweather ,在这里我们要输入我们想要显示的信息。
让我们首先导入以下包。
import moment from "moment-timezone";
import React from "react";
import Image from "next/image";
在函数里面,让我们把道具city ,以及weather 和timezone 。
{
city, weather, timezone;
}
现在,我们要做的是渲染城市数据和天气数据。所以,在我们的div classname ,粘贴这个。
<h1>
{city.name} ({city.country})
</h1>
<h2>
<span>{weather.temp.max.toFixed(0)}°C</span>
<span>{weather.temp.min.toFixed(0)}°C</span>
</h2>
在我们的span sunrise 之后粘贴这个。
<span>{moment.unix(weather.sunrise).tz(timezone).format("LT")}</span>
在我们的span sunset ,粘贴这些。
<span>{moment.unix(weather.sunset).tz(timezone).format("LT")}</span>
让我们粘贴一些图片图标,天气API允许我们在div 图标包装器下使用。
<Image
src={`https://openweathermap.org/img/wn/${weather.weather[0].icon}@2x.png`}
alt="Weather Icon"
layout="fill"
/>
然后我们想获得天气描述,所以我们在两个封闭的divs 后粘贴这个。
<h3>{weather.weather[0].description}</h3>
我们已经完成了今天的天气
让我们来处理每小时的天气。
每小时天气(Hourlyweather
导航到components 文件夹下的文件Hourlyweather 。就像我们对todaysweather ,我们要渲染天气数据。首先,我们要导入以下内容。
import React from "react";
import moment from "moment-timezone";
import Image from "next/image";
在我们导出函数城市的括号内,我们粘贴这个。
{
hourlyWeather, timezone;
}
在我们的div 小时天气之后,让我们粘贴这个。
<div className="hourly__inner">
{hourlyWeather.length > 0 &&
hourlyWeather.map((weather, index) => (
<div className="hourly__box-wrapper" key={weather.dt}>
<div className="hourly__box">
<span
className={`hourly__time ${index == 0 ? "hourly__time-now" : ""}`}
>
{index == 0
? "NOW"
: moment.unix(weather.dt).tz(timezone).format("LT")}
</span>
<Image
src={`https://openweathermap.org/img/wn/${weather.weather[0].icon}@2x.png`}
alt={weather.weather[0].description}
width="100"
height="100"
/>
<span>{weather.temp.toFixed(0)}°C</span>
</div>
</div>
))}
</div>
因此,就像我们的todaysweather ,我们正在渲染我们的天气数据(天气,格式,显示一个来自我们openweather的图像图标)或结果应该是这样的。

如果你有这样的东西,那么恭喜你,你刚刚为自己建立了一个天气预报应用程序
当我们在城市页面下搜索另一个城市时,我们要做的是,我们要倾听页面的变化,并将我们的查询设置为更新的值。
让我们把这些代码粘贴到我们的搜索框内,就在我们创建的两个变量状态之后。
React.useEffect(() => {
const clearQuery = () => setQuery("");
Router.events.on("routeChangeComplete", clearQuery);
return () => {
Router.events.off("routeChangeComplete", clearQuery);
};
}, []);
我们使用useEffect ,打开一个函数,在里面我们创建了一个名为clearQuery 的迷你函数,并利用Next.js路由。
当我们改变页面或清除查询时,该路由将告诉我们。
总结
在本教程中,我们已经了解了Next.js是什么以及何时使用它。我们还将它与React.js进行了比较。并且,我们使用Next.js构建了一个天气应用程序。