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
- 專為練習打造有常用範例所需的資料:post、comments、photos、todos、users 等等
- http://jsonplaceholder.typicode.com/
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
useLayoutEffect
與componentDidMount
和componentDidUpdate
的呼叫時機是一樣。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>
標籤的srcset
和sizes
來定義各倍率的圖檔,瀏覽器會自行抓取最適合的做顯示。
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 控制何時該重新渲染,一個個將組件的渲染優化。