next訓練營 | 10. 路由守衛 | 實現全域性loading
theme: cyanosis highlight: atom-one-dark
持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第3天,點選檢視活動詳情
王志遠,微醫前端技術部
前言
路由切換的控制時機很關鍵,我們用一個【切換路由頁面未載入完成時顯示 loading】的效果來學習下如何在 next 中使用路由守衛。效果如下
開始實現
實現思路
- 全域性元件例項上存放 loading 開關屬性,並實現在 loading 為 true 時顯示載入 gif,為 false 時才展示內容
- 元件掛載時註冊路由守衛事件:路由開始變化時開啟 loading,路由結束變化時關閉 loading
- 元件解除安裝時關閉路由守衛事件
具體實現
以下修改均在_app.tsx
中實現
引入路由
import router from "next/router";
全域性元件例項上存放 loading 開關屬性
tsx
routeChangeStart: any; // 路由開始變化時事件
routeChangeComplete: any; // 路由結束變化時事件
state = {
loading: false,
};
元件掛載時註冊
tsx
componentDidMount() {
this.routeChangeStart = (url) => {
this.setState({ loading: true });
};
this.routeChangeComplete = (url) => {
this.setState({ loading: false });
};
router.events.on("routeChangeStart", this.routeChangeStart);
router.events.on("routeChangeComplete", this.routeChangeComplete);
}
元件解除安裝時
tsx
componentWillUnmount() {
router.events.off("routeChangeStart", this.routeChangeStart);
router.events.off("routeChangeComplete", this.routeChangeComplete);
}
最終的_app.tsx
```tsx
import App, { Container } from "next/app";
import Link from "next/link";
import { Layout, Menu, Icon, Avatar, Spin } from "antd";
import router from "next/router";
import "antd/dist/antd.css";
import { withRouter } from "next/router";
const { Header, Footer } = Layout;
import * as TYEPS from "../store/action-types";
import axios from "../utils/axios";
import createStore from "../store";
import { Provider } from "react-redux";
const REDUX_STORE = "REDUX_STORE";
function getStore(initialState) {
if (typeof window == "undefined") {
//如果 在伺服器端執行的。那麼直接建立新倉庫返回
return createStore(initialState);
} else {
//如果此程式碼是在客戶端執行的,第一次會建立,以後每次都複用上一次建立的
if (!window[REDUX_STORE]) {
window[REDUX_STORE] = createStore(initialState);
}
return window[REDUX_STORE];
}
}
class LayoutApp extends App
//此建構函式只會在客戶端執行一次 這裡的 initialState 是 getInitialProps 返回的
this.store = getStore(props.initialState);
}
// 在頁面級別渲染時只會被執行一次,即服務端渲染或每次切換客戶端渲染都會被執行,但服務端渲染時客戶端不會執行
static async getInitialProps({ Component, ctx }) {
let store = getStore({});
let pageProps = {};
console.log("2. getInitialProps");
let options: any = {
url: "/api/currentUser",
};
//如果此方法是在伺服器執行的,那麼會有 ctx.req 屬性,它代表本次 node 請求物件
if (ctx.req && ctx.req.headers.cookie) {
options.headers = options.headers || {};
options.headers.cookie = ctx.req.headers.cookie;
}
let response = await axios(options);
if (response.data.code === 0) {
// 當前登入的使用者
let currentUser = response.data.data;
store.dispatch({ type: TYEPS.SET_USER_INFO, payload: currentUser });
}
if (Component.getInitialProps) {
// 執行當前頁面的 getInitialProps
let data = await Component.getInitialProps(ctx);
pageProps = { ...data };
}
return { pageProps, initialState: store.getState() };
}
componentDidMount() {
this.routeChangeStart = (url) => {
this.setState({ loading: true });
};
this.routeChangeComplete = (url) => {
this.setState({ loading: false });
};
router.events.on("routeChangeStart", this.routeChangeStart);
router.events.on("routeChangeComplete", this.routeChangeComplete);
}
componentWillUnmount() {
router.events.off("routeChangeStart", this.routeChangeStart);
router.events.off("routeChangeComplete", this.routeChangeComplete);
}
render() {
console.log("3.LayoutApp.render");
let { Component, pageProps } = this.props as any;
let { currentUser } = this.store.getState();
let pathname = this.props.router.pathname;
pathname = "/" + pathname.split("/")[1];
return (