JavaScript >> Javascript 文檔 >  >> Tags >> Next.js

Next.js 入門指南 - 創建 Next.js 應用程序

簡介

Next.js 是由 Vercel 創建的開源 JavaScript 框架,用於通過 服務器端渲染 等功能增強 React 應用程序 靜態站點生成 .

傳統上,React 用於創建單頁應用程序 (SPA) 其內容在客戶端呈現。 Next.js 通過允許開發人員創建可以執行服務器端操作、預取路由並支持 TypeScript 的應用程序來擴展這一點。最重要的是 - 默認情況下它不需要任何額外的配置!

注意: 此應用程序的完整代碼可以在 GitHub 上找到。

安裝和設置

創建新 Next.js 應用程序的最簡單方法是使用 create-next-app 命令行工具。您可以通過 npm 安裝它 :

$ npm install create-next-app

安裝後,您可以通過調用該工具並為您的項目提供一個名稱來初始化一個新的 Next.js 應用程序:

$ npx create-next-app weather-app

注意: 如果您還沒有 create-next-app 已安裝 - npx 會提示你自動安裝。

一旦工具完成了一個骨架項目的初始化,讓我們移動到目錄並看看裡面:

$ cd weather-app
$ ls
README.md       node_modules/      package.json  public/
next.config.js  package-lock.json  pages/        styles/

標準 package.json , package-lock.jsonnode_modules 但是,我們也有 /pages , /public/styles 目錄,以及一個 next.config.js 文件!

讓我們來看看這些是什麼。

Next.js 的特點

Next.js 最終是 React 的一個擴展,它確實引入了一些新的東西,使 React 應用程序開發更簡單、更快 - 從 Next.js 頁面開始 .

頁面

Next.js 使用其默認的基於文件系統的路由器,讓使用 React 創建多頁應用程序變得非常容易 .您不需要安裝任何額外的包,例如 react-router-dom ,或者完全配置路由器。

所有 Next.js 項目都包含一個默認的 /pages 目錄,這是您將使用的所有 React 組件的主目錄。對於每個組件 - 路由器將基於該組件提供一個頁面。

例如,假設我們創建一個組件 contact.js ,在 /pages 內 目錄:

const Contact = () => {
    return (
        <div>
            Contact
        </div>
    )
}

export default Contact

Next.js 使用的基於文件系統的路由器將使該頁面在 /contact 下可訪問 路線!此規則的唯一例外是 index.js 頁面,不在 /index 下 路線,而是在 / 提供服務 .

此外,您可以嵌套路線 使用 Next.js,因此您可以輕鬆創建 /weather/berlin 通過創建 /weather 動態地創建 文件夾和動態 [city].js 組件作為頁面。

注意: 對於動態路由,您需要 name 一組方括號中的文件。沒有它們,它是一個靜態文字字符串,它將被解析。 city.js 將解析為 /weather/city 路線,並且不會匹配其他任何東西。另一方面 [city.js] 將匹配 /weather/berlin , /weather/london , /weather/lagos 等。

組件

<Link> 組件可用於在應用中的頁面之間導航。假設我們的項目頁面結構在/pages下有幾個頁面 目錄:

- pages
  - index.js
  - weather.js
  - contact.js

<Link> 組件的href 屬性可以用來指向每個頁面的相對路徑,從/pages開始 目錄:

import Link from "next/link";

export default function Home() {
  return (
    <div>
      <Link href="/">Home</Link>
      <Link href="/weather">Weather</Link>
      <Link href="/contact">Contact</Link>
    </div>
  )
}

當然,如果您有嵌套的文件層次結構,您也可以鏈接到嵌套頁面:

- pages
  - weather
    - [city].js
import Link from "next/link";

export default function Weather() {
  return (
    <div>
      <Link href="/weather/berlin">Berlin</Link>
      <Link href="/weather/london">London</Link>
      <Link href="/weather/lagos">Lagos</Link>
    </div>
  )
}

<Link> 組件也可以預取 頁!一旦頁面被加載,並且有多個指向其他頁面的鏈接 - 如果您知道某個頁面要經常訪問,或者希望確保該頁面盡快加載(不影響初始頁面),您可以預取與 <Link> 關聯的頁面 讓過渡更快更順暢!

