顯示具有 REACT 標籤的文章。 顯示所有文章
顯示具有 REACT 標籤的文章。 顯示所有文章

2025/05/09

還在 React 從入門到放棄?告訴你選擇比努力還重要的事

 作者:吳祐賓

 

 


 

 

用 Vite,別再用 CRA

 

2025年02月,React 官方公告,建議使用 Vite,所以別再用 CRA (create-react-app)

看了許多 React 的入門資料,還有很多古典資料,大多還是從 create-react-app 專案起始,用久了覺得很不舒服

 

NPM 編譯經常無預期出錯 


NPM 編譯經常無預期出錯,學習 React 免不了會經過一連串試錯的過程

但往往會不經意的出現許多編譯錯誤,例如在安裝某 UI 套件時,就遇到依賴套件版本衝突的問題,錯誤訊息洋洋灑灑一大篇,

指向的卻是 Webpack 內部模組的錯誤,對於當時的我來說,根本無從下手

在無法排除錯誤的情形下,最常做的就是重建 node_modules 內容,次數一多

是在學習 React 還是背誦 NPM i 指令,我已經搞不清楚了

 

 

在設計時期,前端和後端 API 網域位置不一致時

 

只要不是靜態網頁,在前端的開發過程一定會和後端 API 傳遞訊息,尤其是開發網頁應用程式更是必須

在設計時期,只要前端和後端的 Web 服務網域不同,就一定會遇上【跨來源資源共用(Cross-Origin Resource Sharing (CORS))】這個大魔王

後端就必須進行 CORS 設定,但實際上線又會是在同一網域,只有開發時期才會遇到的 CORS 顯得是非常詭異的歷程。

 

 

你以為要從 JavaScript ES6 開始學習,事實上

 

包含 React 官方網站,很多學習 React 的資料顯示,JavaScript ES6 是必須先學會的基礎,我認為這是造成 React 陡峭的學習曲線的罪魁禍首

許多社團新手遇到的問題大多是編譯 React 時產生的異常狀態,NPM 會回傳非常完整的【長篇報告】,也因為內容非常的多

若直接複製貼上,只會造成有心要幫忙的人在理解前還必須先把整篇報告看過一輪,除錯到心累。 



React 三大核心


React 是水很深的函式庫之一,技術中心思想在【狀態的傳遞】,基於中心所延伸出來的有三大核心:

 

  • React : 屬性、狀態、事件、元件和元件狀態互動等。
  • Router : 頁面到頁面間狀態的互動
  • Redux : 全域狀態管理

 

React 的問題大多就是狀態問題,Router 和 Redux 都是 React 官方沒做但是推薦的解決方案,都是學習 React 過程中必須學習的技能。

我歸納學習 React 學習資源後,發現從 create-react-app 幾乎就是給新人從入門到放棄的好專案,而我認為學習 React 路線有二:

  • 拿掉 NPM 編譯,純粹的 React
  • 擁抱編譯,從 Vite 開始


使用 esm.sh。拿掉 NPM 編譯,純粹的 React

 

 使用 esm.sh,就可以先專注在 React 的學習,esm.sh 也支援 JSX,範例如下:

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!doctype html>
<html>
<head>
<script type="importmap">
{
  "imports": {
    "react": "https://esm.sh/react@19.1.0",
    "react-dom/client": "https://esm.sh/react-dom@19.1.0/client"   
  }
}
</script>
<script type="module" src="https://esm.sh/tsx"></script>
</head>
<body>
  <div id="root"></div>
<script type="text/babel">
import React from "react";
import { createRoot } from 'react-dom/client';

const rootElement = document.getElementById("root");

const App = <h1>Hello, Eden</h1>;

const root = createRoot(rootElement);
root.render(App);

</script>
</body>
</html>

 

 <script type="importmap"> ... </script>

後面程式碼要使用 React 的時候,瀏覽器就知道要去 esm.sh 下載

 

 <script type="module" src="https://esm.sh/tsx"></script>

 esm.sh 讓瀏覽器看懂 JSX 語法

 

 

擁抱編譯,從 Vite 開始

 



在 npm 下,使用 vite cli 指令建立 vite react 專案


npm create vite@latest


接著按精靈指引,依序填入專案名稱、目錄名稱等內容


完成後,進入指定目錄就可以使用 npm 啟動 vite 建立的 React 網站,並開始學習 React 入門

 

  cd Eden-vite-project
  npm install
  npm run dev





2025/02/21

React 19 use API 避坑指南:常見錯誤與解決方案 (含 RCC 說明)

作者:吳祐賓 

 

 


 

 

 

