顯示具有 react-router 標籤的文章。 顯示所有文章
顯示具有 react-router 標籤的文章。 顯示所有文章

2023年9月13日 星期三

react-router一般加載與懶加載

 在原先規劃系統設計是去讀src/pages/裡面有index.jsx?的頁面,搞得跟next.js類似

事實上這樣也不難,在vite.config.js這樣寫就好

import { defineConfig } from "vite"
import { GlobSync } from "glob"

export default defineConfig(({ mode }) => {
return {
......
define: {
__PAGES_ROUTES__: GlobSync("src/pages/**/index.{js,jsx}").found.map((x) => x.replace(/src\/pages\/(.*)\/index.jsx?/, "$1")),
},
}
})


 import loadable from "@loadable/component"
使用loadable進行懶加載
 ......
        __PAGES_ROUTES__.map((route, i) => {
const Component = loadable(() => import(`./pages/${route}`), {
     fallback: <LayoutPage center={() => ""} />,
})
return <Route path={route} key={i} element={<Component />} />
})

但今天收到一個難題需求,老闆希望把懶加載拔掉,減短切換頁的等待時間,因為不可能把Component在後端讀好傳到前端來,所以上面那方法就作廢,但總不會要我把上百個Component import進來吧,後來想到vite有附glob給我,稍微研究一下改成以下

const modules = import.meta.glob("./pages/**/index.{js,jsx}", {
import: "default",
eager: true,
})
......
    Object.keys(modules).map((route, i) => {
const path = route.replace(/\.\/pages\/(.*)\/index.jsx?/, "$1")
const Component = modules[route]
return <Route path={path} key={i} element={<Component />} />
})}

這樣改唯一的問題是,yarn dev第一次進入頁面等待時間會比較久一點,之後改動要等編譯時間也會久一點。當然在build的客戶端是沒有差的,是開發上的爽度差別而已
補充:用判斷讓import.meta.glob不要執行也沒用,他是屬於編譯層級的,所以一定會跑,要快只能註解掉
然後不能有在./pages/...../index.js是沒有export default的喔,不然會爆掉

總之就是這樣拉,打完收工

react-router動畫切換

 我挺訝異vue-router原生支援的動畫換頁效果,在react-router6竟然沒有支援,官網老實說也寫得不是很清楚,畢竟這版本目前來說還很新,也沒有別人寫好的套件可以直接使用,只好上網爬文怎麼實現

基本上就是參考了這個網站(https://dev.to/fazliddin04/react-router-v6-animated-transitions-diy-3e6l)把最終效果實踐出來

正文開始

// src/style/route-animate.module.scss
.fadeIn {
animation: 0.4s fadeIn forwards;
}

.fadeOut {
animation: 0.2s fadeOut forwards;
}

@keyframes fadeIn {
from {
opacity: 0.6;
transform: translate(-3rem, 0px);
}
to {
opacity: 1;
transform: translate(0px, 0px);
}
}

@keyframes fadeOut {
from {
opacity: 1;
transform: translate(0px, 0px);
}
to {
opacity: 0;
transform: translate(-3rem, 0px);
}
}

// src/App.jsx
import route_styles from "./style/route-animate.module.scss"
......
const RoutesWithAnimate = () => {
const animateEnum = {
fadeIn: "fadeIn",
fadeOut: "fadeOut",
}
const location = useLocation()
const [displayLocation, setDisplayLocation] = useState(location)
const [transitionStage, setTransistionStage] = useState(animateEnum.fadeIn)
useEffect(() => {
    // 防止有變化query的頁面但route沒有變化
if (location.pathname !== displayLocation.pathname) {
setTransistionStage(animateEnum.fadeOut)
} else if (location !== displayLocation) {
setDisplayLocation(location)
}
}, [location, displayLocation])


return (
<div
className={classNames("min-h-full", route_styles[transitionStage])}
onAnimationEnd={() => {
if (transitionStage === animateEnum.fadeOut) {
setTransistionStage(animateEnum.fadeIn)
setDisplayLocation(location)
}
}}
>
<Routes location={displayLocation}>
<Route path={"/home"} element={<Home />} />
        <Route path={"/about"} element={<About />} />
<Route path="*" element={<Navigate to="/home" replace />} />
</Routes>
</div>
)
}

藉由把location暫存放入Routes裡面,利用時間差做到動畫效果,但終究沒辦法同時存在兩個location,所以沒辦法做到同時兩個route畫面出現在畫面上的效果,類似輪動這樣,這可能需要靠htmlToCanvas達成類似的效果或是等其他人出套件了,總之專案做到這裡就被打掉了....沒錯你沒看錯,被打掉了,因為有人覺得淡入淡出很醜,好吧就這樣吧,做個紀錄,紀錄自己曾經做過。

踩過的小坑
if (location !== displayLocation)

參考的網站判斷原來長這樣,但是這會造成一個問題,如果只是query切換沒有切換pathname,然後畫面中假設彈窗顯示,然後彈窗會去取query的值,這時因為Routes的location還沒有轉換,所以取到的query會是舊的。

例如
router變化:/home?id=1 => /home?id=2
希望達成效果:/home?id=1 => 開始淡出 => 彈窗抓取qeury: id = 2 => 開始淡入
實際執行狀況:/home?id=1 => 開始淡出 => 彈窗抓取qeury: id = 1 => 淡出跑完(setDisplayLocation) => /home?id=2 => 開始淡入 => 淡入跑完

先不要管為什麼系統這樣設計,總之~就是這樣拉~