1 of 31

React

@前端讀書會

上德, 2021/6/4

2 of 31

Outline

  1. React 基本知識
  2. React vs Vue
  3. 總結

1

3 of 31

一、React 基本知識

2

4 of 31

Hello World

<script>

3

ReactDOM.render(

<h1>Hello, world!</h1>,

document.getElementById('root')

)

<body>

<div id="root">

</div>

JSX

5 of 31

JSX

JSX 是一個直接寫在 javascript中的擴充語法,會透過 Babel編譯成 React 用來建立 Element的函式。

React 的 Element並不是真的 DOM,他是一種「virtual DOM」。所有的真實 DOM的改變都是經由改變 virtual DOM之後,經由 diff演算法批次改變 DOM的內容

4

const content = (

<TalkList>

{ talks.map(talk => <Talk talk={talk} />)}

</TalkList>

)

const h1 = <h1 className="title">Hello Adam</h1>

const input = <label htmlFor="test">Test</label>

大部份的 attribute和原來的 html都長得一樣,只是轉為小駝峰的格式而已。只有極少部份會因為 javascript保留字的考量會稍微不太一樣,比如:

class => className

for => htmlFor

6 of 31

單向資料流 (Unidirectional Data Flow)

React Component只有兩種資料型式:1. Props 2. State

React 中資料的流向只能從父層 Component 的 State/Props 傳入子 Component的 Props

每當 React偵測到 Props或 State有更新時,就會自動重繪整個 Component

5

7 of 31

React的更新機制

6

Props/State 改變

重新呼叫 render()

生成新的 virtual DOM

新舊 virtual DOM 進行 diff 演算法比對

把差異更新到真的的 DOM上

8 of 31

State

State是 Component內部維護的的資料。

不能直接對 State賦值,只能透過 setState() 來更新 state。

當 state被更新時 render function會被重新呼叫。

註:在介紹 hooks之前,用 Class Component比較好解釋 State,所以暫時還是先用 Class Component的程式來當範例。

7

class Clock extends React.Component {

constructor(props) {

super(props);

this.state = {date: new Date()};

}

componentDidMount() {

// 啟動一個 timer 每秒鐘會自動更新時間

this.timerID = setInterval(() => this.tick(),1000);

}

// 當元件要從 DOM 中被移除 (remove) 前,要手動清理監聽事件

componentWillUnmount() {

clearInterval(this.timerID);

}

tick() {

// 用 setState() 來更新 State React 會自動呼叫 render() 重繪畫面

this.setState({ date: new Date() });

}

render() {

return (

<div>

<h1>Hello, world!</h1>

<h2>It is {this.state.date.toLocaleTimeString()}.</h2>

</div>

);

}

}

9 of 31

Stateful / Stateless Component

Stateful Component:內部有維護自身狀態 (State)的改變,通常會伴隨著事件處理或生命週期下觸發狀態的更新。早期只能用 Class Component來寫,現在可以使用 Function Component + hooks來寫。

8

const ImageList = props => {

const images = props.images.map({ description, id, url} => {

return <img alt={description} key={id} src={urls.regular} />

})

return <div>{images}</div>

}

Stateless Component: (又稱 Pure Component) 是一個 pure function,props傳入什麼他就 render什麼。

10 of 31

Controlled / Uncontrolled Element

9

state = { term: '' }

render () {

return (

<input

type="text"

value={this.state.term}

onChange={e => this.setState({ term: e.target.value })}

/>

)

}

Controlled Element: value存放在 state

Uncontrolled Element: value存放在 DOM裡面,必須要從 DOM取出來後才能知道 value是什麼(古早時期 jQuery的慣用寫法)

* 我們應該將所有資訊存放在 Component內而非 DOM裡面

11 of 31

Class Component vs Function Component

10

Class Component

Function Componnet

Function Component + Hooks

Lifecycle

O

X

X

State

O

X

O

非同步操作

O

X

O

傳統上 Function Component 因為必須是 pure function – 透過 function傳入進去的 props,回傳 virtual DOM回去;既無法暫停 render也不能自動 render。

所以早期大多採用 Class Component以保存 Component的內部狀態。

在React v16.8 (2019) 推出 hooks功能之後官方已不再推薦採用 Class Component了。

12 of 31

Lifecycle

看一下就好… 改用 hooks之後就不用再管 Lifecycle了

11