React 應用程式開發過程中,很常需要去 Server 端拉資料回來,通常我們會使用 useState, useEffect 來完成工作。

 

底下是一個簡單的範例,從 JSONPlaceholder (著名的公開偽資料 API 服務)提供的 API 取得使用者資料。

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import React, { useState, useEffect } from 'https://esm.sh/react@18'; // Changed to React 18 for compatibility with useEffect/useState
import ReactDOMClient from 'https://esm.sh/react-dom@18/client'; // Changed to React 18

const Users = () => {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null); // Added error handling

  useEffect(() => {
    const fetchUsers = async () => {
      const res = await fetch('https://jsonplaceholder.typicode.com/users');
      const data = await res.json();
      setUsers(data);
      setLoading(false);
    };

    fetchUsers();
  }, []); // Empty dependency array ensures this runs only once on mount

  if (loading) {
    return <h1>Loading...</h1>;
  }

  if (error) {
    return <h1>Error: {error.message}</h1>; // Display the error message
  }

  return (
    <ul>
      {users.map((user) => (
        <div key={user.id}>
          <h2>{user.name}</h2>
        </div>
      ))}
    </ul>
  );
};


function App() {
  return (
    // Suspense is not needed with useEffect + useState for initial data loading
    <Users />
  );
}

const root = ReactDOMClient.createRoot(document.getElementById('root'));
root.render(<App />);

 

 

useEffect + fetch 坊間有很多教學,因為很多人會遇到 "Warning"

 

這個設計法已經行之有年,還是有很多人搞不清楚 useEffect + fetch 為什麼會出現類似 "Warning: useEffect function must return a cleanup function or nothing. Promises..." 的訊息。

 

解決方式很多,React 19 也提出了一個解決方式。

 

 

 use API

 

use API 是 React 19 提出的新概念,是搭配 React Server Components 框架,如 Next.js 等設計使用。

 

use 的設計目標是:

 

  • 簡化 Server Components 中的資料取得設計
  • 更簡潔的程式碼和更高的可讀性
  • 錯誤處理更方便 (Error Boundaries 整合)

 

 

直接將 React 18 程式碼升級到 React 19,參考 How to fetch an API with REACT 19 | use - suspense 文章,程式碼如下:

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import React, { Suspense, use } from 'https://esm.sh/react@19';
import ReactDOMClient from 'https://esm.sh/react-dom@19/client';
const fetchUsers = async () => {
  const res = await 
  fetch('https://jsonplaceholder.typicode.com/users');
  return res.json();
};


const Users = () => {
  const users = use(fetchUsers());

  return (
    <ul>
      {users.map((user) => (
        <div key={user.id}>
          <h2>{user.name}</h2>
        </div>
      ))}
    </ul>
  );
};


function App() {
  return (
    <Suspense fallback={<h1>Loading...</h1>}>
      <Users />
    </Suspense>
  );
}

const root = ReactDOMClient.createRoot(document.getElementById('root'));
root.render(<App />);

 

 

程式碼就是如此簡單,但 React Client Components 時會無限 Promise

 

React Stand-alone 就是 RCC 模式,在這個模式下使用 use API,就會出現如下圖般無限 Promise 的情形。

 

因為 use 是搭配 RSC 的設計,所以和 render 會有關係,RSC 框架會管理好 use 裡 Promise 的狀態,但 RCC 沒有框架處理 Promise 狀態,流程上是:

 

User render -> use(new Promise) <in use> -> do Promise -> retrun New Promise <in use> -> "use" call User render... repeat 

 

 


 

 

 RCC 的 Promise 管理快取機制自己寫,就為了 use API

 

這部份和 useCallback 有點像,只有 callback 相依的變數修改了才會更新。Promise 也可以如法泡製。參考 New React 19 use hook–deep dive 的教學,寫了 useQuery 來模擬 Server 端對 Promise 快取機制的處理。

 

程式碼修改如下。

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import React, { Suspense, use, useState } from 'https://esm.sh/react@19';
import ReactDOMClient from 'https://esm.sh/react-dom@19/client';

/** useQuery Hook */
const promiseCache = new Map();

const useQuery = ({ fn, key }) => {
  if (!promiseCache.has(key)) {
    promiseCache.set(key, fn());
  }

  const promise = promiseCache.get(key);

  const result = use(promise);

  return result;
};
/* end useQuery Hook */

const fetchUsers = () => {
  const res = fetch('https://jsonplaceholder.typicode.com/users')
    .then( response => response.json());
  return res;
};

const Users = () => {
  const users = useQuery({fn: ()=> fetchUsers(), key: "fetchUsers"});

  return (
    <ul>
      {users.map((user) => (
        <div key={user.id}>
          <h2>{user.name}</h2>
        </div>
      ))}
    </ul>
  );
};