例如,可以想像,在天氣應用中,人們更有可能導航到 /weather 從主頁路由,而不是 /about .無需預取 about.js 頁面/組件,因為您會為不太可能被點擊的頁面增加服務器負擔。另一方面 - weather.js 最有可能是人們訪問的下一條路線,因此您可以通過預取來縮短過渡時間:

import Link from "next/link";

export default function Home() {
  return (
    <div>
      <Link prefetch=true href="/weather">Weather</Link>
      <Link prefetch=false href="/about">About Us</Link>
    </div>
  )
}

其他一些屬性包括 scroll 屬性(默認為 true ),當用戶使用 <Link> 重新路由自己時,它會將用戶導航到頁面頂部 .這是一個非常明智的默認設置,儘管您可能希望將其關閉以獲得更具體的效果。

另一個值得注意的屬性是 replace 屬性,默認為 false .如果設置為 true ,當您使用 <Link> 導航到新頁面/路線時,它將替換歷史堆棧中的最新條目,而不是推送新條目 .

預渲染頁面

說到預取和預渲染頁面——Next.js 的這個特性是比較相關的特性之一。同樣,默認情況下,Next.js 將預先獲取您鏈接到的所有頁面,從而在它們之間實現平滑、快速的轉換。

對於每個頁面,您可以選擇服務器端渲染 靜態生成 使用哪種技術取決於您用於獲取數據的功能。您不必在整個應用程序中都遵循這些技術之一!

如果您在服務器端呈現頁面,它們將在每個請求上呈現,使用您的服務器資源,並發送給最終用戶。如果您靜態生成一個頁面,它會生成一次,並且在構建時間之後可以重複使用。

注意: 一般來說,您會希望使用 Static Generation 只要不需要使用服務器端渲染 因為頁面可以被緩存和重用,從而節省了寶貴的計算量。每當頁面上的組件頻繁出現時,服務器端渲染 是必需的,並且在請求新數據時呈現頁面(其中一些可能取決於請求本身)。

您還可以決定讓頁面上的某些頁面或元素通過客戶端渲染進行渲染 這會將負載放在最終用戶的機器上,但您無法保證也無法控制他們的資源,因此通常情況下,您希望避免對他們進行任何密集計算。

這對最終用戶有何影響,它如何改進普通的 React 應用程序?預渲染允許用戶在加載任何 JavaScript 代碼之前查看頁面。加載 JavaScript 需要很短的時間 - 但是這些 毫秒 不經意間影響了我們的認知。即使頁面顯示為一旦加載所有組件後用戶就會看到 - 它們都還沒有工作。

只有在頁面顯示後,組件才會被處理和加載以成為交互式組件。這個過程稱為水合 .

如果沒有 Next.js,頁面會在 JavaScript 加載時為空,並且正在初始化組件。

由於預渲染是一個積分 作為 Next.js 的一部分,我們將通過 SSR 了解一些可用於促進預渲染的功能 和SG .

獲取服務器端數據 - getServerSideProps()

getServerSideProps() 函數用於執行與服務器相關的操作,例如從外部 API 獲取數據。同樣,您希望在頁面上的數據快速變化時執行 SSR,並且緩存它沒有意義。例如,API 可能會響應更新的股票價格,或者每秒的時鐘時間,以及用戶的每個請求——這些都應該是最新的。

這是一個向示例 API 發送請求並將接收到的數據作為道具傳遞給我們的頁面組件的示例:

const Weather = ({temperature}) => {
// display temperature

}
export default Weather

export async function getServerSideProps() {
  const res = fetch('http://example.com/api')
  ...
  const temperature = res.temperature
  return {
    props: {temperature},
  }
}

getServerSideProps() 收到 context 對象,其中包含與服務器相關的信息,例如傳入請求、服務器響應、查詢。這很關鍵,因為渲染本身可能依賴於 context .

靜態生成路徑 - getStaticPaths()

我們使用 getStaticPaths() 函數來定義應該為動態路由靜態生成的路徑列表。假設我們有一個動態路由 pages/weather/[city].js 我們導出一個 getStaticPaths() 該文件中的函數如下:

export async function getStaticPaths() {
  return {
    paths: [{ params: { id: 'paris' } }, { params: { id: 'london' } }],
  };
}

Next.js 會自動靜態生成 /weather/paris/weather/london 在構建時為我們服務。

靜態生成道具 - getStaticProps()

getStaticProps() 功能類似於 getServerSideProps() 從某種意義上說,我們使用它在預渲染頁面上加載道具。然而,在這種情況下,props 是在構建時靜態生成的,並在以後被所有請求重用,而不是在請求時呈現:

export async function getStaticProps() {

  const res = await fetch('http://someapi/toget/cities')
  ...
  const cities = await res.json()
  return {
    props: {
      cities,
    },
  }
}

注意: getStaticPaths() 不適用於 getServerSideProps() - 改為使用 getStaticProps() .最好僅在要預渲染的數據加載較快或可以公開緩存的情況下使用此功能。

&搜索引擎優化

由於單頁應用程序 很難被搜索引擎抓取,為搜索引擎優化 React 應用程序可能會很困難。雖然 Next.js 服務器端渲染解決了這個問題,但該框架還包含一個特殊的 <Head /> 組件,可以輕鬆地將元素附加到頁面的頭部。

因此,更新您的應用頁面的 SEO 設置,如標題標籤、元描述以及您將包含在標準 HTML <head> 中的任何其他元素 標籤更簡單:

import Head from "next/head";

const Contact = () => {
  return (
    <div>
      <Head>
        <title>Contact</title>
        <meta name="description" content="Welcome to our contact page!"/>
      </Head>
    </div>
  );
};

export default Contact;

使用 Next.js 創建 API 路由

