自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

React 19 要來了!究竟帶來了哪些實用的特性呢?

開發(fā) 前端
React 團隊正在緊鑼密鼓地籌備,即將推出備受期待的 v19 版本。那么,React v19 究竟帶來了哪些實用的特性呢?接下來,讓我們一起來探索吧!

自 2022 年 React v18.2 版本發(fā)布后,React 社區(qū)一直翹首以盼新版本的到來。好消息是,React 團隊正在緊鑼密鼓地籌備,即將推出備受期待的 v19 版本。那么,React v19 究竟帶來了哪些實用的特性呢?接下來,讓我們一起來探索吧!

React v19 新功能概覽

以下是 React 19 即將帶來的新功能的簡要概述:

  • React編譯器:React 團隊正在實現(xiàn)一個新的編譯器。目前,Instagram 已經(jīng)在使用這項技術(shù),它將在未來的React版本中發(fā)布。
  • 服務端組件:經(jīng)過多年的開發(fā),React引入了服務端組件的概念?,F(xiàn)在可以在Next.js中使用這個功能。
  • Actions:Actions 將徹底改變我們與 DOM 元素的交互方式。
  • 文檔元數(shù)據(jù):使開發(fā)者能夠用更少的代碼完成更多工作。
  • 資源加載:此功能將啟用在后臺加載資源,從而改善應用的加載時間和用戶體驗。
  • Web Components:React 代碼現(xiàn)在將能夠集成 Web Components。
  • 增強的 Hooks:全新 Hook 即將出現(xiàn),有望徹底改變我們的編碼體驗。

React 19 旨在解決 React 長期以來的一個挑戰(zhàn):過度重渲染的問題。以前,開發(fā)人員依賴于 useMemo()、useCallback()、memo等技術(shù)來管理重渲染。新版本將自動處理過度重渲染問題,徹底解放開發(fā)者雙手,讓代碼更簡潔、高效。

React 編譯器

目前,React不會自動在狀態(tài)改變時重新渲染,這通常需要開發(fā)者手動優(yōu)化,如使用useMemo()、useCallback()和memo API。然而,React團隊認為這是一種“合理的手動折衷”,并非長久之計,他們的目標是讓 React 自行管理這些重渲染過程。

為此,他們創(chuàng)建了“React 編譯器”,它將自動決策如何、何時更新狀態(tài)和用戶界面,從而徹底解放開發(fā)者的雙手。這也意味著開發(fā)者不再需要手動使用上述優(yōu)化工具。雖然該功能尚未全面發(fā)布,但已率先在Instagram等生產(chǎn)環(huán)境中得到應用,效果顯著。

React 團隊這一創(chuàng)新舉措,不僅簡化了開發(fā)流程,也進一步提升了React的性能和穩(wěn)定性,令人期待其在未來版本中的全面應用。

服務端組件

如果你尚未聽聞服務器端組件,那你可能錯過了React和Next.js領(lǐng)域的一大革新。過去,React組件主要運行于客戶端,但如今React正引領(lǐng)一場變革——在服務端運行組件。

服務端組件的概念已流傳多年,而 Next.js 則率先將其應用于生產(chǎn)環(huán)境。從Next.js 13開始,所有組件默認都是服務器端組件,若想讓組件在客戶端運行,只需使用“use client”指令。

在即將發(fā)布的React 19中,服務器端組件將直接融入 React 核心,帶來多重優(yōu)勢:

  • SEO優(yōu)化:服務端渲染的組件能向網(wǎng)絡爬蟲提供更豐富的內(nèi)容,進而提升搜索引擎排名。
  • 性能飛躍:服務器=端組件能顯著加快頁面初始加載速度,優(yōu)化整體性能,對內(nèi)容密集型應用尤為有效。
  • 服務器端執(zhí)行:服務器=端組件使得代碼在服務器端執(zhí)行成為可能,從而高效處理如API調(diào)用等任務。

在React中,所有組件默認是客戶端組件,若需轉(zhuǎn)為服務器端組件,只需在組件頂部添加“use server”即可。這樣,組件將僅在服務器端運行,不會涉及客戶端。

使用服務端組件非常簡單。可以在任何 React 組件中導入服務端組件,并通過“Actions”來執(zhí)行特定任務。

'use server';