function App() {
  return (
    <Suspense fallback={<h1>Loading...</h1>}>
      <Users />
    </Suspense>
  );
}

const root = ReactDOMClient.createRoot(document.getElementById('root'));
root.render(<App />);

 

 

 

重點總結:


傳統方式 (React 18 及之前): 使用 useState 和 useEffect 組合來處理資料獲取、載入狀態和錯誤處理。  這是常見且有效的方法,但程式碼相對較多。



React 19 的 use API:


  • 設計目標: 簡化資料獲取,特別是在 Server Components (RSC) 環境下,提供更簡潔的程式碼和內建的錯誤處理。
  • RSC 環境: use hook 旨在與 React Server Components 框架 (如 Next.js) 搭配使用。 框架會負責管理 Promise 的狀態,避免無限迴圈。
  • RCC (React Client Components) 環境限制: 在獨立的 React Client Components (沒有 RSC 框架) 中直接使用 use 會導致無限迴圈,因為沒有機制來管理 Promise 的狀態。


模擬 RSC 的 Promise 快取 (RCC 環境):

 

  • 為了在 RCC 環境下也能體驗 use 的簡潔性,可以自行建立一個 Promise 快取機制 (例如範例中的 useQuery hook)。
  • useQuery hook 使用 Map 來儲存 Promise,確保相同的請求只會發送一次,避免重複的非同步操作。


 

總之,use API 是 React 19 中一個強大的新特性,但目前主要適用於 Server Components 環境。對於傳統的 Client Components,就繼續使用 useEffect 和 useState,對使用新技術有興趣的人,可以自行實作 Promise 快取機制來模擬 use 的行為。 

 

如果你正在使用 Next.js 這樣的 RSC 框架,use API 可以大幅簡化你的資料 request 的程式碼。

 

和你分享。

 

See Also

 

2025/02/19

React 19 + esm.sh CDN 新手入門:快速掌握 React 19 最新功能

作者:吳祐賓

 

 


 

 

前言


React 19 帶來了許多令人興奮的新功能,例如 Actions, Server Components, Asset Loading 等,可以幫助我們提升開發效率和應用程式效能。

 

本教學將以新手友善的方式,帶領大家快速入門 React 19,並搭配 esm.sh CDN 快速搭建開發環境,讓你立即體驗 React 最新功能!


附帶說明,本教學只會提及 Client-Side Rendering (CSR) 功能,如果這篇文章迴響不錯,會再另外推出 Server-Side Rendering (SSR) 的教學內容,敬請期待!


準備工作


要開始 React 19 開發,最快速的方式就是使用 esm.sh CDN。它讓我們無需安裝 Node.js 和 npm,就能在瀏覽器中直接使用 React 19。細節可以參閱我之前寫的:我在 React 19 新手入門:CDN + esm.sh 快速上手

 

首先,建立一個 HTML 檔案 (例如 index.html),並加入以下程式碼:

 

<!doctype html>
<html>
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <script type="importmap">
    {
      "imports": {
        "react": "https://esm.sh/react@19",
        "react-dom/client": "https://esm.sh/react-dom@19/client"
      }
    }
  </script>
  <script type="module" src="https://esm.sh/tsx"></script>
  <title>React App with JSX React 19</title>
</head>
<body>
<div id="root"></div>

<script type="text/babel">
  import { createRoot } from "react-dom/client"  

  function App(){
    return <>
      <h1>Hello, React 19 with ESM.sh</h1>
    </>    
  }

  createRoot(root).render(<App />)
</script>
</body>
</html>

 

這個簡單的 HTML 檔案就建立了一個 React 19 開發環境。esm.sh 使用 `esm.sh/tsx`,用來啟動 JSX 即時編譯的能力。在開發模式 (development mode)下非常好用。

 

我還是習慣使用 CDN 做教學範例。現在,你可以直接用瀏覽器打開 index.html,看到 "Hello, React 19 with ESM.sh!" 的訊息,就代表成功了!

 

 

React 19 新功能巡禮

 

接下來,我們將逐一介紹 React 19 的重點新功能,讓你快速掌握 React 最新技術。

Actions:簡化資料流程與狀態管理


在 React 18 之前,處理表單提交或資料更新時,我們需要手動管理 loading 狀態和錯誤處理。

 

React 19 引入了 Actions (useTranstition),可以更簡潔地處理表單的非同步操作。

 

簡單的說,React 19 Actions 比較接近資料庫的交易概念,我們拿官方的 18/19 的程式碼來比較,並附上我修改後的程式碼。

 


 

 

