認識react
- JSX 并不是一個新的模板語言,而可以認為是一個語法糖。
- React.createElement 作用就是創建一個組件的實例;參數:
- 第一個參數表示組件的類型;
- 第二個參數是傳給組件的屬性,也就是 props
- 第三個以及后續所有的參數則是子組件。
React.createElement(
"div",
null,
React.createElement(
"button",
{ onClick: function onClick() {
return setCount(count + 1);
} },
React.createElement(CountLabel, { count: count })
)
);
理解hooks
- hooks的好處:簡化了邏輯復用。
如果用class組件實現邏輯復用需要封裝高階組件;有以下缺點:
- 代碼難理解,不直觀,很多人甚至寧愿重復代碼,也不愿用高階組件;
- 會增加很多額外的組件節點。每一個高階組件都會多一層節點,這就會給調試帶來很大的負擔。
- 在 Class 組件中,代碼是從技術角度組織在一起的,例如在 componentDidMount 中都去做一些初始化的事情。而在函數組件中,代碼是從業務角度組織在一起的,相關代碼能夠出現在集中的地方,從而更容易理解和維護。
內置 Hooks
- React 提供的 Hooks 其實非常少,一共只有 10 個,比如 useState、useEffect、useCallback、useMemo、useRef、useContext 等等。
- 副作用是指一段和當前執行結果無關的代碼。
- useEffect 讓我們能夠在下面四種時機去執行一個回調函數產生副作用:
- 每次 render 后執行:不提供第二個依賴項參數。比如useEffect(() => {})。
- 僅第一次 render 后執行:提供一個空數組作為依賴項。比如useEffect(() => {}, [])。
- 第一次以及依賴項發生變化后執行:提供依賴項數組。比如useEffect(() => {}, [deps])。
- 組件 unmount 后執行:返回一個回調函數。比如useEffect() => { return () => {} }, [])。
- Hooks 的使用規則包括以下兩個:
- 只能在函數組件的頂級作用域使用;
- 只能在函數組件或者其他 Hooks 中使用。
- useCallback解決事件處理函數的問題:
- 不僅增加了系統的開銷
- 每次創建新函數的方式會讓接收事件處理函數的組件,需要重新渲染。
- useCallback和useMemo做了同一件事情:建立了一個綁定某個結果到依賴數據的關系。只有當依賴變了,這個結果才需要被重新得到
用useMemo實現useCallback:
const myEventHandler = useMemo(() => {
// 返回一個函數作為緩存結果
return () => {
// 在這里進行事件處理
}
}, [dep1, dep2]);
問題:useCallback/useMemo 什么情況下使用?是所有情況?還是個別情況,例如計算量較大等情況?
- useRef:
- 在多次渲染之間共享數據
使用 useRef 保存的數據一般是和 UI 的渲染無關的,因此當 ref 的值發生變化時,是不會觸發組件的重新渲染的,這也是 useRef 區別于 useState 的地方。
- 保存某個 DOM 節點的引用
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// current 屬性指向了真實的 input 這個 DOM 節點,從而可以調用 focus 方法
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
ref 這個屬性提供了獲得 DOM 節點的能力,并利用 useRef 保存了這個節點的應用。這樣的話,一旦 input 節點被渲染到界面上,那我們通過 inputEl.current 就能訪問到真實的 DOM 節點的實例了。
- useContext 定義全局狀態
和全局的變量去保存數據的區別?
就是為了能夠進行數據的綁定。當這個 Context 的數據發生變化時,使用這個數據的組件就能夠自動刷新。但如果沒有 Context,而是使用一個簡單的全局變量,就很難去實現了。
- useContext 的缺點:
- 會讓調試變得困難,因為你很難跟蹤某個 Context 的變化究竟是如何產生的。
- 讓組件的復用變得困難,因為一個組件如果使用了某個 Context,它就必須確保被用到的地方一定有這個 Context 的 Provider 在其父組件的路徑上。
所以在 React 的開發中,除了像 Theme、Language 等一目了然的需要全局設置的變量外,我們很少會使用 Context 來做太多數據的共享。需要再三強調的是,Context 更多的是提供了一個強大的機制,讓 React 應用具備定義全局的響應式數據的能力。
自定義hooks的使用場景:
- 抽取業務邏輯;
- 封裝通用邏輯;
- 監聽瀏覽器狀態;
- 拆分復雜組件。
全局狀態管理:如何在函數組件中使用 Redux?:
- 理解 Redux 的三個基本概念:State、Action 和 Reducer。
- 其中 State 即 Store,一般就是一個純 JavaScript Object。
- Action 也是一個 Object,用于描述發生的動作。
- 而 Reducer 則是一個函數,接收 Action 和 State 并作為參數,通過計算得到新的 Store。
好處:1. 可預測性(Predictable):即給定一個初始狀態和一系列的 Action,一定能得到一致的結果,同時這也讓代碼更容易測試。2. 易于調試:可以跟蹤 Store 中數據的變化,甚至暫停和回放。因為每次 Action 產生的變化都會產生新的對象,而我們可以緩存這些對象用于調試。Redux 的基于瀏覽器插件的開發工具就是基于這個機制,非常有利于調試。
- React 和 Redux 共同使用時的單向數據流:
- 什么是異步action?Middleware 在 Action 真正到達 Reducer 之前提供的一個額外處理 Action 的機會:
Redux 中的 Action 不僅僅可以是一個 Object,它可以是任何東西,也可以是一個函數。利用這個機制,Redux 提供了 redux-thunk 這樣一個中間件,它如果發現接受到的 action 是一個函數,那么就不會傳遞給 Reducer,而是執行這個函數,并把 dispatch 作為參數傳給這個函數,從而在這個函數中你可以自由決定何時,如何發送 Action。
復雜狀態處理:如何保證狀態一致性?
- 一個是狀態最小化原則,也就是說要避免冗余的狀態;
- 另一個則是唯一數據源原則,避免中間狀態。
函數組件設計模式:如何應對復雜條件渲染場景?
- 容器模式:實現按條件執行 Hooks
- 把條件判斷的結果放到兩個組件之中,確保真正 render UI 的組件收到的所有屬性都是有值的。
- 還有一種做法,就是把判斷條件放到 Hooks 中去。
-
使用 render props 模式重用 UI 邏輯
邏輯復用,自定義hook亦可
事件處理:如何創建自定義事件?
- React 原生事件的原理:合成事件(Synthetic Events)
由于虛擬 DOM 的存在,在 React 中即使綁定一個事件到原生的 DOM 節點,事件也并不是綁定在對應的節點上,而是所有的事件都是綁定在根節點上。然后由 React 統一監聽和管理,獲取事件后再分發到具體的虛擬 DOM 節點上。
在 React 17 之前,所有的事件都是綁定在 document 上的,而從 React 17 開始,所有的事件都綁定在整個 App 上的根節點上,這主要是為了以后頁面上可能存在多版本 React 的考慮。
-
React 這么做的原因主要有兩個:
- 虛擬 DOM render 的時候, DOM 很可能還沒有真實地 render 到頁面上,所以無法綁定事件。
- React 可以屏蔽底層事件的細節,避免瀏覽器的兼容性問題。同時呢,對于 React Native 這種不是通過瀏覽器 render 的運行時,也能提供一致的 API。
- 由于瀏覽器事件的冒泡模型。無論事件在哪個節點被觸發, React 都可以通過事件的 srcElement這個屬性,知道它是從哪個節點開始發出的,這樣 React 就可以收集管理所有的事件,然后再以一致的 API 暴露出來。
- 雖然自定義事件和原生事件看上去類似,但是兩者的機制是完全不一樣的:
- 原生事件是瀏覽器的機制;
- 而自定義事件則是純粹的組件自己的行為,本質是一種回調函數機制。
路由管理:為什么每一個前端應用都需要使用路由機制?
-
React Router 管理
react router示例
這里需要注意,React Router 不僅支持瀏覽器,還支持 React Native,以及一些用 Web 實現的移動 App,所以它提供了多個 npm 模塊。
- BrowserRouter、Link、Route、Switch 等組件的用法及作用。
- BrowserRouter:表示用標準的 URL 路徑去管理路由,比如 /my-page1 這樣的標準 URL 路徑。除此之外,還有 MemoryRouter,表示通過內存管理路由;HashRouter,標識通過 hash 管理路由。我們自己實現的例子其實就是用的 hash 來實現路由。
- Link:定義一個導航鏈接,點擊時可以無刷新地改變頁面 URL,從而實現 React Router 控制的導航。
- Route: 定義一條路由規則,可以指定匹配的路徑、要渲染的內容等等。
- Switch:在默認情況下,所有匹配的 Route 節點都會被展示,但是 Switch 標記可以保證只有第一個匹配到的路由才會被渲染。
- 使用嵌套路由:實現二級導航頁面所謂嵌套路由,也稱為子路由,就是一個頁面組件內部,還需要通過 URL 上的信息來決定組件內部某個區域該如何顯示。嵌套路由示例
- 在 URL 中保存頁面狀態
一方面可以提升用戶體驗,另一方面也可以簡化頁面之間的交互。
- 路由層面實現權限控制
我們完全可以利用前端路由的動態特性。你已經看到了,路由是通過 JSX 以聲明式的方式去定義的,這就意味著路由的定義規則是可以根據條件進行變化的,也就是所謂的動態路由。
jsx聲明routes
按需加載:如何提升應用打開速度?
- 如何實現按需加載?
使用 import 語句,定義按需加載的起始模塊語法是 import(someModule)。
webpack分包
react-loadable按需加載
- 使用 service worker 緩存前端資源
- 緩存永遠不過期。你只要下載過一次,就永遠不需要再重新下載,除非主動刪除。
- 永遠不會訪問過期的資源。換句話說,如果發布了一個新版本,那么你可以通過版本化的一些機制,來確保用戶訪問到的一定是最新的資源。
1.注冊 Service Worker
注冊service worker
在 Service Worker 安裝之后初始化緩存機制
安裝- 攔截請求
攔截請求
參考鏈接:cache mdn