欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React實現(xiàn)原生APP切換效果

 更新時間:2025年01月07日 09:11:59   作者:meikeluckly  
最近需要使用 Hybrid 的方式開發(fā)一 個 APP,交互和原生 APP 相似并且需要 IM 通信,本文給大家介紹了使用React實現(xiàn)原生APP切換效果,文中通過代碼示例講解的非常詳細(xì),感興趣的小伙伴跟著小編一起來看看吧

背景

最近需要使用 Hybrid 的方式開發(fā)一 個 APP,交互和原生 APP 相似并且需要 IM 通信。根據(jù)目前公司已有的實現(xiàn)方案,每次進入一個新的頁面時,可以調(diào)用 Native 提供的 createWebview 方法重新創(chuàng)建一個 Webview,這樣在打開一個頁面時,就能像原生 APP 一樣實現(xiàn) pushpop 的效果。但新創(chuàng)建 Webview 后,之前 Webview 中的長鏈就會被掛起,IM 消息可能被中斷,這樣在功能使用上會存在著一些問題。

經(jīng)過討論,有想到兩種可行的解決方案。

  • 長鏈下沉到 Native。我們調(diào)用 Native 提供的 API 來實現(xiàn) IM 的唯一。但它有個缺點,以后有調(diào)整,Native 也需要發(fā)版,并且不太好保證不同 WebviewIM 信息的一致性和及時性。
  • H5 實現(xiàn)一個原生 APP 的切換效果。讓系統(tǒng)成為一個單頁應(yīng)用。這樣長鏈就可以是唯一的并且不需要下沉到 Native 端。

最后決定嘗試使用第二種方案。

需求概覽

  • 頁面進入時從右向左推入、返回上一頁時頁面從左向右推出。
  • 進入下一頁時,下一頁的數(shù)據(jù)需要重新請求。
  • 返回上一頁時,保持上一頁的數(shù)據(jù)展示并且不用重新請求數(shù)據(jù)。

先看一下前后效果對比。

網(wǎng)頁默認(rèn)切換效果

仿原生切換效果

技術(shù)棧

實現(xiàn)步驟

根據(jù) react-router-dom 文檔配置好路由

配置路由

// router.tsx
// ...

const router = createBrowserRouter([
  {
    path: "/",
    element: <BaseLayout />,
    errorElement: <ErrorBoundary />,
    children: [
      {
        index: true,
        element: <Home />,
      },
      // 登錄注冊頁面
      {
        path: "/login",
        element: <Login />,
      },
    ],
  },
]);

export default router;

每個頁面都是 BaseLayout 的子節(jié)點。

// BaseLayout.tsx

function BaseLayout() {
  // ...
  return <Outlet />;
}
export default BaseLayout;

在項目的入口添加 RouterProvider

import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider } from "react-router-dom";
import router from "@/router";
import "@/assets/styles/global.less";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
     {/* router */}
     <RouterProvider router={router} />
  </React.StrictMode>
);

配置路由:createBrowserRouter | React Router API Reference

添加過渡動畫

過渡動畫使用 react-transition-group 庫。

react-transition-group 組合著 react-router-dom v6 使用時,需要給每個 rouer 都添加一個 nodeRef(跟著官方文檔 demo 來)。

修改 router.tsx

  • 將路由展平并 export 出去,方面在其他頁面調(diào)用。
  • 給導(dǎo)出去的 newRouter 每個路由都添加一個 nodeRef。
// router.tsx
// ...

// 所有的路由都在這里配置
const defaultRouters: RouteObject[] = [
  {
    index: true,
    path: "/",
    element: <Home />,
  },
  // 登錄注冊頁面
  {
    path: "/login",
    element: <Login />,
  }
];

/**
 * 將路由展平,并添加 nodeRef 字段
 * @param routerParams RouteObject[]
 * @returns RouteObject[]
 */
function flatRouters(routerParams: RouteObject[]) {
  let newRouters: Array<RouteObject & { nodeRef: RefObject<any> }> = [];
  routerParams.forEach((router) => {
    newRouters.push({
      ...router,
      nodeRef: createRef(),
    });
    if (router.children?.length) {
      newRouters = newRouters.concat(flatRouters(router.children));
    }
  });
  return newRouters;
}

const newRouters = flatRouters(defaultRouters);

// react-router-dom 創(chuàng)建的路由
const router = createBrowserRouter([
  {
    path: "/",
    element: <BaseLayout />,
    errorElement: <ErrorBoundary />,
    children: defaultRouters,
  },
]);

export default router;

export { newRouters };

在 BaseLayout 中添加過渡。根據(jù) useNavigationType 獲取當(dāng)前頁面是 push 還是 pop 更改 CSSTransition 的 className。