class Title extends Component {

shouldComponentUpdate(nextProps, nextState) {

// 改變後的 props 跟原本一樣的話就不要呼叫 render()

if (nextProps.title !== this.props.title) {

return true

}

return false

}

render() {

return <div>{this.props.title}</div>

}

}

13 of 31

效能優化

React需要特別注意減少 render次數和計算量

範例(每當state變更,render就一直被執行)

https://codepen.io/cdpqdnvr/pen/ZEeawKx?editors=1010

參考資料:

12

import React from 'react';

const Child = () => {

console.log('觸發Child元件渲染');

return (

<h1>這是child元件的渲染內容!</h1>

)

}

const areEqual = (prevProps, nextProps) => {

// 在這裡比較差異

}

const MemoChild = React.memo(Child, areEqual);

export default () => {

const [num, setNum] = useState(0);

return (

<>

{num}

<button onClick={() => setNum(num + 1)}>num加1</button>

<MemoChild />

</>

);

}

14 of 31

Hooks

13

  • 具有狀態(State)可邏輯重用的函式寫法。

  • React v16.8 推出了 hooks功能之後(2019/2/6發布),Class Component已不再被官方推薦使用。Function Component 搭配 hooks使用也可以有 State。

15 of 31

Hooks - useState

14

const { useState } = React

// 寫在 render function內

const [input, setValue] = useState("")

16 of 31

Hooks - useEffect

15

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

function FriendStatus(props) {

const [isOnline, setIsOnline] = useState(null);

useEffect(() => {

function handleStatusChange(status) {

setIsOnline(status.isOnline);

}

ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);

// 指定如何在這個 effect 之後執行清除:

return function cleanup() {

ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);

};

});

if (isOnline === null) {

return 'Loading...';

}

return isOnline ? 'Online' : 'Offline';

}

和 Vue的 watch有點像,但在 React裡面被認為是一種 state變更的附作用,可見背後思考邏輯的不同

17 of 31

Hooks - useContext

16

// App.js

export const ThemeContext = React.createContext()

const App = () => {

const [dark, setDark] = useState(true);

return (

<>

<ThemeContext.Provider value={dark}>

<FunctionComponent />

<ClassComponent />

</ThemeContext.Provider>

</>

);

}

// FunctionComponent.js

const FunctionComponent = () => {

return (

<ButtonGroupComponent />

);

}

// ButtonGroupComponent.js

import { ThemeContext } from './App.js';

const ButtonGroupComponent = () => {

// get dark value from ThemeContext

const darkTheme = useContext(ThemeContext)

const themeStyle = {

backgroundColor: darkTheme ? '#2c3e50': '#f1c40f',

color: darkTheme ? '#ecf0f1' : '#2c3e50'

}

return (

<button style={themeStyle}>useContext</button>

);

}

18 of 31

二、React vs Vue

17

19 of 31

functional

View = fn(state)

18

MVVM

vs

20 of 31

Pull更新

this.setState({value: 3})

State是不可變的,需用新的 State-tree全部取代舊的 State-tree,再經由React內部的演算法比較全部的 virtual DOM差異。

需額外手動優化效能,以避免不必要的 rerender。

19

Push更新

vs

this.value = 3

各別元素監聽 State的更新,以響應式的機制更新局部節點的 DOM。

Vue內部已完成所有的效能優化,不需手動優化效能。

21 of 31

手動優化

  • Render function 中盡量減少執行次數和計算量。
  • 主要的做法是將不必要的元素製作成 Stateless Component(使用 React.memo)

20

Do nothing

vs

22 of 31

Controlled Element

Vue有和「雙向綁定」的方式,可以簡單的達到和 React 的 Controlled Element一樣的效果。

是以直接監聽資料變更,以響應式的方式來綁定資料。

21

Two-way binding

vs

state = { term: '' }

render () {

return (

<input

type="text"

value={this.state.term}

onChange={e => this.setState({ term: e.target.value })}

/>

)

}

React 的 State是不可變的,必須透過 setState函式變更(實際上是替換掉),所以是以單向資料流的方式來完成。

實作上需要手動寫監聽事件將元素的 Value更新到 State。

<input

type="text"

v-model="term"

/>

23 of 31

props.fn (Callback)

  • React 只能

22

Emit

vs

React 的資料流向只能從父層 Component 透過 Props 流向子 Component,所以當子 Component需要將資料傳遞過父層時,得透過 Props傳入的 Callback Function回傳回去。

// parent

const App = () => {

const handleClick = (evt) => {

console.log(evt)

}

return (

<div>

<AppButton onClick={handleClick}></AppButton>

</div>

)

}