export default async function requestUsername(formData) {
  const username = formData.get('username');
  if (canRequest(username)) {
    // ...
    return 'successful';
  }
  return 'failed';
}

Actions

在 React 19 中,另一個激動人心的新功能就是Actions。這將是我們在處理表單時工作方式的一個重大改變。

Actions 允許開發(fā)者將動作與HTML的<form/>標簽無縫融合。簡言之,現(xiàn)在可以直接使用Actions替代傳統(tǒng)的onSubmit事件。這些動作被巧妙地設(shè)計成了HTML表單的屬性,讓表單處理更為靈活和高效。

在Actions之前,我們常依賴于 React 的onSubmit事件來處理表單提交,觸發(fā)如搜索等方法的執(zhí)行。然而,這樣的處理方式通常受限于客戶端,無法在服務端直接執(zhí)行相關(guān)邏輯。

<form onSubmit={search}>  
  <input name="query" />  
  <button type="submit">Search</button>  
</form>

但在 Actions 推出后,結(jié)合服務端組件的使用,我們可以輕松地在服務端執(zhí)行表單提交動作。在JSX中,不再需要繁瑣的onSubmit事件,只需在<form/>標簽中使用action屬性即可。這個屬性的值將指向一個方法,負責處理數(shù)據(jù)的客戶端或服務端提交。

更值得一提的是,Actions 不僅支持同步操作,還能輕松應對異步任務,從而極大地簡化了數(shù)據(jù)提交管理和狀態(tài)更新的流程。React 的目標是通過這一創(chuàng)新功能,讓表單處理和數(shù)據(jù)管理變得更加簡單、直觀。

下面來通過一個具體示例來深入了解 Actions 是如何工作的:

"use server"

const submitData = async (userData) => {
  const newUser = {
    username: userData.get('username'),
    email: userData.get('email')
  }
  console.log(newUser)
}
const Form = () => {
    return <form action={submitData}>
        <div>
            <label>Name</label>
            <input type="text" name='username'/>
        </div>
        <div>
            <label>Name</label>
            <input type="text" name="email" />
        </div>
        <button type='submit'>Submit</button>
    </form>
}

export default Form;

在上述代碼中,submitData是服務端組件中的動作。form是一個客戶端組件,它使用submitData作為動作。submitData將在服務端執(zhí)行??蛻舳耍╢orm)和服務端(submitData)組件之間的通信之所以成為可能,正是得益于action屬性。

Web Components

Web Components 賦予我們能力,使用原生HTML、CSS和JavaScript創(chuàng)建自定義組件,并輕松地將它們?nèi)谌隬eb應用中,如同操作原生HTML標簽般自然流暢。

然而,目前將 Web Components 集成到 React 框架中并非易事。我們通常需要將 Web Components 轉(zhuǎn)化為React組件,或者安裝額外的庫并編寫額外代碼來使它們與 React 兼容,這無疑增加了開發(fā)的復雜度。

但好消息是,React 19 將極大地簡化這一過程。未來,當你發(fā)現(xiàn)某個實用的 Web Components,如輪播圖組件時,可以輕松地在React項目中引入它,無需繁瑣的轉(zhuǎn)換工作。

這一改進將大大提升開發(fā)效率,使我們能夠充分利用現(xiàn)有龐大的 Web Components 生態(tài),為React應用增添更多可能性。

盡管目前我們尚未得知具體的實現(xiàn)細節(jié),但我期待著它可能帶來的便捷性——或許,我們只需簡單地將 Web Components 導入 React 項目,就像模塊聯(lián)邦那樣。

文檔元數(shù)據(jù)

諸如“標題”、“元標簽”和“描述”等元素在優(yōu)化搜索引擎優(yōu)化(SEO)和確??稍L問性方面起著至關(guān)重要的作用。在React中,由于單頁面應用的普及,跨不同路由管理這些元素可能會有點麻煩。

目前,開發(fā)者通常不得不編寫自定義代碼,或者使用像react-helmet這樣的包來處理路由變更并相應更新元數(shù)據(jù)。這個過程可能是重復的,并且容易出錯,尤其是在處理像元標簽這樣的SEO敏感元素時。

之前的方式:

import React, { useEffect } from 'react';  
  
