前端面試資料整理【React篇】

donggg 發表於 2022-05-19
前端 面試 React

React

參考

React 原始碼

react 為什麼實用 JSX?
蘿蔔青菜各有所愛。
但是 react 團隊認為模板方案不好:

  • 模板分離了技術棧,增加了技術點。
  • 看起來像 html 的 jsx。
  • react 規矩性太強,需要 jsx 來輔助
例如使用 vue 上面是 html 模板,下面是js邏輯從而形成一個元件功能。但是 react 將 html 和執行邏輯都執行在一個 class。

JSX 對映虛擬 DOM 的原理?
JSX 是 creatElement 的語法糖,使用的是 babel 的轉義器,實際是通過兩個方法完成,一個是 creatElement,一個 reactElement

// 定義一個 react 物件
reactElement(type, key, ref, self, source, ReactCurrentOwner.current, props)

Rreact 資料流管理
資料驅動檢視?
UI = render(data)

data 與 state 的區別是什麼?
狀態(state)與資料(data),元件中的通訊就是 data 的通訊,react 核心就是對資料的管理

資料流動:

  • 父 -> 子
  • 子 -> 父
  • 兄弟
  • 無關係

單向資料流

MVC的劣勢雙向資料流。引起混亂。

Action -> Dispatcher -> Store -> View

              |-------<------Action<-|

ACTION 檢視層傳送的訊息

redux 是 js 的狀態容器

Action -> Reducer -> Store[state, state]

              |-------<------View--<-|

redux 優缺點
缺點:

  • 使用流程複雜。
  • state 不會隨著元件銷燬,狀態殘留。
  • 頻繁更新 store 時,會更加卡頓。
  • 不支援 ts。

mobx

原理是利用 es6 proxy 資料劫持,不會想 redux 使用複雜。

React Fiber

超過 16 毫秒後會掉幀。

原來的 React 是通過利用js的執行棧,一直執行到棧空位置。新版的React 維護了自己的執行棧,通過連結串列的形式,遍歷樹形結構,優化了執行過程。

參考

虛擬 DOM 和 diff演算法

虛擬 DOM
React 渲染 dom 的一種優化手段,其原理是利用 createFragment ,即“創造碎片”。所有的 DOM 操作都將在“碎片”中操作,直到操作完成,再一起渲染。

diff 演算法
是 React 更新 dom 元素的一套演算法,其核心思想是,比較“節點樹”的各個節點,根據比較的結果來決定是否更新該節點。通過其優化手段,將更新樹的時間複雜度從O(n^3)變為O(n)。

diff 策略

  • 忽略跨元件的移動操作
  • 同類元件建立相同樹,不同類則建立不同類的數(React Component 和 function Component)
  • 兄弟,key

分為3個更新策略 tree diff, component diff, element diff:

tree diff: 當遇到樹形結構更新時,僅比較同層節點,起子節點僅有建立和刪除。這就意味這,如果存在移動某個樹形結構的中間節點,那麼原樹將直接刪除該節點及其子節點,在新樹中建立起節點及其子節點。另外,官方不建議存在跨節點的移動操作。

component diff: 當節點更新為不同型別的節點時,成為 dirty component,react 認為更新成不同型別的節點,其結構一定是不一樣的(樹形結構),因此直接刪除該節點後,直接建立。

element diff: 計算兄弟節點直接的移動操作,由原來的將目標位節點刪除再建立的更新策略,更改為通過標識key,判斷是為原來的節點,另外也獲得了是否產生移動操作的判斷依據。

由此也得出了3條優化建議:

  • 設定 key
  • 不要將元件更新為不同型別
  • 減少將兄弟元件從末尾移至頭部的操作

參考

Redux

待補充

React 優化

常見的優化細節:

  • function 元件代替 class 元件 -> 為什麼?參考1 參考2
  • HOC
  • 使用 redux 這類狀態管理工具時,部分不公用的state 不用掛載在 model 中 --> 為什麼?
  • 首屏優化方案
  • lazy 懶載入元件
  • react-router 的 loading 改善體驗
  • ssr (service side rendering)和 csr (client side rendering)
  • pureComponent 和 shouldUpdate 優化更新頻率
  • componentDidCatch 探測錯誤邊界
  • 通過 Fragment 減少標籤深度
  • render 函式中的變數宣告提升到外面,減少 GC

React-Hooks

useState 相當於 class component 中的 setState。


function render() {
  ReactDom.render(<App />, document.getElementById('root'))
}
function myState(initState) {
  let state = initState
  const update = (newState) => {
    state = newState
    render()
    
  }
  return [state, update]
}
function App () {}

useEffect 相當於 class component 中的若干宣告周期函式

為什麼16.8加入Hook?或者為什麼增加 function component?

  • class component 複用狀態會很難。HOC 雖然解決了,但是有巢狀地獄的問題。
  • class component 宣告週期如果存在更改,其他生命週期可能均需要更改。
  • es6 的 class 不如 function 對初學者更友好
  • this 的指向

官方回答

站在設計者的角度來思考。

SSR

next 主要是用在 React。

npx create-next-app

react-router

根據 history 開發的無重新整理路由器。
主要有3個型別:hashHistory、borwerHistory、memoryHistory,常用的是 hashHistory 和 browerHistory。
新版本是 react-router-dom,

import React from "react";
import { Router } from "react-router";
import { createBrowserHistory as createHistory } from "history";
import PropTypes from "prop-types";
import warning from "tiny-warning";

/**
 * The public API for a <Router> that uses HTML5 history.
 */
class BrowserRouter extends React.Component {
  history = createHistory(this.props);

  render() {
    return <Router history={this.history} children={this.props.children} />;
  }
}

export default BrowserRouter;

區別

browerHistory 與 hashHistory 區別

hash 路由是是根據改變 # 後面的錨點來重新整理,通過 window.onhashChange 監聽變化,從而根據路由處找到相應的檔案
history 是通過 html5 的 window.history 這個 web API 來實現

hashHistory的特點:

  • 使用的是 history.location.hash 即 history.location.hash = "abc" (相當於是 localhost/#abc)
  • 只能通過修改 # 後面的地址實現重新整理
  • 通過 window.onhashchange 監聽 hash 變化,window.addEventListener('onhashchange', hashchangeHandler)
  • 不能想 window.history.go(-1) 這種方式,只能通過字串改變 url
  • 對搜尋引擎不友好,且不好追蹤

browerHistory的特點:

  • 使用的是 History 物件,提供 go, back, forward 的方法
  • 相同的 url 會發生更新,並且壓入歷史記錄中
  • 通過 push 和 replace 方法,實際是通過 pushState,replaceState 實現無重新整理跳轉。其中 pushState 會壓入瀏覽器歷史棧,即 History.length + 1,replaceState 則不會。

問題

使用 history 更新路由且正常渲染後,再重新整理,會引起404。(待驗證)
是由於 history 更新了 url,也就是訪問地址,此時向後端伺服器請求時,如果沒有匹配地址的資源則會 404。
例如 nginx 配置 /test 地址訪問。通過 history router 從 /test 跳轉至 /say ,重新整理時,伺服器將找不到資源,可以通過配置父級路徑規避掉。

window.location 也會更改 url 但是會引起重新整理

相關文章