Skip to main content

2-5


React WAI-ARIA

  • 所有的 aria-* HTML attribute 在 JSX 中都是支援的。
  • 使用連字符 (kebab-case)
<input
type="text"
aria-label={labelText}
aria-required="true"
onChange={onchangeHandler}
value={inputValue}
name="name"
/>

React Fragment

  • 使用 <Fragment> 能不違反有限制 HTML 上下層結構的規則。
<dl>
{props.items.map(item => (
<Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</Fragment>
))}
</dl>
  • <Fragment> 不需要任何的 props ,則可以使用簡寫:
<>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</>

Input for in React

  • for attribute 在 JSX 中是寫作 htmlFor

React Code-Splitting

  • 動態 import() 語法:
import("./math").then(math => {
console.log(math.add(16, 26));
});
  • React.lazy 用法:
  • 其中 <Suspense> 可包裹多個 lazy component,並在 fallback 傳入 lazy loading 時顯示的 component
import React, { Suspense } from 'react';

const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));

function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<section> // 可用其他元素包 lazy component,只要在 <Suspense> 內即可
<OtherComponent />
<AnotherComponent />
</section>
</Suspense>
</div>
);
}

HTML <details>

  • 原生 HTML 元素,點擊 maker 可打開內文
  • <summary> 可不給,會顯示對應瀏覽器語言環境的“詳細資訊“文字
<details>
<summary>Epcot Center</summary>
<p>Epcot is a theme park at Walt Disney World Resort featuring exciting attractions, international pavilions, award-winning fireworks and seasonal special events.</p>
</details>

[Image: 截圖 2021-05-03 下午2.17.27.png]


MVC 架構概念加強

  • serperation concecpt (關注點分離),此概念衍生了 MVC 的程式設計架構。
  • model 與 view 的部分較好做測試,controller 等於是 glue code 將 MV 做關聯,邏輯複雜較不好模擬測試。
  • fat model、slim controller,故以上述來說, model 的東西應該是要最多,controller 部分應該盡量減少。

React import assets

  • 跟 Vue 一樣,在 component 內 import 會被打包成動態名稱的資源,若直接用 /logo.svg 寫法則是存取 public 內不會被 webpack 處理的靜態資源。
  • 只要是 svg 檔就能 import { ReactComponent as Logo } from "./logo.svg";ReactComponent 引入再進行 as 重新命名就能將 svg 做為一般的 component 做使用,也能給予 class 或 style 等等。
<Logo className="App-logo" style={{ height: 200 }} />

Open API

React CSS scoped

  • 引入 component 的 css 檔都會是 global 的。

useEffect

  • 傳遞到 useEffect 的 function 會在 render 到螢幕之後執行。類似於 Vue2 的 mounted
  • useEffect 觸發時機保證在資料更新畫面後觸發,保證在第二次資料更新前。
  • useEffect 第一個參數給要執行的 function,第二個參數給陣列,陣列內的值若更新將會觸發執行 useEffect。類似於 Vue2 的 updated 。若給定空陣列將只會在第一次 render 後執行一次而已。
  • 傳遞給 useEffect 的 function 可以回傳一個 function,它將會在組件銷毀前執行,例如 event handler 要移除,不然下次 render 後又會再偵聽一次。
  • 若陣列內參數值更新後的值還是和原本一樣,則不會觸發執行,React 使用 Object.is 比較。
  • useEffect 跑太久,也會影響後面的畫面更新。

useLayoutEffect

  • useLayoutEffectcomponentDidMountcomponentDidUpdate 的呼叫時機是一樣。
  • useLayoutEffect 會在 計算完 state,Virtual DOM 更新完後,畫面 render 之前執行,也就是 Virtual DOM 更新完後,可以再更新一次 state,或者做其他事情,可以避免多一次 reflow、repaint,但會阻塞 render 到畫面,若 useLayoutEffect 裡做的事情或觸發更新 state 需要跑很久的話,畫面更新阻塞的情況會很明顯。