<!doctype html> <html> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> <title>React App with JSX React 19</title> </head> <body class="dx-viewport"> <div id="root"></div> <script type="text/babel" data-type="module"> import React, {useState, useTransition} from "https://esm.sh/react@19" import ReactDOMClient from "https://esm.sh/react-dom@19/client" // Using pending state from Actions function UpdateName({}) { const [name, setName] = useState(""); const [error, setError] = useState(null); const [isPending, startTransition] = useTransition(); async function updateName(name) { // 模擬異步操作,例如 API 呼叫 return new Promise((resolve) => { setTimeout(() => { if (!name) { resolve("Name cannot be empty."); // 模擬錯誤情況 } else if (name.length > 20) { resolve("Name is too long."); // 模擬另一個錯誤情況 } else { // 在此處添加更新名稱的實際邏輯,例如 API 呼叫 console.log("Name updated to:", name); // 模擬成功情況 resolve(null); // 成功時返回 null 表示沒有錯誤 } }, 1000); // 模擬 1 秒的延遲 }); } const handleSubmit = () => { startTransition(async () => { const error = await updateName(name); if (error) { setError(error); return; } setError("Ok!"); }) }; return ( <div> <input value={name} onChange={(event) => setName(event.target.value)} /> <button onClick={handleSubmit} disabled={isPending}> Update </button> {error && <p>{error}</p>} </div> ); } function App(){ return <> <h1>Hello, React 19 with ESM.sh</h1> <UpdateName /> </> } const root = ReactDOMClient.createRoot(document.getElementById('root')) root.render(<App />) </script> </body> </html>

 

 

使用 form 和 useActionState,使程式更簡單

 

前面的例子是使用 Button 直接送出 request 進行狀態切換。React 19 對 form 也加入了一些 Action。修改後程式碼如下。可以看到 useActionState 內可以自動切換狀態,並且和錯誤機制配合的冾到好處。

 

<!doctype html>
<html>
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <title>React App with JSX React 19</title>
</head>
<body class="dx-viewport">
<div id="root"></div>

<script type="text/babel" data-type="module">
  import React, {useState, useActionState} from "https://esm.sh/react@19"
  import ReactDOMClient from "https://esm.sh/react-dom@19/client" 


  async function updateName(name) {
      // 模擬異步操作,例如 API 呼叫
      return new Promise((resolve) => {
        setTimeout(() => {
          if (!name) {
            resolve("Name cannot be empty."); // 模擬錯誤情況
          } else if (name.length > 20) {
            resolve("Name is too long."); // 模擬另一個錯誤情況
          }
          else {
            // 在此處添加更新名稱的實際邏輯,例如 API 呼叫
            console.log("Name updated to:", name); // 模擬成功情況
            resolve(null); // 成功時返回 null 表示沒有錯誤
          }
        }, 1000); // 模擬 1 秒的延遲
      });
    }


    // Using <form> Actions and useActionState
    function ChangeName({ name, setName }) {
      const [error, submitAction, isPending] = useActionState(
      async (previousState, formData) => {
        const error = await updateName(formData.get("name"));
        if (error) {
          return error;
        }
        return null;
      },
      null,
    );

    return (
      <form action={submitAction}>
        <input type="text" name="name" />
        <button type="submit" disabled={isPending}>Update</button>
        {error && <p>{error}</p>}
      </form>
    );
  }

  function App(){
    return <>
      <h1>Hello, React 19 with ESM.sh</h1>
      <ChangeName />
    </>    
  }

  const root = ReactDOMClient.createRoot(document.getElementById('root'))
  root.render(<App />)
</script>
</body>
</html>

 

 

New API:  use - 讓 Async Function 回傳 Promise 更融入 React 世界


在 React 19 之前,我們在 Component 裡處理 Async Function 的回傳值時,通常需要 useState, useEffect 搭配 async/await 才能比較好的處理 loading, error, data 狀態。

 

這在 fetch 資料庫經常會希望有但都要自己來刻的功能。


React 19 引入了 use 這個新的 Hook,讓你在 React Component 裡可以直接 "await" Promise,讓 Async Function 的回傳值可以更自然的融入 React 的世界。

我們來看一個簡單的例子。

 

// New API: use function fetchData() { console.log('Fetching data...'); return new Promise(resolve => { setTimeout(() => { console.log('Data fetched!'); resolve({ message: "Data from Async Function!" }); }, 1500); // 模擬 1.5 秒的 API 延遲 }); } function DataDisplay() { // 直接 use(Promise) const data = use(fetchData()); return ( <div> <p>Data Display:</p> {data ? <p>{data.message}</p> : <p>Loading data...</p>} </div> ); }

 