const HeadDocument = ({ title }) => {  
  useEffect(() => {  
    document.title = title;  
  
    const metaDescriptionTag = document.querySelector('meta[name="description"]');  
    if (metaDescriptionTag) {  
      metaDescriptionTag.setAttribute('content', '新描述');  
    }  
  }, [title]);  
  
  return null;  
};  
  
export default HeadDocument;

在上面的代碼中,有一個名為HeadDocument的組件,它負責根據(jù)傳入的屬性更新標題和元標簽。我們在useEffect中執(zhí)行這些更新。同時,使用JavaScript來更新標題和元標簽。這個組件將在路由更改時更新。這不是一個干凈的處理方式。

在React 19中,可以直接在React組件中使用標題和元標簽:

const HomePage = () => {  
  return (  
    <>  
      <title>博客</title>  
      <meta name="description" content="博客" />  
      {/* 頁面內(nèi)容 */}  
    </>  
  );  
}

這在React之前是不可能的。之前唯一的辦法是使用像react-helmet這樣的包。React 19的新特性將大大簡化元數(shù)據(jù)的管理,使開發(fā)者能夠直接在組件內(nèi)部聲明和更新這些元素,從而提高代碼的可讀性和可維護性。

資源加載

在 React 應用中,有效管理資源加載和性能至關(guān)重要,特別是針對圖片和其他資源文件。

通常,瀏覽器會先渲染視圖,然后再加載樣式表、字體和圖片。這可能會導致從非樣式化(或未樣式化內(nèi)容的閃爍)到樣式化視圖的閃爍。為了緩解這個問題,開發(fā)者通常會添加自定義代碼來檢測這些資源何時準備好,確保只在所有內(nèi)容加載完成后才顯示視圖。

在 React 19 中,隨著用戶瀏覽當前頁面,圖片和其他文件將在后臺加載。這一改進將有助于減少頁面加載時間,降低等待時間。此外,React 19 引入了資源加載的生命周期 Suspense ,包括腳本、樣式表和字體等。這一新特性允許React精確判斷何時內(nèi)容已準備完畢,可以安全展示給用戶,從而徹底消除了因資源未加載而導致的頁面閃爍問題。同時,React 19 還會提供preload和preinit等新的資源加載API,讓開發(fā)者對資源何時加載和初始化擁有更精細的控制權(quán)。

通過實現(xiàn)資源的后臺異步加載,React 19 極大地減少了用戶的等待時間,讓他們能夠流暢地與頁面內(nèi)容進行交互。這一重大改進不僅提升了React應用的性能,也為用戶帶來了更加流暢、愉悅的瀏覽體驗。

增強的 Hooks

use

use 是一個實驗性 React Hook,它可以讓讀取類似于 Promise 或 context 的資源的值。

const value = use(resource);

官方文檔https://zh-hans.react.dev/reference/react/use。

use(Promise)

新的 use hook 可以在客戶端進行“掛起”的 API??梢詫⒁粋€ promise 傳遞給它,React 將會在該 promise 解決之前進行掛起。它的基本語法如下:

import { use } from 'react';

function MessageComponent({ messagePromise }) {
    const message = use(messagePromise);
    // ...
}

下面來看一個簡單的例子:

import * as React from 'react';
import { useState, use, Suspense } from 'react';
import { faker } from '@faker-js/faker';

export const App = () => {
  const [newsPromise, setNewsPromise] = useState(() => fetchNews());

  const handleUpdate = () => {
    fetchNews().then((news) => {
      setNewsPromise(Promise.resolve(news));
    });
  };

  return (
    <>
      <h3>
        新聞列表
    		<button onClick={handleUpdate}>刷新</button>
      </h3>
      <NewsContainer newsPromise={newsPromise} />
    </>
  );
};

let news = [...new Array(4)].map(() => faker.lorem.sentence());

const fetchNews = () =>
  new Promise<string[]>((resolve) =>
    // 使用 setTimeout 模擬數(shù)據(jù)獲取
    setTimeout(() => {
      // 每次刷新時添加一個標題
      news.unshift(faker.lorem.sentence());
      resolve(news);
    }, 1000)
  );

const NewsContainer = ({ newsPromise }) => (
  <Suspense fallback={<p>請求中...</p>}>
    <News newsPromise={newsPromise} />
  </Suspense>
);