Pixel、DPI/PPI

  • PPI → pixels per inch, DPI → dots per inch
  • PPI 單位是 pixel,永遠跟螢幕有關; DPI 單位是點,這個點在有些地方是代表 pixel,在輸出列印時代表墨點(dot)。
  • CSS pixel 是 web 世界的 pixel,它與螢幕像素之間有一個倍率關係,這個倍率關係有許多名字,有稱之為 device pixel ratio,簡稱 DPR,也有稱之為 dots per pixel,簡稱 DPPX,也有人稱為 pixel density,不論是 DRP 或 DPPX 或 pixel density,他們的意義都是一樣的——螢幕像素與 CSS pixel 之間轉換的倍率值。
  • 實際上用 CSS media query 與 JavaScript 查出來的寬度,也都會是經過 DPR 換算後的寬度,CSS pixel 與 DPR 的計算一切都是隱式發生的,就算對 CSS Pixel 與 DPR 完全沒概念的人,也不影響開發 web,不需考慮設備的真實解析度,一律都以 CSS pixel 的尺度去設計即可。
  • 在 Retina Dispaly 出現以前,CSS pixel 與螢幕 pixel 是很間單的 1:1 對應,一顆像素就是一顆像素,並沒有比例關系的概念,Retina display 問世後,一個 CSS pixel 對應到螢幕像素的關係呈一種比例關係。
  • 不用去管圖檔本身的 DPI 設定,它只在列印時有意義。
  • 平台佈局的單位是什麼?web 用 CSS pixel、iOS 用 iOS pt、Android 用 dp,用正確的單位進行版面的佈局,不要去糾結螢幕的硬體解析度。
  • 圖檔依照平台指定的倍率提供,iOS 有 @1x ~ @4x、Android 有 ldpi ~ xxxhdpi,確保 app 內的圖檔都有提供這些倍率的版本,web 的話則是用 <img> 標籤的 srcsetsizes 來定義各倍率的圖檔,瀏覽器會自行抓取最適合的做顯示。

useRef

function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
  • 建立一個 mutable 的變數,此變數更新不觸發重新 render 畫面,到下次 state 更新後,ref 的更新才會顯示到畫面。
  • 可用於放 Dom 元素或是紀錄 state 前一次的值。
  • 補充:useEffect、useLayoutEffect 皆是在 state 計算完後與 Vitual DOM 更新才執行,因此在這兩個 hook 中執行變更 ref 的值時,因為已經計算完與更新 Vitual DOM 了,故接下來 render 到畫面上時,ref 的值還會是未變更前的。

useCallback

const clickHandler = useCallback(() => {
console.log('clickHandler!')
}, [])
  • 將 function Instance 紀錄起來使每次 render 時,若第二個陣列參數的 ref 都沒變,則會傳繼續用上次的 funciton Instance。
  • 使用場景:若將某個 function 當作 props 傳給子元件時,父元件本身重新 render 時,每次都會建立新的 function 傳給子,子得到的 props 若前後不一樣,則子也會重新 render,在渲染大量列表時會非常耗效能,故可以使用 useCallback 將 funciton Instance 沿用。

useMemo

const expensiveValue = useMemo(() => {
return id + (Math.random() * Math.random() * 1000000 / Math.random() + Math.random() * Math.random() * 100)
}, [id])
  • useCallback 雷同,只是 useMemo 是紀錄純值,將耗費大量運算得到的值記錄起來,若陣列參數的 ref 都沒變,則不會再算一遍。

React.memo()

const PostView = ({ id, title, content, createdAt }) => {
return (
<>
<h3>{title}</h3>
<TimeAgo timestamp={createdAt} />
<p>{content.slice(0, 100) + '...'}</p>
<Link to={`/post/${id}`}>Read more</Link><br/>
</>
)
}

export default React.memo(PostView)
  • React.memo 是用來記住 component 的,若此 component 的 state, props 等等都沒變,則不會重新渲染,一樣在渲染大量列表時,父組件本身狀態更新,但子組件的狀態都一樣時,可以省下不必要的重新渲染。

react-transition 會渲染多次

  • 使用 react-transition 套件時,綁定動畫的組件會在執行動畫時渲染三次,開始進場、進場中、進場完成,離開時等同。
  • 因此若 route switch 時頁面有動畫進退場的話,該頁面也會重新渲染三次,在複雜的頁面下會消耗許多運算資源,因此若真的要頁面有進退場動畫,要將該頁面裡較複雜的組件做好 memo 控制何時該重新渲染,一個個將組件的渲染優化。