Next.js 還提供了在您的項目中開發自己的 API 的功能,其過程類似於創建頁面的過程!首先,您需要創建一個新的 api /pages 下的子目錄 (即 /pages/api ),並且此目錄中的任何文件都將被路由到 /api/* .

要使這些端點正常工作,您必須導出默認的 handler() 每個端點的函數(請求處理程序),它接收兩個參數:req (傳入請求)和 res (服務器響應)。

為了試試這個,讓我們更新我們的 /pages/api/weather.js 示例內容如下:

export default function handler(req, res) {
  res.status(200)
  res.json({
    city: 'London',
    temperature: '20',
    description: 'sunny',
  });
}

如果我們訪問或發送請求到 /api/weather ,我們應該看到返回的倫敦的虛擬天氣信息,以及 200 響應碼。

靜態資產

在某些時候,您可能需要加載圖像、視頻、字體等資產。所有 Next.js 項目都有一個名為 /public 的目錄 為此目的。

例如,如果我們在 /public/weather-icon.svg 下有一個文件 ,我們可以在任何組件中訪問它:

const WeatherIcon = () => {
  return <img src="/weather-icon.svg" alt="Weather Icon"/>
}

export default WeatherIcon

Next.js 環境變量

環境變量是在我們的應用程序之外設置值的變量,我們主要使用它們來保存 API 密鑰或服務器配置等敏感數據,以避免將它們推送到 Github、GitLab 等版本控制工具。

Next.js 通過 .env.local 提供對使用環境變量的支持 文件。此文件中的所有變量都映射到 process.env .

如果我們有一個 .env.local 包含以下變量的文件:

WEATHER_API_KEY=abcd123
CITY_API_KEY=123abc

我們現在可以通過 process.env.WEATHER_API_KEY 訪問它們 和 process.env.CITY_API_KEY .

免費電子書:Git Essentials

查看我們的 Git 學習實踐指南,其中包含最佳實踐、行業認可的標準以及隨附的備忘單。停止谷歌搜索 Git 命令並真正學習 它!

此外,默認情況下,環境變量不會在瀏覽器中公開,只能在 Node.js 環境(在服務器端)中訪問。但是,我們可以通過在首選變量前加上 NEXT_PUBLIC_ 來選擇將它們公開給客戶端 .例如,如果我們有一個變量:

NEXT_PUBLIC_CITY_API_KEY=123abc

現在可以通過 process.env.NEXT_PUBLIC_CITY_API_KEY 在我們應用程序中的任何位置訪問此變量 .

使用 Next.js 構建天氣應用

我們將構建一個天氣應用程序,用於檢測用戶所在的城市並根據該信息顯示天氣信息。此外,我們將實施一項功能,允許用戶隨時保存特定天氣信息並在以後訪問。

該應用程序將如下所示:

如果您還沒有,請使用以下命令創建一個新的 Next.js 應用:

$ npx create-next-app weather-app

我們可以通過以下方式啟動我們的應用程序:

$ npm run dev

為簡單起見,我們將使用 Bootstrap 來設置應用程序的界面,而不是編寫自定義 CSS。您可以使用以下命令安裝 Bootstrap:

$ npm install bootstrap

安裝完成後,我們打開pages/_app.js 並包含一個 Bootstrap 條目:

import "bootstrap/dist/css/bootstrap.css";
import "../styles/globals.css";

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

export default MyApp;

注意: _app.js 文件是默認的App Next.js 用於初始化頁面的組件。它是所有頁面組件的起點。

現在,我們可以通過更改默認字體並添加漂亮的背景顏色來使我們的應用程序更具視覺吸引力。讓我們打開 styles/global.css 並進行以下更改:

@import url('https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:[email protected];200;300;400;500;800;900&display=swap');

body {
  background: #4F32FF;
  color: #fff;
  font-family: 'Be Vietnam Pro', sans-serif;
}

這已經足夠開始了!讓我們為通過 API 獲取的數據定義頁面結構和占位符。

頁面標記

對於我們的前端,讓我們打開 pages/index.js 並定義我們主頁的標記(結構):

import Link from "next/link";

export default function Home() {
  return (
    <div>
      <div
        className="d-flex justify-content-center align-items-center"
        style={{ minHeight: "100vh" }}
      >
        <div>
          <div>
            <h1 className="fw-bolder" style={{ fontSize: "60px" }}>
              Null City.
            </h1>
            13 January, 2022
          </div>
          <div className="d-flex justify-content-between align-items-center mt-4">
            <div className="pe-5">
              <h2 className="d-inline">0</h2>
              <sup>°C</sup>
              <p className="text-info">Cloudy</p>
            </div>
            <div>
              <img src="/1.png" alt="" width={100} draggable="false" />
            </div>
          </div>
          <hr />
          <div className="d-md-flex justify-content-between align-items-center mt-4">
            <button className="btn btn-success border-0 save-btn px-4 py-3">
             Timestamp
            </button>
            <Link href="/history">
              <button className="btn btn-danger border-0 history-btn px-4 py-3 ms-auto">
                My History
              </button>
            </Link>
          </div>
        </div>
      </div>
    </div>
  );
}

注意: 您需要從我們的 GitHub 下載天氣圖標,並將其包含在您的項目 /public 中 文件夾。

而且,此時,如果我們在瀏覽器中預覽我們的應用程序,我們應該會看到以下帶有虛擬數據的輸出:

獲取天氣信息

我們將使用一個免費的天氣 API 來獲取用戶當前的天氣信息,但是因為我們想要顯示用戶當前所在城市的天氣信息,我們需要使用另一個 API 來獲取用戶的當前城市並傳遞這個天氣 API 的參數以獲取所需的信息。

下圖描述了這個過程

為了獲取天氣信息,我們將使用 OpenWeather API ,雖然他們提供免費計劃,但您需要創建一個帳戶才能獲取 API 密鑰。

為了檢索用戶所在的城市,我們將使用免費的 IP Geolocation API 不需要使用 API 密鑰。

另外,我們要確保在頁面加載後立即顯示天氣信息,所以 Next.js getServerSideProps() 這裡確實派上用場了!

現在,讓我們將以下導出添加到 index.js 執行上述所有功能:

export async function getServerSideProps() {
  const ipRequest = await fetch(`http://ip-api.com/json/`);
  const ipData = await ipRequest.json();
  const city = ipData.regionName;

  const api_key = 'YOUR_OPEN-WEATHER_API_KEY';
  const url = `http://api.openweathermap.org/data/2.5/weather?q=${city},&appid=${api_key}&units=metric`;
  const weatherRequest = await fetch(url);
  const weatherInfo = await weatherRequest.json();

  console.log(weatherInfo);
  return { props: { weatherInfo, city } };
}

上面的代碼執行了兩個異步操作:

  • 首先是檢索用戶所在的城市,我們將其存儲在一個名為 city 的變量中 .
  • 第二個是向天氣 API 發送請求。

最後,我們將天氣 API 返回的結果以及作為道具的城市傳遞給我們的索引頁面。

注意: 您需要替換 YOUR_OPEN-WEATHER_API_KEY 使用您自己的 OpenWeather API 密鑰。

所需信息現在作為我們的索引頁面的道具存儲在 weatherInfo 中 和 city ,我們可以通過以下方式訪問它們:

...
export default function Home({ weatherInfo, city }) {
...
}

如果您嘗試記錄 weatherInfo 到控制台,您會注意到返回了很多信息,包括用戶的坐標和一些我們的應用程序不需要的其他信息。根據我們的應用設計,我們只需要以下數據:

  • 用戶所在城市
  • 當前溫度
  • 天氣說明(例如多雲、小雨、下雪等)

最後,一個基於當前溫度的天氣圖標。當前溫度返回 weatherInfo.main.temp ,以及 weatherInfo.weather[0].description 處的天氣描述 .

所以,讓我們繼續用這些信息替換我們標記中的虛擬數據:

{/* ... */}
<div>
  <h1 className="fw-bolder" style={{fontsize: "60px"}}>
    {city}
  </h1>
  13 January, 2022