const News = ({ newsPromise }) => {
  const news = use<string[]>(newsPromise);
  return (
    <ul>
      {news.map((title, index) => (
        <li key={index}>{title}</li>
      ))}
    </ul>
  );
};

在上面的例子中,每次刷新時,都會先顯示“請求中...”,請求到數(shù)據(jù)后進行展示:

官方文檔中,關(guān)于 <Suspense> 有一個警告:

目前尚不支持在不使用固定框架的情況下進行啟用 Suspense 的數(shù)據(jù)獲取。實現(xiàn)支持 Suspense 數(shù)據(jù)源的要求是不穩(wěn)定的,也沒有文檔。React 將在未來的版本中發(fā)布官方 API,用于與 Suspense 集成數(shù)據(jù)源。

對于 React 19 來說,use 可能就是用于與 Suspense 集成數(shù)據(jù)源的官方 API。

這個全新的use hook 與其他的 React Hooks 不同,它可以在循環(huán)和條件語句中像 if 一樣被調(diào)用。這意味著我們可能不再需要依賴像 TanStack Query 這樣的第三方庫在客戶端進行數(shù)據(jù)獲取。然而,這仍需進一步觀察,因為 Tanstack Query 的功能遠不止解析 Promise 這么簡單。

use(Context)

這個 use hook 也可以用來讀取 React Context。它與 useContext 作用完全相同,只是可以在循環(huán)(如 for)和條件語句(如 if)中調(diào)用。

import { use } from 'react';

function HorizontalRule({ show }) {
    if (show) {
        const theme = use(ThemeContext);
        return <hr className={theme} />;
    }
    return false;
}

這將簡化某些場景下的組件層級結(jié)構(gòu),因為在循環(huán)或條件語句中讀取 context,之前唯一的方法就是將組件一分為二。

在性能方面,這一改進也是巨大的進步,因為現(xiàn)在即使 context 發(fā)生變化,我們也可以有條件地跳過組件的重新渲染。

useOptimistic

useOptimistic Hook 允許在進行提交動作的同時,能夠樂觀地更新用戶界面,提升用戶體驗。其語法如下:

import { useOptimistic } from 'react';

function AppContainer() {
    const [optimisticState, addOptimistic] = useOptimistic(
        state,
        // 更新函數(shù)
        (currentState, optimisticValue) => {
            // 合并并返回帶有樂觀值的新狀態(tài)  
        },
    );
}

樂觀更新:一種更新應用程序中數(shù)據(jù)的策略。這種策略通常會先更改前端頁面,然后向服務器發(fā)送請求,如果請求成功,則結(jié)束操作;如果請求失敗,則頁面回滾到先前狀態(tài)。這種做法可以防止新舊數(shù)據(jù)之間的跳轉(zhuǎn)或閃爍,提供更快的用戶體驗。

下面來看一個添加購物車的例子:

import { useState, useOptimistic } from 'react';

const AddToCartForm = ({ id, title, addToCart, optimisticAddToCart }) => {
  const formAction = async (formData) => {
    optimisticAddToCart({ id, title });
    try {
      await addToCart(formData, title);
    } catch (e) {
      // 捕獲錯誤
    }
  };

  return (
    <form action={formAction}>
      <h2>{title}</h2>
      <input type="hidden" name="itemID" value={id} />
      <button type="submit">添加到購物車</button>
    </form>
  );
};

type Item = {
  id: string;
  title: string;
};

const Cart = ({ cart }: { cart: Item[] }) => {
  if (cart.length == 0) {
    return null;
  }
  return (
    <>
      購物車:
      <ul>
        {cart.map((item, index) => (
          <li key={index}>{item.title}</li>
        ))}
      </ul>
      <hr />
    </>
  );
};

export const App = () => {
  const [cart, setCart] = useState<Item[]>([]);

  const [optimisticCart, optimisticAddToCart] = useOptimistic<Item[], Item>(
    cart,
    (state, item) => [...state, item]
  );

  const addToCart = async (formData: FormData, title) => {
    const id = String(formData.get('itemID'));
    await new Promise((resolve) => setTimeout(resolve, 1000));
    setCart((cart: Item[]) => [...cart, { id, title }]);

    return { id };
  };

  return (
    <>
      <Cart cart={optimisticCart} />
      <AddToCartForm
        id="1"
        title="JavaScript權(quán)威指南"
        addToCart={addToCart}
        optimisticAddToCart={optimisticAddToCart}
      />
      <AddToCartForm
        id="2"
        title="JavaScript高級程序設(shè)計"
        addToCart={addToCart}
        optimisticAddToCart={optimisticAddToCart}
      />
    </>
  );
};

