如今稍微大型的站點都會採用H5/PC
端 並行,經過nignx
獲取瀏覽器的UA
信息來切換站點。前端
但這對於一些企業站點或人手不足的小型項目來講,就很難實現。react
經過CSS
媒體查詢實現響應式佈局,是主流方式。git
可是,有時在React程序中,須要根據屏幕大小有條件地渲染不一樣的組件(寫媒體查詢太麻煩了,還不如另寫組件),其實使用React Hooks
,能夠更靈活實現。github
本文的實現來自:瀏覽器
innerWidth
一個很簡單粗略的方案,是個前端都知道:微信
const MyComponent = () => {
// 當前窗口寬度
const width = window.innerWidth;
// 鄰介值
const breakpoint = 620;
// 寬度小於620時渲染手機組件,反之桌面組件
return width < breakpoint ? <MobileComponent /> : <DesktopComponent />;
}
複製代碼
這個簡單的解決方案確定會起做用。根據用戶設備的窗口寬度,咱們能夠呈現桌面視圖或手機視圖。ide
可是,當調整窗口大小時,未解決寬度值的更新問題,可能會渲染錯誤的組件。函數
Hooks
+resize
說着也簡單,監聽resize
事件時,觸發useEffect
改變數據。佈局
const MyComponent = () => {
const [width, setWidth] = React.useState(window.innerWidth);
const breakpoint = 620;
React.useEffect(() => {
window.addEventListener("resize", () => setWidth(window.innerWidth));
}, []);
return width < breakpoint ? <MobileComponent /> : <DesktopComponent />;
}
複製代碼
但精通Hooks
的你,必定知道這裏存在內存性能消耗問題:resize
事件沒移除!
優化版本:
const useViewport = () => {
const [width, setWidth] = React.useState(window.innerWidth);
React.useEffect(() => {
const handleWindowResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", handleWindowResize);
return () => window.removeEventListener("resize", handleWindowResize);
}, []);
return { width };
}
複製代碼
useViewport
自定義React Hooks
,能夠將組件/函數最大程度的複用。構建一個也很簡單:
const useViewport = () => {
const [width, setWidth] = React.useState(window.innerWidth);
React.useEffect(() => {
const handleWindowResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", handleWindowResize);
return () => window.removeEventListener("resize", handleWindowResize);
}, []);
return { width };
}
複製代碼
精簡後的組件代碼:
const MyComponent = () => {
const { width } = useViewport();
const breakpoint = 620;
return width < breakpoint ? <MobileComponent /> : <DesktopComponent />;
}
複製代碼
可是這裏還有另外一個性能問題:
響應式佈局影響的是多個組件,若是在多處使用useViewport
,這將浪費性能。
這時就須要另外一個React
親兒子:React Context(上下文)
來幫忙。
Hooks
+Context
咱們將建立一個新的文件viewportContext
,在其中能夠存儲當前視口大小的狀態以及計算邏輯。
const viewportContext = React.createContext({});
const ViewportProvider = ({ children }) => {
// 順帶監聽下高度,備用
const [width, setWidth] = React.useState(window.innerWidth);
const [height, setHeight] = React.useState(window.innerHeight);
const handleWindowResize = () => {
setWidth(window.innerWidth);
setHeight(window.innerHeight);
}
React.useEffect(() => {
window.addEventListener("resize", handleWindowResize);
return () => window.removeEventListener("resize", handleWindowResize);
}, []);
return (
<viewportContext.Provider value={{ width, height }}>
{children}
</viewportContext.Provider>
);
};
const useViewport = () => {
const { width, height } = React.useContext(viewportContext);
return { width, height };
}
複製代碼
緊接着,你須要在React
根節點,確保已經包裹住了App
:
const App = () => {
return (
<ViewportProvider>
<AppComponent />
</ViewportProvider>
);
}
複製代碼
在日後的每次useViewport()
,其實都只是共享Hooks
。
const MyComponent = () => {
const { width } = useViewport();
const breakpoint = 620;
return width < breakpoint ? <MobileComponent /> : <DesktopComponent />;
}
複製代碼
github
上面的響應式佈局hooks
,都是大同小異的實現方式。
本文除了介紹React Hooks
的響應式佈局實現,還介紹瞭如何自定義hooks
與使用Context
上下文,來複用,以達到性能最佳優化。
若是你以爲這篇內容對你挺有啓發,我想邀請你幫我三個小忙:
勸退師我的微信:huab119
也能夠來個人GitHub
博客裏拿全部文章的源文件:
前端勸退指南:github.com/roger-hiro/… 一塊兒玩耍呀。~