// BaseLayout.tsx

import React, { useEffect } from "react";
import { useOutlet, useNavigationType } from "react-router-dom";
import { CSSTransition, TransitionGroup } from "react-transition-group";
import { newRouters } from "@/router";
import "./style.less";

const ANIMATION_MAP = {
  PUSH: "forward",
  POP: "back",
  REPLACE: "fade-route",
};

// 授權(quán)組件
function BaseLayout() {
  const currentOutlet = useOutlet();
  const navigateType = useNavigationType();
  const { nodeRef } =
    newRouters.find((route) => route.path === location.pathname) ?? {};
  const fullPath = `${location.pathname}${location.search}`;

  return (
    <TransitionGroup
      childFactory={(child) =>
        React.cloneElement(child, { classNames: ANIMATION_MAP[navigateType] })
      }
    >
      <CSSTransition
        key={location.pathname}
        nodeRef={nodeRef}
        timeout={500}
        unmountOnExit
      >
        {() => (
          <div ref={nodeRef}>
            {currentOutlet}
          </div>
        )}
      </CSSTransition>
    </TransitionGroup>
  );
}

export default BaseLayout;

因為 react-transition-group 是結(jié)合著 css-transition 一起使用的,使用 CSSTransition 組件,它會自動地在頁面過渡時,給節(jié)點加上:

  • *-enter
  • *-enter-active
  • *-enter-done
  • *-exit
  • *-exit-active
  • *-exit-done
  • ...

className,所以再添加一下對應(yīng)的 CSS 動畫效果,過渡的效果就實現(xiàn)了。

// style.less
/* 路由前進時的入場/離場動畫 */
.forward-enter {
  .base-layout;
  transform: translate3d(100vw, 0, 0);
  z-index: 2;
}

.forward-enter-active {
  .base-layout;
  transform: translate3d(0, 0, 0);
  transition: all 500ms;
  z-index: 2;
}

.forward-exit {
  .base-layout;
  transform: translate3d(0, 0, 0);
  z-index: 1;
}

.forward-exit-active {
  .base-layout;
  transform: translate3d(-100vw, 0, 0);
  transition: all 500ms;
  z-index: 1;
}

/* 路由后退時的入場/離場動畫 */
.back-enter {
  transform: translate3d(-100vw, 0, 0);
  z-index: 1;
}

.back-enter-active {
  .base-layout;
  transform: translate3d(0, 0, 0);
  transition: all 500ms ease-out;
  z-index: 1;
}

.back-exit {
  .base-layout;
  transform: translate3d(0, 0, 0);
  z-index: 2;
}

.back-exit-active {
  .base-layout;
  transform: translate3d(100vw, 0, 0);
  transition: all 500ms ease-out;
  z-index: 2;
}

到目前為止,和 Native 一樣的切換效果就都實現(xiàn)了。

Native 還有一個特點,只有進入下一頁時才會重新請求數(shù)據(jù),返回上一頁時,是直接展示之前的頁面,不需要再重新請求數(shù)據(jù)。

這個可以使用虛擬任務(wù)棧的方式來緩存頁面,以達到返回上一頁時,不需要重新請求并重新渲染頁面的效果。

react-transition-group: React Transition Group

使用虛擬任務(wù)棧緩存頁面

虛擬任務(wù)棧是使用 react-activation 包來實現(xiàn)的。

安裝好后,在 main.tsx 處使用 AliveScopeRouterProvider 包裹起來。

ReactDOM.createRoot(document.getElementById("root")!).render(
  <Provider store={store}>
    <AliveScope>
      {/* router */}
      <RouterProvider router={router} />
    </AliveScope>
  </Provider>
);

然后在 BaseLayout.tsx 處給子頁面用 KeepAlive 包裹起來。

// BaseLayout.tsx

// 授權(quán)組件
function BaseLayout() {
  // ...

  return (
    <TransitionGroup
      childFactory={(child) =>
        React.cloneElement(child, { classNames: ANIMATION_MAP[navigateType] })
      }
    >
      <CSSTransition
        key={location.pathname}
        nodeRef={nodeRef}
        timeout={500}
        unmountOnExit
      >
        {() => (
          <div ref={nodeRef}>
            <KeepAlive
              id={fullPath}
              saveScrollPosition="screen"
              name={fullPath}
            >
              {currentOutlet}
            </KeepAlive>
          </div>
        )}
      </CSSTransition>
    </TransitionGroup>
  );
}

export default BaseLayout;

這樣,所有訪問過的頁面都會被緩存起來。

返回上一頁時,我們需要清理掉當(dāng)前頁面的緩存,使頁面再次進入時,可以重新請求并渲染頁面。