在 DataDisplay 這個 Component 裡,我們定義了一個 fetchData 的 Async Function,這個 Function 模擬了一個 API 呼叫,會在 1.5 秒後 resolve 一個包含 message 的 Object。

 

重點在 DataDisplay Component 裡,我們可以直接使用 use(fetchData()),use Hook 會處理 fetchData() 回傳的 Promise

 

Pending 狀態: 在 Promise resolve 之前,Component 會進入 Pending 狀態 (Suspense),你可以看到畫面上顯示 "Loading data..."。
    成功狀態: 當 Promise resolve 後,use(fetchData()) 會回傳 Promise 的 resolve 值,也就是 { message: "Data from Async Function!" },Component 重新 render,畫面就會顯示 "Data from Async Function!"。

 

use 的優點:


  • 更簡潔的 Async Function 處理: 不需要再手動管理 loading 狀態,程式碼更簡潔易讀。
  • 提升開發體驗: 讓 Async Function 更自然的融入 React Component 的開發流程。
  • 搭配 Suspense: 可以和 Suspense Component 搭配使用,讓 Loading 體驗更流暢。


 

Ref as a prop - Function Component 也可以直接接收 Ref 了!


在 React 19 之前,Function Component  如果需要接收 Ref,必須要透過 forwardRef 這個 API 包裝才能使用。


// React 18 寫法
const MyInput = React.forwardRef((props, ref) => {
  return <input placeholder={props.placeholder} ref={ref} />;
})

 

// React 19 寫法 - Function Component 直接接收 ref prop
function MyInput({placeholder, ref}) {
  return <input placeholder={placeholder} ref={ref} />
}

 

React 18 寫法對於 Function Component 來說,多了一層 forwardRef 的包裝,語法上比較囉嗦一點,也讓 Function Component 的程式碼看起來比較複雜。

 

React 19  簡化了 Function Component  Ref 的使用方式。現在,Function Component  可以像 Class Component 一樣,直接接收 ref 這個 Prop 了!

 

未來 forwardRef 也將棄用,請盡早學會 ref as a prop。

 

實際案例會像這樣:

 

// Ref as a prop
  function MyInput({placeholder, ref}) {
    return <input placeholder={placeholder} ref={ref} />
  }

  function RefInputComponent() {
    const inputRef = useRef(null);

    useEffect(() => {
      if (inputRef.current) {
        console.log('Input Ref:', inputRef.current);
      }
    }, []);

    return (
      <div>
        <p>Ref as a prop:</p>
        <MyInput placeholder="Enter text" ref={inputRef} />
      </div>
    );
  }

 

總結

 

好的,React 19 這次更新真的太棒啦!一口氣帶來了 Actions, use Hook, ref as a prop 這些超實用的新功能,每一個都打中開發者的痛點,讓開發 React 應用程式變得更輕鬆、更高效。

 

總結一下 React 19 這些必學新功能:

  • Actions:  告別複雜的表單狀態管理!useTransition 和 useActionState 就像是神隊友,幫你優雅地處理非同步操作, loading 狀態、錯誤處理都變得超簡單,程式碼也更簡潔易讀,真的就像資料庫交易一樣方便!


  • use Hook:  Async Function 的救星!以前在 Component 裡處理 Promise,總是要 useState, useEffect 寫一堆,現在有了 use,直接 await 就搞定,程式碼瞬間清爽!資料載入 loading 狀態也自動處理,開發體驗直接起飛!


  • ref as a prop:  Function Component 的 Ref 也太方便了吧!再也不用 forwardRef 包裝了,直接像 Class Component 一樣接收 ref prop 就行,語法更直覺,程式碼也更簡潔,而且聽說 forwardRef 以後要被棄用,這個一定要學起來!


 

React 19 這些新功能,不只是錦上添花,更是實實在在地提升了開發效率和體驗。無論你是 React 新手還是老手,都非常建議趕快升級到 React 19,體驗這些新功能帶來的魅力!

 

看完這篇入門教學,是不是覺得 React 19 其實也沒那麼難?趕快動手試試看,用 esm.sh CDN 快速搭建你的 React 19 開發環境

 

一起擁抱 React 新時代吧!  相信你會愛上 React 19 的!
 

 

 

See also

 


2025/02/18

React 19 新手入門:CDN + esm.sh 快速上手

作者:吳祐賓

 

 


 

 

 

自學習 React 以來,一直很嚮往使用 CDN 來寫 React,因為習慣 jQuery 那種不需要建構環境就能開發應用程式的模式 (No-build development),總認為網頁就該輕鬆寫才是。(當然,複雜邏輯怎樣都不會輕鬆寫就是)

 

