- 1. 什麼是 React.js?
- 2. 什麼是元件? 為什麼元件的概念對 React 來說很重要?
- 3. Props 和 State 差別在哪?
- 4. 選擇一個曾經在專案中用過的元件週期,並介紹你是怎麼使用的?
- 5. Class 跟 Functional 元件的差別? 該怎麼選擇?
- 6. 什麼是 React 中的 Events?
- 7. 什麼是 JSX?
- 8. 什麼是 Virtual DOMs? 又是怎麼運作的?
- 9. 你會怎麼 Debug 用 React 寫出來的網頁應用? 用哪些工具?
- 10. React 跟 React Native 差別在哪?
- 11. 用 React 的優點是什麼?
- 12. React 有什麼缺點和限制?
- 13. 什麼是 Redux?
- 14. 使用 Redux 的優點是什麼?
- 15. 為什麼你會想選擇 React?
- 16. 元件渲染的三個狀態
- 17. 什麼是 Reconciliation?
- 18. 什麼是 HOC (Higher-Order Components)?
- 19. React 中的 Portals 是做什麼用的?
- 20. React v16 錯誤邊界 (Error Boundaries) 是什麼?
- 21. React 中要怎麼實作 Props 相關檢查?
- 22. 如何在頁面載入時自動 focus 在輸入框上?
- 23. 同場加映
因為最近想準備面試,所以在網路上看到國外這篇 15 個關於 React JS 的面試問題覺得蠻適合當作 React 入門的必備知識,底下是常見的 React 相關問題提供給各位大大參考。
什麼是 React.js?
- React.js 是一套協助實作 Web 和 Mobile 使用者介面的前端函式庫 (library)
- 是 Facebook 2011 年認為現有解決方案無法處理目前問題,決定開始研發的一套函式庫
- 累積至今有大量開源社群的支持
什麼是元件? 為什麼元件的概念對 React 來說很重要?
React 是以元件為基礎的函式庫,元件像積木一樣組成網站的使用者介面。主要有兩個好處:
- 把使用者介面拆解成可重複使用的部分
- 透過 React 的優化,可以做到只更新畫面中部份的元件讓效能更好
Props 和 State 差別在哪?
之前剛好寫過關於元件狀態與性質的文章,不過簡單回答:
狀態(State): 元件裡的資料,但值可以透過元件內邏輯操作而改變
性質(Props): 用來設定元件初始化的相關資料,無法透過元件內邏輯去改變
選擇一個曾經在專案中用過的元件週期,並介紹你是怎麼使用的?
週期在元件中是以函式方式顯示,在特殊階段會被觸發執行,其中 componentDidMount
就是當元件出現在畫面後會被執行。
Class 跟 Functional 元件的差別? 該怎麼選擇?
React 提供了兩種元件實做方式,更詳細的比較歡迎參考 React Class-based vs Functional Component 從特性淺談兩種寫法之異同
- Functional component: 是最簡單的寫法,比較適合用來實作邏輯單純顯示用的元件
- Class components: 較複雜且可以控制較多的元件週期和狀態比對
什麼是 React 中的 Events?
事件其實可以看成反應 (reactions) 的意思,還記得函式庫命名就是 React 吧。透過使用者的動作像是點擊、鍵盤輸入等來觸發使用者介面的變化,React 其實主要就是協助我們處理這些事情。
React 中的事件在撰寫上有稍微的不一樣
- React event handlers 會用駝峰式的命名,HTML 上面的是全小寫
- 透過 JSX 綁定的是 event handler 而不像 HTML 上面是綁字串
什麼是 JSX?
JSX 是 React 的語法糖,讓開發者能夠在元件中寫類似 HTML 的語法,能夠提升可閱讀性,所以如果不嫌麻煩也可以選擇不使用,雖然類似但命名和寫法還是略有差異,但透過編輯器外掛協助下其實算容易適應,像是 JSX 就需要把 class
寫成 className
。
function Demo() {
return (
<div>
<h1>Hello world!</h1>
</div>
);
}
function Demo() {
return React.createElement(
"div",
null,
React.createElement("h1", null, "Hello world!")
);
}
什麼是 Virtual DOMs? 又是怎麼運作的?
瀏覽器載入網頁的過程會把 HTML 的內容轉成資料結構存放在執行環境中,那個結構我們叫做 Document Object Model (DOM)。
如果不使用 React 又想更新網頁上的畫面,就是直接更新 DOM 裡面的值產生畫面改變。由於在結構中去尋找並更新值屬於複雜的行為,所以 React 在特定的假設下透過多實作一層 DOM 的影分身 Virtual DOM 來加速網頁的效能。
Virtual DOM 這個影分身會被存在記憶體中,並透過一個算法來和真實的 DOM 保持同步,透過算法比對到更新 DOM 的整個過程就叫做 reconciliation,其中 Fiber 則是 React v16 中新的 reconciliation 算法。
Virtual DOM 為什麼可以這麼快? 其實 React 只是策略上改進並沒有做什麼新的花樣,透過演算法操作在記憶體中的 DOM,接著 batch 一小段時間的變化才去改變真實的 DOM,盡可能減少 re-flow 跟 re-paint 的發生而已。
你會怎麼 Debug 用 React 寫出來的網頁應用? 用哪些工具?
- Linters (eslint, jslint)
- Debuggers (React Developer Tools)
React 跟 React Native 差別在哪?
- React.js 是一套協助實作 Web 和 Mobile 使用者介面的前端函式庫 (library)
- React Native 是一套以 React 概念為基礎用來開發原生 Android 或 iOS APP 用的框架
用 React 的優點是什麼?
- 可能會提升網站效能和使用者體驗
- 元件可重複使用提升開發效率
- React 生態系提供外掛工具提升專案除錯效率
React 有什麼缺點和限制?
前陣子剛好在網路上看到一篇國外文章分享,所以小編就翻譯了一篇不在大型專案導入 React.js 的 5 個原因: 關於跟著 React.js 一路成長的心得分享,簡單來說:
- 不是完整框架只是一套 UI 函式庫所以要裝很多外部套件才能成為完整的應用
- 要整合到傳統的 MVC 架構需要比較複雜的額外配置
- 學習上有一定的難度,像是元件概念和 JSX 都要花時間熟悉
- 缺乏規劃會寫出很多 boilerplate
什麼是 Redux?
Redux 是一套除了 React 外也可以在其他 UI 的函式庫中使用的資料流管理工具,定義了資料流的規範補足了 React 在元件變多後狀態難以控管的問題,讓開發者能統一管理元件的狀態,讓程式更容易去維護和測試。
使用 Redux 的優點是什麼?
定義了一套公版資料流與程式架構,提高開發方式的可預期性、可維護性,透過 Redux 的開發者外掛開發也能夠更方便。
為什麼你會想選擇 React?
- Virtual DOM 在某些情境下優化了效能
- JavaScript 的開發者能在同個檔案完成所有開發動作
- 元件能重覆利用,加速了開發速度
- 提供 JSX 支援增加程式碼易讀性,但有沒有更好寫就見仁見智
- 同樣的程式碼可以在 client 端和 Sever 端渲染出一樣的結果
- Jest 讓單元測試更簡單
元件渲染的三個狀態
元件從 Props 或 State 變化後引發改變的過程,主要分為兩大階段 Render 和 Commit,會先進行 Render 的計算後才會真的 Commit 結果到真正的 DOM 上面,Commit 前會有個 Pre-commit。
- Render: 在這個階段 React 能自行暫停、取消、重新這個過程
- Pre-commit: 文件上有出現但甚少使用的功能,有一個週期是
getSnapshotBeforeUpdate()
,可以看成是一個做決定前的再次狀態確認 - Commit: 套用改變到瀏覽器的 DOM 上,而這是肉眼可見,也是我們較常操作的週期
componentDidMount()
: Mount 成功,出現在 DOM 上面componentDidUpdate()
: Props 或 State 改變componentWillUnmount()
: 從 DOM 上移除
什麼是 Reconciliation?
當元件 props 或是 state 改變後,React 會透過 render 的演算法來決定最終要更新到 DOM 上面的改變,這個過程就是 reconciliation。
如果是想自己手動提升效能則可以運用條件減少狀態改變:
getUserAddress = (user) => {
const latestAddress = user.address;
this.setState((state) => {
// 地址沒變就不用換畫面
if (state.address === latestAddress) {
return null;
} else {
return { title: latestAddress };
}
});
};
hooks 的形式則是 useMemo
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
什麼是 HOC (Higher-Order Components)?
常寫 JavaScript 的開發者就會知道,function 可以 return 任何東西,而 return 一個新的 function 的 function 我們就稱作 HOF (Higher-Order Function),底下是範例:
var xHOF = function (x) {
return function (y) {
return x * y;
};
};
var xFive = xHOF(5);
var xTen = xHOF(10);
xFive(2); // 10
xTen(2); // 20
一個 HOC 就是一個函式 (function),接收到元件後,產生出另一個新的元件,在卡通影片中,可以想像成金剛戰士透過手表進行變身,或是數碼寶貝透過共振主人的無限可能進化成更強的模樣。一個 HOC 有底下四個特點:
- Pure component: 沒有修改原來行為只有加強功能
- Code reuse: 讓邏輯能夠重複使用
- Render hijacking: 在真的元件 render 前,處理 render 前需要處理的事情,像是資料防呆或是將操作記錄發到後台
- Manipulation: State 和 Props 統一控制,元件載入狀態動畫
function HOC(WrappedComponent) {
return class Test extends Component {
render() {
const newProps = {
title: "New Header",
footer: false,
showFeatureX: false,
showFeatureY: true,
};
return <WrappedComponent {...this.props} {...newProps} />;
}
};
}
有 staticMethod 的話要自己複製:
function enhance(WrappedComponent) {
class Enhance extends React.Component {
/*...*/
}
Enhance.staticMethod = WrappedComponent.staticMethod;
return Enhance;
}
React 中的 Portals 是做什麼用的?
正常 react 所有的渲染都只掛載在某個 Parent 節點下,如果需要跳脫這個節點就需要 Portals,第一個參數可以放任何可被渲染的 child,第二個參數則是真實的 DOM element。
ReactDOM.createPortal(child, container)
React v16 錯誤邊界 (Error Boundaries) 是什麼?
錯誤邊界其實也只是個元件,錯誤邊界的概念像是一道防火門,讓當渲染發生錯誤時,能夠被關在 child component 中並顯示客製化的錯誤 UI,透過 componentDidCatch(error, info)
或 static getDerivedStateFromError()
這兩個新增的周期來協助記錄相關錯誤。
This lifecycle method is invoked after an error has been thrown by a descendant component. It receives the error that was thrown as a parameter and should return a value to update state.
The signature of the lifecycle method is as follows,
static getDerivedStateFromError(error)
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
// You can also log the error to an error reporting service
logErrorToMyService(error, info);
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>{"Something went wrong."}</h1>;
}
return this.props.children;
}
}
當撰寫完成後,使用上也非常簡單,只要把原來的元件包起來就好了,底下是範例
<ErrorBoundary>
<MyBugComponent />
</ErrorBoundary>
錯誤邊界在底下四種情形不會 catch 到錯誤
- 在 Event handlers 中,只能夠自己寫 try catch
- setTimeout 或 requestAnimationFrame 的 callbacks
- 伺服器渲染的過程
- ErrorBoundary 沒寫好自己發生的錯誤
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { error: null };
this.handleClick = this.handleClick.bind(this);
}
// 在 Event handlers 中,只能夠自己寫 try catch
handleClick() {
try {
// Do something that could throw
} catch (error) {
this.setState({ error });
}
}
render() {
if (this.state.error) {
return <h1>Caught an error.</h1>;
}
return <button onClick={this.handleClick}>Click Me</button>;
}
}
React 中要怎麼實作 Props 相關檢查?
雖然 JavaScript 是弱型別的語言,但 React 內建 PropTypes check 機制,當 APP 處在開發模式下,React 會自動確認是否每個 props 都是如所設定的格式,若不正確 React 就會在命令列中產生警告的訊息,這個機制由於效能考量在 Production 環境中會被停用。
React 定義了底下十種型別:
PropTypes.number
PropTypes.string
PropTypes.array
PropTypes.object
PropTypes.func
PropTypes.node
PropTypes.element
PropTypes.bool
PropTypes.symbol
PropTypes.any
Props 中可分為必填和非必填,必填的後面則加上 isRequired
像是 PropTypes.number.isRequired
,如果陣列中的物件需要定義則可以搭配使用 React.PropTypes.shape()
和 React.PropTypes.arrayOf()
,如果 Props 會是變動的,也可以使用 React.PropTypes.oneOfType()
。
ReactComponent.propTypes = {
arrayWithShape: React.PropTypes.arrayOf(
React.PropTypes.shape({
name: React.PropTypes.string.isRequired,
weight: React.PropTypes.number.isRequired,
})
).isRequired,
};
ReactComponent.PropTypes = {
size: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
};
如何在頁面載入時自動 focus 在輸入框上?
只需要幫 input
加上參照 ref
就可以在 componentDidMount()
後針對 input
進行操作。不過這種 inline function 的寫法在元件更新的時候因為底層的邏輯會被執行兩次,所以不被推薦。
class App extends React.Component {
componentDidMount() {
this.nameInput.focus();
}
render() {
return (
<div>
<input defaultValue={"Won't focus"} />
<input
ref={(input) => (this.nameInput = input)}
defaultValue={"Will focus"}
/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
比較推薦的寫法如下:
class UserForm extends Component {
handleSubmit = () => {
console.log("Input Value is: ", this.input.value);
};
setSearchInput = (input) => {
this.input = input;
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" ref={this.setSearchInput} /> // Access DOM input in
handle submit
<button type="submit">Submit</button>
</form>
);
}
}
同場加映
在面試的整個流程中,其實有許多事情是可以多加強的,底下也列出幾篇文章,分享這幾年面試下來的心得給大家:
喜歡這篇文章,請幫忙拍拍手喔 🤣