封裝一個 useGoBack() 方法。

// useGoBack.tsx

import { useNavigate } from "react-router-dom";
import { useAliveController } from "react-activation";

// 頁面返回 hooks
const useGoBack = () => {
  const navigate = useNavigate();
  const { dropScope, getCachingNodes } = useAliveController();

  return (pageNum = -1) => {
    const allCachingNodes = getCachingNodes() || [];
    navigate(pageNum);
    // 清除 keepAlive 節(jié)點緩存
    const pageNumAbs = Math.abs(pageNum);
    const dropNodes = allCachingNodes.slice(
      allCachingNodes.length - pageNumAbs
    );
    dropNodes.forEach((node) => {
      dropScope(node.name!);
    });
  };
};

export default useGoBack;

使用 自定義 Hooks - useGoBack() 返回上一頁的頁面,當(dāng)前頁面就會從緩存中被清理掉,再將進入頁面時,會重新走 useEffect 等生命周期。

注意

  • 需要根據(jù) useNavigationType 獲取當(dāng)前頁面是 push 還是 pop 更改 CSSTransitionclassName。
  • CSSTransition 下面要緊挨著需要過渡的 div,KeepAlive 要放在這個 div 下面。
  • react-activation 需要配置 babel。
  • 返回上一頁時,一定要清理掉不需要的緩存頁面,以防止緩存頁面過多,頁面使用卡頓。
  • 要實現(xiàn)兩個頁面同時在頁面上展示并過渡,需要使用 TransitionGroup。

以上就是React實現(xiàn)原生APP切換效果的詳細(xì)內(nèi)容,更多關(guān)于React APP切換的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 淺談React多個setState會調(diào)用幾次

    淺談React多個setState會調(diào)用幾次

    在React的生命周期鉤子和合成事件中,多次執(zhí)行setState,會被調(diào)用幾次,本文就詳細(xì)的介紹一下,感興趣的可以了解一下
    2021-11-11
  • React項目打包發(fā)布到Tomcat頁面空白問題及解決

    React項目打包發(fā)布到Tomcat頁面空白問題及解決

    這篇文章主要介紹了React項目打包發(fā)布到Tomcat頁面空白問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • TS裝飾器bindThis優(yōu)雅實現(xiàn)React類組件中this綁定

    TS裝飾器bindThis優(yōu)雅實現(xiàn)React類組件中this綁定

    這篇文章主要為大家介紹了TS裝飾器bindThis優(yōu)雅實現(xiàn)React類組件中this綁定,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-11-11
  • 手挽手帶你學(xué)React之React-router4.x的使用

    手挽手帶你學(xué)React之React-router4.x的使用

    這篇文章主要介紹了手挽手帶你學(xué)React之React-router4.x的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-02-02
  • React內(nèi)部實現(xiàn)cache方法示例詳解

    React內(nèi)部實現(xiàn)cache方法示例詳解

    這篇文章主要為大家介紹了React內(nèi)部實現(xiàn)cache方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-11-11
  • 在 React 項目中使用 Auth0 并集成到后端服務(wù)的配置步驟詳解

    在 React 項目中使用 Auth0 并集成到后端服務(wù)的配置步驟詳解

    這篇文章主要介紹了在 React 項目中使用 Auth0 并集成到后端服務(wù)的配置步驟詳解,通過本文詳細(xì)步驟,您可以將 Auth0 集成到 React 項目并與后端服務(wù)交互,需要的朋友可以參考下
    2024-07-07
  • ReactDOM.render在react源碼中執(zhí)行原理

    ReactDOM.render在react源碼中執(zhí)行原理

    這篇文章主要為大家介紹了ReactDOM.render在react源碼中執(zhí)行原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • React-hook-form-mui基本使用教程(入門篇)

    React-hook-form-mui基本使用教程(入門篇)

    react-hook-form-mui可以幫助開發(fā)人員更輕松地構(gòu)建表單,它結(jié)合了React?Hook?Form和Material-UI組件庫,使用react-hook-form-mui,開發(fā)人員可以更快速地構(gòu)建表單,并且可以輕松地進行表單驗證和數(shù)據(jù)處理,本文介紹React-hook-form-mui基本使用,感興趣的朋友一起看看吧
    2024-02-02
  • React的三大屬性你都知道嗎

    React的三大屬性你都知道嗎

    這篇文章主要為大家詳細(xì)介紹了React的三大屬性,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • 解決React報錯Property?'value'?does?not?exist?on?type?EventTarget

    解決React報錯Property?'value'?does?not?exist?on?

    這篇文章主要為大家介紹了React報錯Property?'value'?does?not?exist?on?type?EventTarget的解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12

最新評論