在上面的例子中,將商品添到購物車時,會先在購物車列表看到剛剛添加的商品,而不必等到數(shù)據(jù)請求完成。這樣,用戶可以更快地看到更新后的購物車內(nèi)容,提供更加流暢的用戶體驗。

在介紹 useFormState 之前,先來看以下這個 Hook 使用的背景。

React 將引入一個新組件:<form>,它是創(chuàng)建用于提交信息的交互式控件,可以將一個函數(shù)作為action的屬性值。當用戶提交表單時,React 將自動調(diào)用此函數(shù),以執(zhí)行相應的操作。

<form action={handleSubmit} />

注意,如果在 React 18 中添加<form action>屬性,就會收到警告:

?? Warning: Invalid value for prop action on  tag. Either remove it from the element or pass a string or number value to keep it in the DOM.

這里的意思是,<form>標簽上的 prop action無效。要么從元素中刪除它,要么傳遞一個字符串或數(shù)字值以將其保留在 DOM 中。

而在新版本中,可以直接在<form>標簽上設(shè)置action屬性。例如,在上面的購物車例子中,:

const AddToCartForm = ({ id, title, addToCart }) => {
  const formAction = async (formData) => {
    try {
      await addToCart(formData, title);
    } catch (e) {
      // 捕獲錯誤
    }
  };

  return (
    <form action={formAction}>
      <h2>{title}</h2>
      <input type="hidden" name="itemID" value={id} />
      <button type="submit">添加到購物車</button>
    </form>
  );
};

addToCart 函數(shù)并不是在服務器端執(zhí)行的,而是在客戶端(例如用戶的瀏覽器)上運行的。這個函數(shù)可以是一個異步函數(shù),如網(wǎng)絡請求,而不阻止其他代碼的執(zhí)行。通過使用addToCart函數(shù),開發(fā)者可以更簡單地處理React中的AJAX表單,例如在搜索表單中。然而,這可能還不足以完全擺脫像 React Hook Form 這樣的第三方庫,因為它們不僅處理表單提交,還包括驗證、副作用等多種功能。

看完這個新功能,下面就來看看這一部分要介紹的新 Hook:useFormState。

useFormState

useFormState 是一個可以根據(jù)某個表單動作的結(jié)果更新 state 的 Hook。

const [state, formAction] = useFormState(fn, initialState);

只有在表單提交觸發(fā) action 后才會被更新的值,如果該表單沒有被提交,該值會保持傳入的初始值不變。

例如,這可以用來顯示由表單操作返回的確認消息或錯誤消息。

import { useState } from 'react';
import { useFormState } from 'react-dom';

const AddToCartForm = ({ id, title, addToCart }) => {
  const addToCartAction = async (prevState, formData) => {
    try {
      await addToCart(formData, title);
      return '添加成功';
    } catch (e) {
      return "添加失?。嘿u完啦";
    }
  };

  const [message, formAction] = useFormState(addToCartAction, null);

  return (
    <form action={formAction}>
      <h2>{title}</h2>
      <input type="hidden" name="itemID" value={id} />
      <button type="submit">添加到購物車</button> 
      {message}
    </form>
  );
};

type Item = {
  id: string;
  title: string;
};

export const App = () => {
  const [cart, setCart] = useState<Item[]>([]);

  const addToCart = async (formData: FormData, title) => {
    const id = String(formData.get('itemID'));
    await new Promise((resolve) => setTimeout(resolve, 1000));
    if (id === '1') {
      setCart((cart: Item[]) => [...cart, { id, title }]);
    } else {
      throw new Error('Unavailable');
    }

    return { id };
  };

  return (
    <>
      <AddToCartForm
        id="1"
        title="JavaScript權(quán)威指南"
        addToCart={addToCart}
      />
      <AddToCartForm
        id="2"
        title="JavaScript高級程序設(shè)計"
        addToCart={addToCart}
      />
    </>
  );
};

效果如下:

注意useFormState需要從react-dom中導入,而不是從react中導入。

useFormStatus

useFormStatus 用于獲取上次表單提交的狀態(tài)信息。

const { pending, data, method, action } = useFormStatus();

它不接收任何參數(shù),會返回一個包含以下屬性的 status 對象:

  • pending:布爾值。如果為 true,則表示父級 <form> 正在等待提交;否則為 false。
  • data:包含父級 <form> 正在提交的數(shù)據(jù);如果沒有進行提交或沒有父級 <form>,它將為 null。
  • method:字符串,可以是 'get' 或 'post'。表示父級 <form> 使用 GET 或 POST HTTP 方法 進行提交。默認情況下,<form> 將使用 GET 方法,并可以通過 method 屬性指定。
  • action:一個傳遞給父級 <form> 的 action 屬性的函數(shù)引用。如果沒有父級 <form>,則該屬性為 null。如果在 action 屬性上提供了 URI 值,或者未指定 action 屬性,status.action 將為 null。

下面來繼續(xù)看購物車的例子,將商品添加到購物車成功前,禁用添加按鈕:

import { useState } from 'react';
import { useFormStatus } from 'react-dom';

const AddToCartForm = ({ id, title, addToCart }) => {
  const formAction = async (formData) => {
    try {
      await addToCart(formData, title);
    } catch (e) {
      // 捕獲錯誤
    }
  };

  return (
    <form action={formAction}>
      <h2>{title}</h2>
      <input type="hidden" name="itemID" value={id} />
      <SubmitButton />
    </form>
  );
};

const SubmitButton = () => {
  const { pending } = useFormStatus();
  return (
    <button disabled={pending} type="submit">
      添加到購物車
    </button>
  );
};

type Item = {
  id: string;
  title: string;
};

const Cart = ({ cart }: { cart: Item[] }) => {
  if (cart.length == 0) {
    return null;
  }
  return (
    <>
      購物車:
      <ul>
        {cart.map((item, index) => (
          <li key={index}>{item.title}</li>
        ))}
      </ul>
      <hr />
    </>
  );
};

export const App = () => {
  const [cart, setCart] = useState<Item[]>([]);

  const addToCart = async (formData: FormData, title) => {
    const id = String(formData.get('itemID'));
    await new Promise((resolve) => setTimeout(resolve, 1000));
    setCart((cart: Item[]) => [...cart, { id, title }]);

    return { id };
  };

  return (
    <>
      <Cart cart={cart} />
      <AddToCartForm
        id="1"
        title="JavaScript權(quán)威指南"
        addToCart={addToCart}
      />
      <AddToCartForm
        id="2"
        title="JavaScript高級程序設(shè)計"
        addToCart={addToCart}
      />
    </>
  );
};

添加購物車時效果如下:

注意useFormState需要從react-dom中導入,而不是從react中導入。此外,它僅在父級表單使用 action 屬性時才有效。

責任編輯:姜華 來源: 前端充電寶
相關(guān)推薦

2024-04-28 09:01:06

React 19更新前端

2024-05-08 08:50:39

React19模式UI

2021-01-28 16:58:12

數(shù)字貨幣加密貨幣區(qū)塊鏈

2024-12-06 08:00:51

2012-05-03 15:14:38

Firefox 13Beta

2020-03-09 18:34:50

ServerlessDevOps前端

2015-01-04 10:11:32

VMwareWorkstation

2010-04-01 09:03:31

RHEL 5.5

2020-05-28 13:10:27

PHP開發(fā)編程

2017-04-17 09:01:39

科技新聞早報

2023-05-29 08:38:56

popover控制懸浮層

2017-03-22 11:59:40

深度神經(jīng)網(wǎng)絡

2009-01-20 13:03:42

服務器虛擬化

2015-10-26 14:56:23

Ignite Chin微軟技術(shù)大會

2022-07-21 10:51:39

物聯(lián)網(wǎng)碎片化物聯(lián)網(wǎng)安全物聯(lián)網(wǎng)

2024-01-02 14:21:33

2012-10-30 09:24:27

2025-02-07 09:09:13

2023-11-26 18:13:07

iOS 18蘋果

2009-03-28 09:39:25

Windows 7微軟操作系統(tǒng)
點贊
收藏

51CTO技術(shù)棧公眾號