轉眼間 React 已經釋出 19 版,多了許多很新奇的東西,最有印象的是:

 

  • 整合 Form 表單的優化 (Action, useActionState 等) 
  • "use" API,可以進行非同步的渲染處理

 

 

 而在 React 19 Upgrade Guide 裡,有段內容吸引到我的注意:

 

UMD builds removed

UMD was widely used in the past as a convenient way to load React without a build step. Now, there are modern alternatives for loading modules as scripts in HTML documents. Starting with React 19, React will no longer produce UMD builds to reduce the complexity of its testing and release process.

To load React 19 with a script tag, we recommend using an ESM-based CDN such as esm.sh.

 


 

這要追朔到 React 誕生年份:2013。當時 JavaScript 模型化規則還沒有定案,所以使用 UMD (Universal Module Definition) ,來讓程式碼可以在各種不同的環境下都能順利執行。

 

隨著 IE6 走入歷史,ESM (ECMAScript Modules) 已成為現代 JavaScript 的官方模組標準。瀏覽器和 Node.js 都已經原生支援 ESM。React 團隊認為使用 ESM 除了能減少維護 UMD 的能量外,也可以提高程式的開發效率。

 

 

回顧使用 CDN 建構 React 18 的 Hello World App

 

 

<!doctype html>
<html>
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <title>React App without JSX in React 18</title>
</head>
<body>
<div id="root"></div>
<!-- script 寫在後面才會在 root 後面執行,或是使用 window.onload 就可以將此段放在 head 標籤內 -->
<script>
  const e = React.createElement;

  const root = ReactDOM.createRoot(document.getElementById('root'));
  root.render(e('div', null, 'Hello React App without JSX in React 18'));
</script>
</body>
</html>

 

 

使用 CDN 建構 React 19 的 Hello World App

 

因為 React19 不再開發 UMD,取而代之的是 ESM,esm.sh CDN 站點有支援 esm 即時載入及編譯的功能,又可以支援 modules 寫法,使用上更加接近本機環境建構內容。

 

<!doctype html>
<html>
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React App without JSX React 19</title>
</head>
<body>
<div id="root"></div>
<script type="module">
  import React from "https://esm.sh/react@19/?dev"
  import ReactDOMClient from "https://esm.sh/react-dom@19/client/?dev"
  
  const e = React.createElement;

  const root = ReactDOMClient.createRoot(document.getElementById('root'));
  root.render(e('div', null, 'Hello React App without JSX in React 19'));
</script>
</body>
</html>

 

 

esm.sh CDN 提供即時模組匯入,但模組來源在哪裡?

 

esm.sh 的原理是即時編譯模組,而模組的來源便是來自 npmjs 倉庫,以 React 來說,我們可以到 npmjs 網站查詢 React。網址:

 

https://www.npmjs.com/package/react

 

 

如下圖所示,目前的版本為 19.0.0,使用 React 19 Upgrade Guide 裡的範例即可使用。

 


 

 


總結:React 19 CDN 方式在新手入門學習上更加方便與平滑

 

從上述的程式碼可以得知 React 19 CDS 開發上很貼近本地建構環境 (例如 React + Webpack) 。

 

以往 UMD 只能使用 React 三本柱 (React, Router, Redux),要使用 npm 套件難度極高,例如 DevExtreme 這類套件就不能使用。

 

而 ESM 開發方式不僅是現代開發主流,更讓這些三方套件在 CDN 下使用成為可能。

 

我認為也能讓 React 新手在適應 ESM CDN 後能更平滑過渡到本地建構環境。

 

實際玩過 React 19 後覺得能夠在 CDN 環境下享受 NPM 豐富的套件,讓 CDN 模式延展性更好,更適合學習、小規模測試和原型(Prototype)開發。

 

下一篇會加入 JSX,來看看 React 19 新規則能否和 Babel 結合,來體驗 React 19 更多的可能。

 

和你分享

 

 

See also

 

 

 

2024/03/04

WinForm 設計師的 Web 開發秘訣:DevExtreme 教學

 

作者:吳祐賓

 


許多 WinForm 設計者都被傳統開發方式所制約,要在短時間內寫出符合桌面、平板、手機通用的畫面實在是非常困難。坊間有許多解決方案,以後端 (Back-End) 設計為主的有:ASP.NET 的 Web Form;Delphi 的 IntraWeb、UniGUI 等他們都是以傳統 WinForm 設計概念所發展的框架,佈局和 UI 元件都已經配置好,設計者只要會操作即可。

 

後端設計很好,但前端仍然要碰

 