</div>
<div className="d-flex justify-content-between align-items-center mt-4">
  <div className="pe-5">
    <h2 className="d-inline">
        {Math.round(weatherInfo.main.temp)}</h2>
    <sup>°C</sup>
    <p className="text-info text-capitalize">
        {weatherInfo.weather[0].description}
    </p>
  </div>
  <div><img src='/1.png' alt="" width={100} draggable="false" /></div>
</div>
{/* ... */}

我們還可以使用 OpenWeather API 根據當前溫度獲取天氣圖標,只需將圖標名稱作為參數傳遞即可,幸運的是,這也可以在 $weatherInfo.weather[0].icon 獲得 .

所以,讓我們繼續替換圖標的 <img> 使用以下代碼標記:

{/* ... */}
<img
  src={`http://openweathermap.org/img/wn/${weatherInfo.weather[0].icon}@2x.png`}
/>
{/* ... */}

而且我們的應用程序應該可以完全運行,根據我們當前所在的城市顯示當前的天氣信息:

本地保存數據

現在,讓我們創建一個函數來保存當前的天氣信息,以及它存儲在瀏覽器的 localStorage 中的日期和時間 .每個條目將被保存為具有以下結構的對象:

{
  date: 'Current Date',
  time: 'Current Time',
  city: 'User\'s City',
  temperature: 'User\'s city temperature',
  description: 'Weather Description',
};

為此,請創建一個新函數 saveWeather() (仍在我們的 index.js 中 文件),代碼如下:

  const saveWeather = () => {
    const date = new Date();

    let data = {
      date: `${date.getDate()} ${date.getMonth() + 1} ${date.getFullYear()}`,
      time: date.toLocaleTimeString(),
      city: city,
      temperature: weatherInfo.main.temp,
      description: weatherInfo.weather[0].description,
    };

    let previousData = localStorage.getItem('weatherHistory');
    previousData = JSON.parse(previousData);
    if (previousData === null) {
      previousData = [];
    }
    previousData.push(data);
    localStorage.setItem('weatherHistory', JSON.stringify(previousData));
    alert('Weather saved successfully');
  };

上面的代碼將解析之前存儲在 localStorage.weatherHistory 中的所有數據 作為 JSON 並根據返回的數據類型,我們將新條目推送到數組中,將此數組轉換為字符串,並在 localStorage.weatherHistory 中恢復它 .我們需要這樣做,因為 localStorage 只能存儲字符串,不能存儲任何其他數據類型。

當然,我們希望在用戶點擊 Timestamp 時調用這個函數 按鈕,所以讓我們添加一個 onClick 按鈕的屬性:

<button onClick={saveWeather}>Timestamp</button>

天氣歷史頁面

最後,我們需要創建一個專用頁面來訪問保存在瀏覽器 localStorage 中的所有天氣信息 .