// childconst AppButton = ({ onClick = () => {} }) => {

return <button onClick={onClick}>Click Me</button>

}

24 of 31

props.children

巢狀Component中,子Component會作為props.children傳入父Component

23

<slot>

vs

const Child = ({ name }) => (

<h1>{`Hello ${name}`}</h1>

);

const Parent = ({ children }) => (

<>

<Something />

{children}

<OtherThings />

</>

);

const Page = () => (

<div>

<Parent>

{/* <Child/>會作為 props.chilren傳入<Parent/>中 */}

<Child name="world"></Child>

</Parent>

</div>

);

<template>

<!– Child.vue -->

<h1>Hello {{name}}</h1>

</template>

<template>

<!– Parent.vue -->

<div>

<Something />

<slot></slot>

<OtherThings />

</div>

</template>

<template>

<!– Page.vue -->

<div>

<Parent>

<Child name="world"></Child>

</Parent>

</div>

</template>

巢狀Component中,子Component會傳入父Component的 <slot>元素中

25 of 31

Hooks

  • 可邏輯重用的函式化Component API。

  • 可以較容易的把相同邏輯的程式整理在一起。

  • 對Typescript的支援度較好。

24

Composition-api

vs

  • 具有狀態(State)的可邏輯重用的函式寫法。

  • 解決同一個程式邏輯分散在不同生命週期裡的問題(因為 Function Component不需要生命週期)。

26 of 31

CSS-in-JS

React 社群多使用 css-in-js方式編寫UI樣式,但也有人主張傳統的 css文件比較好

有跨平台優勢。

效能可能會較差一些。

目前最多人使用的套件是 styled-components。

25

.css file

vs

Vue原生對於 css module支援度就很好,不需額外搭配套件就能整合的很好。

Vue 也可以額外搭配套件用 css-in-js的方式編寫樣式,但較少人這樣做。

27 of 31

三、總結

26

28 of 31

其他

React的生態系相較於 Vue和 Angular感覺比較零散一點,雖然近年來已經沒那麼大變化了,但還是會明顯感覺沒有所謂的「公版」,需要花力氣整理出自己的寫法;覺得比較麻煩的有:

  1. CSS:有「CSS in JS」及「非CSS in JS」不同流派,以及許多套件可選擇(目前主流應該是 styled-component)。
  2. 狀態管理套件:不像 Vue只要選擇 Vuex就搞定了。雖然說主流是 Redux(也有人用MobX),但是 Redux異常複雜,就我自己研究至少要安裝 Redux、React-Redux、Redux-Thunk 共三個套件才能達到最低的使用需求;其中非同步處理的套件又各有支持者(一般是說入門用Redux-Thunk),而且要整理出適合自己的寫法又要花一翻力氣。可以參考我寫的文章→Next.js上使用 Redux四兄弟

27

29 of 31

個人小小的見解

  • 以前端工程師來說:雖然 React學習曲線較陡但不會是個問題,(可能)會覺得寫起來蠻有趣的。

  • 非前端工程師但想寫一點前端的人來說:想要快速建立一個簡單的網頁,雖然一開始可能會有不難的錯覺,但隨著需求愈來愈複雜會一直遇到愈來愈多的障礙,(很有可能)會寫得很痛苦。

  • 對設計師來說:雖然JSX不難,但如果想寫一點 js做點互動效果對設計師來說就有點不太友善了。如果在團隊裡設計師需要切版的話,可以切純靜態的版 + css file;如果要用 css-in-js(現在 React社群流行的寫法),可能就必須將切版工作交給工程師了。

  • 以團隊來說:前期的時候可能需要花多一點心思建置環境、以及建立團隊的程式規範(否則每個人的寫法會差異很大)。

  • 總的來說:React做的到的事情 Vue也做的到(只是背後的思考邏輯不太一樣而已)。個人覺得寫 Vue的開發體驗還是會比較愉快一點,因為可以用較簡易的方式就達到同樣的目的。而且在 Vue v3.x之後,React的「有彈性」的特性似乎也沒有特別的優勢。

28

30 of 31

  1. 職缺較多、薪水(可能)較高
  2. React-Native

29

  1. 需要開發者手動處理效能優化
  2. 學習曲線較陡,入門成本較高
  3. 技術發展變化很大,生態系相較其他框架感覺比較破碎,較仰賴自行整合適合自己的開發方式

優/缺點分析

31 of 31

謝謝聆聽

30