後端玩過一陣後,還是覺得以後端為主的 Web 應用程式很沉重,不論是設計還是操作上都是。除此之外,前後端混雜交錯的網頁 (MVC 裡的 V 最明顯) 複雜的程度很高,能接手的大多已是專家級,能交付任務的人選並不多。WinForm 轉 Web 的設計師往往是蠟燭兩頭燒,有沒有更好的作法?

 

前端框架學完才發現只有骨架

 

WinForm 設計者已經習慣邏輯設計,全心學習前端後,我的情況是發現:三大框架 Angular、VUE、React 都只有設計框架,佈局、元件還得另學 HTML、CSS。後台要的格式就那樣,每次都要手刻也太辛苦。而且前端 CSS 佈局難度其實比邏輯更加困難,如果有人可以把 UI 調整一致就能讓我專注在邏輯上了。

 

入門?就從 DevExtreme 開始

 

Bootstrap、EasyUI 都有推 Augular, VUE, React 元件,佈局也沒問題,然而 DevExtreme 還提供了命令列工具,新增頁面、調整配色都可以使用 DevExtreme-CLI 命令列工具完成,這種感覺和操作 Visual Studio Code 的 File > New File 快沒什麼兩樣了。但在這之前請先安裝好 DevExtreme-CLI,如果還沒安裝,請先參閱:【翻譯】建立 DevExtreme React 應用程式 

 

以 DevExtreme + React 為例,建立 Web 應用程式專案

 

使用如下的 DevExtreme-CLI 指令建立 my-hello-world 專案,並以預設值:TypeScript、Outer toolbar Layout 作為開始。建立完成後進入該目錄。

 

devextreme new react-app my-hello-world
cd my-hello-world



建立 HelloWorld 分頁

專案剛建立會有幾張頁面,以方便我們參考。在使用 Visual Studio Code 開啟目錄之前,我們先使用 DevExtreme-CLI 建立一張新頁面,使用指令:

 

devextreme add view HelloWorld

 

接下來啟動 Visual Studio Code, 開啟 my-hello-world 目錄,可以在 Pages 路徑看到剛才建立的 HelloWorld 資料夾,裡面有 HelloWorld.cssHelloWorld.tsx 檔案,副檔名 tsx 表示為 支援 TypeScript 的設計檔案。畫面應如下顯示:

 


主菜 -- 建立 Button

 

接在 import 區的最末句,輸入:


import { Button } from 'devextreme-react';

 

把 "Put your content here" 內容置換為 Button 標籤,並設定按下後會跳出 Hello World 的提示訊息:

 

<Button text='Eden Button' onClick={()=>alert("Hello World!")}></Button>

 

此時程式畫面應如下所示:

 


啟動 Hello World 服務,來看成果吧!

 

開啟 Visual Studio Code 的終端機頁面,輸入:

 

npm run start

 

啟動網站後等它一段時間,系統會自動開啟瀏覽器,並開啟我們的 my-hello-world 網站。接下來點擊選單中的 HelloWorld 項目就會看到剛剛我們所建立的頁面,裡面會看到一個按鈕,按下後就會看到 Hello World 的提示訊息。此時畫面效果應該如下所示:


總結

 

習慣 WinForm 體貼的 IDE 工具開發者在切換到指令模式可能剛開始還不習慣。把上述的指令多敲幾次後就會習慣使用 DevExtreme-CLI 的開發模式。

和完全從無到有的鍵盤輸入,DevExtreme-CLI 縮短了很多重複建立環境的時間。下一回我會介紹選單內容和標題的修改的教學。

 

和你分享

 

See also

 

2024/01/19

如何在 React 18 使用 React Router v6.4 建立路由,超簡單

作者:吳祐賓





React Router 目的在實現"客戶端路由" (Client Site Routing)。官方文件是這麼介紹的:

在傳統網站(Server Site),瀏覽器從網站服務端下載文件,解析 CSS 和 JavaScript 資源,之後呈現服務端所要顯示的 HTML 內容。當使用者點擊網頁內的超連結時,它會在頁面(同一個或新頁面)上重新啟動此流程。

客戶端路由可以實現在您的頁面透過點擊超連結更新 URL 時,不需要再從網站服務端再次下載另一個文件。您的頁面可以立即顯示一些新的 UI,並使用 fetch 實現 AJAX,以新資訊來刷新頁面。來讓使用者有更好的操作體驗

React Router 和 React Redux 幾乎是 React 開發者必須會學會的函式庫,本篇文章會逐步帶你邊做邊學如何在 React 18 使用 React Router v6 建立路由,超簡單


 

前置作業:安裝 pnpm 管理 npm 套件