注意: 我們將無法使用 Next.js 數據獲取函數,因為 localStorage 或者任何其他文檔對像在服務器上不可用,因此我們將不得不依賴客戶端數據獲取。

創建一個新的 history.js pages 下的文件 目錄內容如下:

import { useState, useEffect } from "react";

const History = ({}) => {
  const [weatherHistory, setweatherHistory] = useState([]);


  useEffect(() => {
    setweatherHistory(
      localStorage.weatherHistory !== undefined
        ? JSON.parse(localStorage.weatherHistory)
        : []
    );
  }, []);

  return (
    <div
      className="d-flex justify-content-center align-items-center p-3"
      style={{ minHeight: "100vh" }}
    >
      <div>
        {" "}
        <h2>My Weather History</h2>
        <div className="mt-5">
          {weatherHistory.length > 0 ? (
            weatherHistory.map((weather, index) => {
              return (
                <div
                  key={index}
                  className="card mb-3"
                  style={{ width: "450px" }}
                >
                  <div className="card-body text-dark">
                    <h5 className="card-title ">
                      {weather.city} - {weather.date}
                    </h5>
                    <small>{weather.time}</small>
                    <hr />
                    <p className="card-text">
                      <span className="font-weight-bold">Temperature: </span>
                      {weather.temperature}
                      <sup>°C</sup>
                    </p>
                    <p className="card-text">
                      <span className="font-weight-bold">Condition: </span>
                      {weather.description}
                    </p>
                  </div>
                </div>
              );
            })
          ) : (
            <p>Nothing to see here - yet</p>
          )}
        </div>
      </div>
    </div>
  );
};

export default History;

上面的代碼檢查是否 localStorage.weatherHistory 存在,如果存在 - 我們解析數據並將其設置為新變量 weatherHistory .如果不是,我們已經將此變量設置為一個空數組。

在我們的標記中,我們檢查 weatherHistory 中是否至少有一個數據條目 數組,並使用 JavaScript 的 .map() 函數,我們遍歷 weatherHistory 中的所有項目 ,將它們顯示在我們的網頁中。

讓我們繼續點擊時間戳 索引頁面上的按鈕記錄當前天氣信息,當您返回曆史頁面時,您應該會看到如下內容:

結論

Next.js 是一個 JavaScript 框架,專為增強和促進高性能 React 應用程序的開發而設計。

在本指南中,我們介紹了該庫的相關功能 - 如何通過 Next.js 的文件路由系統創建和路由頁面 ,如何<Link> 組件的工作原理,什麼是預取和預渲染以及如何利用它來增強用戶體驗,如何輕鬆創建 API 路由和請求處理程序以及如何使用環境變量。

最重要的是 - 我們構建了一個天氣應用程序,它與外部 API 通信以獲取數據並將其顯示給最終用戶,允許他們將任何給定的時間戳保存到他們的本地存儲中。

同樣,該應用程序的完整源代碼可在 GitHub 上獲得。


Tutorial JavaScript 教程
  1. 第 1 部分 - 我現在用於我的網頁設計項目的設計方法 - 咖啡店網站

  2. 使用 CRUD 操作的簡單 JavaScipt 項目。

  3. Power Surge – 使用 Firefox 開發者版優化這款 HTML5 遊戲中的 JavaScript

  4. My WebDev Notes:一個簡單的 TO-DO 列表應用程序

  5. 我將如何移動帶有元素的 SVG 模式

  6. 你應該在 2021 年學習 Java 嗎?

  7. 異步和承諾函數在我的 res.json 調用中不顯示任何內容

  1. JavaScript 生成器:卓越的 async/await

  2. 在 Web 應用程序中隱藏 iPhone 狀態欄?

  3. JavaScript 數字轉 unicode |示例代碼

  4. 帶有 CSS 的浮動標籤

  5. 你知道如何修改正則表達式以允許字符串開頭有一個“-”字符

  6. 使用 Angular 10 和 Tailwind 創建應用程序

  7. 多次轉義“或”

  1. 具有拖放功能的響應式 React 文件上傳組件

  2. 無服務器上的通用應用程序?是的。

  3. 如何添加暗模式以響應上下文和掛鉤

  4. 如何使用 Nuxt 創建實時聊天應用程序