.st0{fill:#FFFFFF;}

React 筆記 – 前端框架 

 2024-04-23

By  Mason

SPA

若我們有一個後端的網路服務 (Web Service) 已經能夠正常運作且受到保護,只有經過身份驗證和授權的用戶才能調用這個 API,則我們可以使用網頁前端的框架來架設網站,並且連結到此 API。

在大多數網站上,當我們點擊連結或提交表單時,瀏覽器會向伺服器發出 request 並下載一個完整的新頁面。我們通常會看到白色閃爍,因為當前頁面消失並加載了新頁面。若使用 AJAX 技術,我們可以編寫一些在瀏覽器上運行的 JavaScript。

JavaScript 將向伺服器發出 request,接收 response 並使用新數據更新當前 HTML 頁面。 整個過程中,只有數據通過網絡傳輸,而不是一個全新的 HTML 頁面。像這樣的頁面,就被稱為 Single Page Application(SPA)

SPA 優點:

1. 減少了伺服器的負載。為每個 reqeust 生成一個 HTML 頁面需要大量的處理能力。如果我們的服務器的 CPU太忙,我們的網站就會變慢,並可能變成反應遲鈍使我們的網站關閉。在使用網頁瀏覽器時,大多數客
戶端的 CPU (可能有 8 個或更多) 都處於空閒狀態。因此,我們可以將數據傳到客戶端,讓客戶端的 CPU 驅動瀏覽器來渲染 (render) 頁面

2. 減少了需要通過網絡傳輸的數據量,因為只發送新數據,而不是完整的 HTML 頁面。

SPA 缺點:

1. SPA 非常複雜。SPA 越大,添加的功能越多時,會變得越複雜。最終,複雜性會影響我們網站的性能,並增加 bug 出現的機率。

2. 搜索引擎優化(SEO) 會出問題。 谷歌和其他搜索引擎有自動掃描程式,但是,這些自動掃描程式不會運行 JS 代碼來加載數據。因此,搜索引擎可能無法正確定位我們的網站。

目前,市面上有幾個熱門框架可以製作 SPA,包括 React.js (由 Facebook 開發和使用)、Angular.js (由 Google 的 Angular 團隊以及社群共同領導 )和 Vue.js。

React

React (也稱為 React.js 或 ReactJS) 是一個免費和開源的前端 JavaScript 框架,用 UI 組件來架構使用者介面。它由 Meta (以前的 Facebook)和個人開發人員社區維護。 React 可用作於開發 SPA 網頁。React的基本原理是,用 JavaScript 來生成 HTML

* React Native 是一個 Facebook 研發的開放原始碼的應用程式框架。 React Native 開發的程式可用於 iOS 和 Android 手機平台。

* React 常與另一個框架 Next.js 合作使用。

* 因為 React 還是個相對年輕的框架(初始版本在2013的9月發布),所以功能上、語法上、套件上都會不斷更新。

使用 React 的好處:

1. 可重複使用的組件(Reusable Components) - Component React 的核心架構;使用 React 建構的每個應用程序的 UI,都可以分解為彼此獨立的小部分。這些小部分稱為 Components。每一個 Component 中的都有自己的程式邏輯,可以單獨編輯,然後在最終的 UI 中合併到一起,這使得創建應用程序 UI 的任務更簡單,更易於管理。 Component 也可以在其他頁面和應用程序上重複使用,從而節省大量的寫程式時間。

2. React 最有用的特性之一是,它能夠更改網站上的 Components,而無需更新整個 DOM。這是通過虛擬 DOM (virtual DOM) 完成的。虛擬 DOM是 DOM 的虛擬表示 (virtual representation) 或副本 (copy)。

每當用戶執行操作時,例如點擊按鈕,React 都會更新虛擬 DOM,將更新後的與之前的版本進行比較,檢測差異,然後只更新受影響的物件而不是刷新整個 DOM。這使得網站反應速度更快、性能更高。

3. JSX 代表 JavaScript XML,是 JavaScript 的語法擴展,它允許寫程式的人在 JavaScript 代碼中,嵌入類似HTML 語法的程式碼。React 的工作就是將 JSX 換成 DOM 元素。

Get Started with React

用指令 npx create-react-app app-name 就可以生成一個 React 的專案。(npx 代表 Node Package
Execution,是 npm 內建的功能。Npx 是一個 npm package 運行程序,可以從 npm 拿到 package 並且直接執行,甚至無需在電腦上安裝該 package)

創建 working directory (practice)之後,

cd practice

npm start

資料夾與檔案的基本用途如下:

 • public folder - 內部放置靜態文件,例如: index.html、JavaScript 文件、圖片、favicon.ico 和其他檔案等等。Public 資料夾內部的 index.html 文件非常重要。用 React 所製作的 Single Page Application 當中所使用的單一頁面就是這個 index.html 文件。

 • src folder - 是我們 React 應用程序的核心資料夾,包含Components、index.js、App.js 等等文件。

    index.js文件的功能是,將最主要的 React Component 渲染到在 index.html 當中 id 為 root 的標籤。

    App.js 文件的功能是,製作「App Component」。App Component 的功能是擔任其他所有 Component 的容器。因為 React 製作出的網站是一頁式的網頁,所以網頁內容會根據 URL 改變。根據不同的 URL 去做相對應的 route 是 App.js 的責任。

[VS code 中,手動切換 select language mode => Javascript JSX]

 • node_modules - 目錄包含所有 React 專案所依賴的 packages,例如:react、react-dom 等等。

 • .gitignore - 任何列出的文件或資料夾,都不會被 push上 GitHub。

 • package.json - 用來保存與 Project 相關的數據,用於管理 Project 的 dependencies、scripts、versions 等等。

 • README.md − 為其他開發人員提供了 GitHub 上的詳細描述。

React 專案環境設定 (VS code)

1. 將所有 js 檔名改為 javascript react 的檔案

manage => Settings => Open Settings (JSON):(增加以下設定程式碼)

"files.associations": {

    "*.js": "javascriptreact"

  }

2. 使用 prettier 來自動排版

manage => Settings => Open Settings (JSON):(增加以下設定程式碼)

"[javascriptreact]": {

    "editor.defaultFormatter": "esbenp.prettier-vscode"

  },

3. 快速製作出一個文件:

於 extension 裡面,安裝 VS Code ES7+ React/Redux/React-Native/JS snippets

指令 rafce

JSX 語法

JSX 的功能讓我們可以在 JavaScript 內部,使用類 HTML 的程式碼來製作 Component。(React 並不要求使用JSX,但大部分人覺得在 JavaScript 程式碼中撰寫使用者介面的同時,這是一個很好的視覺輔助)

由於網頁瀏覽器無法理解 JSX 語法,我們需要先做 JSX Transformation。在React Project 內部的node_modules 資料夾內,可以找到一個資料夾「babel」。Babel 是一個 JavaScript 編譯器,它可以將不是每個瀏覽器都可以理解的最新 JavaScript 功能轉換為當前和舊瀏覽器或環境中向後兼容的 JavaScript 版本。Babel 在React 的功能在於將 JSX 語法轉換成 React Components。

JSX 的特殊語法如下:

1. 我們可以在大括號 {} 內編寫 expression。在程式語言中,Statements 代表一個動作或是指令,例如打印出某個值或是 if statement。Expression 是會算出某個值的操作,例如一個變數、數學運算或是執行函數等等。基本的原則是,「An expression is something, while a statement does something.」在 JSX 當中使用 {} 可以執行 expression 本身,並且顯示 return value。

2. 在 JSX 當中,HTML 的標籤內,class 屬性都需要改叫做 className。這是因為 class 這個字在 JavaScript 內部是個保留字,所以不能直接寫class。

3. 在 JSX 內做 inline-styling 時,需要給 style 屬性一個 expression。這個 expression 內部需要放入一個物件,所以 inline-styling 的語法會變成 style={{}}。其中,外部的大括號是 JSX expression 語法,內部的大括號是 JavaScript 物件語法。此外,因為連字號 (Hyphen) 在 JavaScript 有特殊意義,所以不能在 JavaScript 物件的屬性使用連字號。因此,在 CSS 中具有連字號的屬性都會被換成 camelCase 的語法。例如:background-color 會需要被寫成 backgroudColor。

Props (properties)

在 React 當中,每個 Component 都可以有自己的屬性 (Props, properties)。Props 可以由 HTML 標籤的attributes 傳遞給 Component。例如:

<Friend name="Wilson" />

若是 Props 傳遞時,要使用變數,則需要將變數換成 expression:

let myName = "Wilson";
<Friend name={myName} />

Props 會透過 argument 的方式傳遞給 Component,所以 Component 使用 Props 的語法為:

const Friend = (props) => {
return (<div><h1>{props.name}</h1></div>);
};

或是,我們也可以用 object destructuring 的語法來取得 props 物件內的屬性:

const Friend = ({name}) => {
return (<div><h1>{name}</h1></div>);
};

app.js

import Nav from "./Nav";

import Info from "./Info";

function App() {

  let friends = [

    { name: "小明", age: "16" },

    { name: "小華", age: "17" },

    { name: "小張", age: "18" },

  ];

  return (

    <div>

      <Nav />

      {friends.map((friend) => (

        <Info name={friend.name} age={friend.age} />

      ))}

    </div>

  );

}

export default App;

 

Info.js (Component)

import React from "react";

import "./styles/style.css";

const Info = ({ name, age }) => {

  return (

    <div className="info">

      <h1>朋友名稱:{name}</h1>

      <h1>朋友年齡:{age}</h1>

    </div>

  );

};

export default Info;

 

事件處理

使用 React element 處理事件跟使用 DOM element 處理事件是十分相似的。它們有一些語法上的差異:

1. 事件的名稱在 React 中都是 camelCase,而在 HTML DOM 中則是小寫。

2. 事件的值在 JSX 中是一個 function (也是一個expression,所以我們需要用 {} 符號),而在 HTML DOM 中則是一個 string。 React事件處理時,會直接執行 expression 內的 function。

例如,在 HTML 當中對 <button> 監聽 click 事件時,語法為:

<button onclick="running()">hello</button>
<script> function running() {console.log("this is running..");}</script>

然而,在 JSX 當中的語法是:(先定義一個 function)

const running = () => {
  prompt("running...");
};

<button onClick={running}>hello</button>

若要在事件的 callback function 執行時加入參數,如果寫:

<button onClick={running(1)}>hello</button>

會造成 React 讀取程式碼時,直接執行了 running(1) 這個 function。

因此,我們可以把帶有參數的 running(1) 放入一個 arrow function expression內部,做成 button 的 onClick 事件的值:

<button onClick={() => {running(1);}}>hello</button>

app.js

import Nav from "./Nav";

import Info from "./Info";

function App() {

  let friends = [

    { name: "小明", age: "16" },

    { name: "小華", age: "17" },

    { name: "小張", age: "18" },

  ];

  const buttonHandler = () => {

    alert("你按了button一次。。。");

  };

  return (

    <div>

      <Nav />

      {friends.map((friend) => (

        <Info name={friend.name} age={friend.age} />

      ))}

    <button 

      onClick={() => {

        buttonHandler();

      }}

    >

      按我一下

    </button>

    <button onClick={buttonHandler}>按我一下</button> 此法最簡單

    </div>

  );

}

export default App;

 

State

使用 React 的其中一個好處在於,他能夠只更改網站上必須改變的 Components,而無需更新整個 DOM。 實現這個功能的工具是 State。

State 是透過 React Hooks 當中的 useState 來完成的。在 React 當中,State 是 Component 所持有的一個物件,此物件包含有關 Component 的數據或信息。 Component 的 State 是可以改變的。每當 Component 的 State 改變時,持有此 state 的所有 Components 都會全部重新渲染 (rerender)。React Components 在其props 或state 改變時,都會重新渲染!

*. Hooks 是 React 16.8 版本中引入的新功能。它允許您在不編寫 class 的情況下使用 State 和其他 React
的功能 (class 是 React 舊版本的常見語法)。 Hooks 在 class 內部無法起作用。我們可以理解為,Hooks
是從 function component 中「鉤入」React State 和生命週期特性的函數。

useState 的語法

const [name, setName] = useState(initialValue);

name 是 state 的名稱,我們可以隨意命名。
setName 是更新 state 時所使用的函數。
initialValue 是 name 這個 state 的初始值。

app.js

import Nav from "./Nav";

import Info from "./Info";

function App() {

  return (

    <div>

      <Nav />

      <Info />

    </div>

  );

}

export default App;

 

Info.js (Component)

import React, {useState} from "react";

import "./styles/style.css";

const Info = () => {

  let [name, setName] = useState("小明");

  let age = 20;

  const changeNameHandler = () => {

    setName("小明先生");

  };

  return (

    <div className="info">

      <h1>朋友名稱:{name}</h1>

      <h1>朋友年齡:{age}</h1>

      <button onClick={changeNameHandler}>改名按鈕</button>

    </div>

  );

};

export default Info;

 

State Lifing

我們有時會希望兩個 Component 之間可以共享某個 state。如果兩個 Component 屬於不同鏈同層級或是不同鏈不同層級,則我們需要將 state 往兩邊最近的 common ancestor (ancestor component) 的方向移動。這樣的做法就叫做「state lifting」

app.js

import React, { useState } from "react";

import Create from "./Create";

import Info from "./Info";


function App() {

  let [messages, setMessages] = useState([]);

  return (

    <div>

      <Create messages={messages} setMessages={setMessages} />

      <Info messages={messages} setMessages={setMessages} />

    </div>

  );

}

export default App;

 

Info.js (Component)

import React, {useState} from "react";

import "./styles/style.css";


const Info = ({ messages, setMessages }) => {

  return (

    <div className="info">

      {messages.map((message, index) => {

        return <p key={index}>學習內容是: {message}</p>;

      })}

    </div>

  );

};

export default Info;

 

Create.js (Component)

import React, { useState } from "react";


const Create = ({ messages, setMessages }) => {

  let [input, setInput] = useState("");

  const submitButtonHandler = (e) => {

    e.preventDefault();

    setMessages([...messages, input]); // spread operator

    setInput(""); // 清除輸入文字

  };

  const inputHandler = (e) => {

    setInput(e.target.value);

  };

  return (

    <form>

      <input onChange={inputHandler} value={input} type="text" />

      <button onClick={submitButtonHandler}>Submit</button>

    </form>

  );

};

export default Create;

useEffect

在程式語言裡,一個函數通常只會做兩件事:

1. return value - 計算或找出某個值,並且從函數內回傳出來。

2. side effect - 當函數做某事時,我們就說這個函數的功能是做 side effect。例如,函數從數據庫讀取或寫入數據。

在React當中,一個 functional component 如果想要做 side effect,則可以使用 useEffect 這個 Hook。常見的side effect 有:向 API 去 fetch 數據、使用 setTimeout 等等的計時函數。

useEffect Hook 語法接收兩個參數:

useEffect(function, dependencies)

Dependencies-array of states:

1. 如果 dependencies 是一個 empty array,則在此 Component 第一次被渲染的時候,就會執行 useEffect 參數的 function一次。

2. 如果 dependencies 是 [name],則在此 Component 第一次被渲染的時候,就會執行 useEffect 參數的 function 一次。每當 name 這個 state 被更新時,也會執行 useEffect 參數的 function 一次。

app.js

import React, { useState, useEffect } from "react";


function App() {

  let [myName, setMyName] = useState("王小明");

  const buttonHandler = () => {

    setMyName("王大明");

  };

  useEffect(() => {

    console.log("useEffect 內部的 function 正在被執行。。。");

  }, [myName]);

  return (

    <div>

      <h1>{myName}</h1>

      <button onClick={buttonHandler}>改變姓名</button>

    </div>

  );

}

export default App;

 

Index.js

import React from "react"; // 相同於 const React = require("react")

import ReactDOM from "react-dom/client";

import App from "./App";


const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(

  <App />

);

 

Reacr Router

因為 Create React App 並不自動包含 page routing 的功能,所以需要下載 react-router-dom 這個 package。

npm install react-router-dom

語法如下:

app.js

import { BrowserRouter, Routes, Route } from "react-router-dom";

import Homepage from "./Homepage";

import About from "./About";

import Blog from "./Blog";

import Page404 from "./Page404";

import Layout from "./Layout";


function App() {

  return (

    <BrowserRouter>

      <Routes>

        <Route path="/" element={<Layout />}>

          <Route index element={<Homepage />} />

          <Route path="about" element={<About />} />

          <Route path="blog" element={<Blog />} />

          <Route path="*" element={<Page404 />} />

        </Route>

      </Routes>

    </BrowserRouter>

  );

}

export default App;

Layout.js

import { Outlet, Link } from "react-router-dom";

import React from "react";

const Layout = () => {

  return (

    <div>

      <nav>

        <ul>

          <li>

            <Link to="/">首頁</Link>

          </li>

          <li>

            <Link to="/about">關於這個網站。。。</Link>

          </li>

          <li>

            <Link to="/blog">Blog 文章</Link>

          </li>

        </ul>

      </nav>

      <Outlet />

    </div>

  );

};

export default Layout;

Homepage.js

import React from "react";

const Homepage = () => {

  return <div>Homepage</div>;

};

export default Homepage;

 

About.js

import React from "react";

const About = () => {

  return <div>About</div>;

};

export default About;

Blog.js

import React from "react";

const Blog = () => {

  return <div> Blog 文章 </div>;

};

export default Blog;

Page404.js

import React from "react";

const Page404 = () => {

  return <div>Page404</div>;

};

export default Page404;

 

相關文章:


React 筆記 – 前端框架


CSS – 筆記


HTML – 筆記

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
>