因為 pnpm 官方是這麼說的:

使用 npm 時,若您有 100 件專案都使用了同個依附套件,磁碟中就會有 100 份該套件的副本。 反之,有了 pnpm,該依附套件會被儲存在一個由內容定址的儲存區,因此:

    如果您同時需要該依附套件的不同版本,只有存在差異的檔案才會被加入儲存區。 例如,如果此依賴套件有 100 個檔案,而新的 版本僅變更其中一個檔案,則 pnpm update 將只會新增一個 新的檔案到儲存庫中,而不會為了單一檔案的變更而複製整個依賴套件。
    所有依賴套件的檔案將被儲存在磁碟中的單一位置。 當依賴套件被 安裝時,依賴套件的檔案會被硬鏈結至該位置,不會消耗 額外的磁碟空間。 這將允許您在不同專案之間共享相同版本的依賴套件。

由於上述原因,您將節省大量的磁碟空間,這將與您的 專案和依賴套件的數量成正比,並且將大幅提升安裝的速度!

所以我現在也改用 pnpm 來管理 npm 套件:

npm install -g pnpm



Step 1. 建立新的 React 應用程式

使用 pnpm 建立 React 應用程式:

pnpm create vite my-react-router --template react


移動到建立的目錄中

cd my-react-router


安裝專案所需套件

pnpm install




Step 2. 建立呈現的頁面

在資料夾 src 中建立 pages 資料夾

接著在資料夾 src/pages 建立 Home.jsx

const Home = () => <div>Home</div>
export default Home


接著在資料夾 src/pages 建立 Content.jsx

const Content = () => <div>Content</div>
export default Content


接著在資料夾 src/pages 建立 About.jsx

const About = () => <div>About Me</div>
export default About



Step 3. 安裝本次主角: React Router npm 套件

使用官方教學的安裝指令進行安裝:

pnpm install react-router-dom localforage match-sorter sort-by

 

Step 4. 註冊 Router

這裡註冊 Router 的意思是指建立一個路由分配器,建立時也一併新建路徑及對應頁面,打開 src/main.jsx,修改後文件應該會如下表所示。



src/main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
import {
  createBrowserRouter,
  RouterProvider,
} from "react-router-dom";
import Home from "./pages/Home"
import Content from "./pages/Content"
import About from "./pages/About"

const router = createBrowserRouter([
  {
    path: "/",
    element: <App />,
    children: [
      {
        path: "Home",
        element: <Home />,
      },
      {
        path: "content",
        element: <Content />,
      },
      {
        path: "about",
        element: <About />,
      },
    ],
  },
]);

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>,
)


因為我計畫在 Root Layout 裡切換分頁,所以在 root 裡加入 children 內容。



Step 5. 製作導覽功能

開啟 src/App.jsx ,並加入導覽介面,將以下程式碼置換原模板內容

 

src/App.jsx
import './App.css'
import { Routes, Route, NavLink, Outlet } from "react-router-dom";
import Home from './pages/Home'
import Content from './pages/Content';
import About from './pages/About';

function Layout() {
  return (
    <>
      <header>
        <h1>My Super Cool App</h1>
        <NavLink to="/">首頁</NavLink>
        <NavLink to="/content">內容</NavLink>
        <NavLink to="/about">關於</NavLink>
      </header>
      <main>
        <Outlet />
      </main>
      <footer>©️ Eden 2024</footer>
    </>
  );
}

function App() {
  return (
    <>
      <Routes>
        <Route element={<Layout />}>
          <Route path="/" element={<Home />} />
          <Route path="/content" element={<Content />} />
          <Route path="/about" element={<About />} />
        </Route>
      </Routes>
    </>
  )
}

export default App


在同一頁進行切換頁面的場合下,會需要在 App 裡增加 Layout 層級,並使用  Router Outlet  來顯示  Route  內容。


Step 6. 啟動瀏覽器執行 React 應用程式

使用以下指令啟動 Web 服務:

pnpm run dev

 

按照預設值,您可以透過以下網址進入 React 應用程式

http://localhost:5173/

 

畫面應該長成這樣:


總結

本篇介紹 pnpm 套件管理器的好處,以及 Vite 建立 React 應用程式的方法,和 React Router v6.4 的使用。在 Router v6.4 中,由於添加  Remix Data APIs,所以原作者開始推薦使用 RouterProvider 來替代舊版 <BrowserRouter> 的使用,所以這篇文章的寫法可能會和你之前所學過的不太一樣。目前 BrowserRouter 仍然可以相容使用,不過身為前端工程師的我們,還是得接受前端技術會不斷進化的現實。學吧!一起加油!

和你分享


See also