最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

背景

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

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

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

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

需求概覽

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

先看一下前后效果對(duì)比。

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

仿原生切換效果

技術(shù)棧

實(shí)現(xiàn)步驟

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

配置路由

// router.tsx
// ...

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

export default router;

每個(gè)頁面都是 BaseLayout 的子節(jié)點(diǎn)。

// BaseLayout.tsx

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

在項(xiàng)目的入口添加 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

添加過渡動(dòng)畫

過渡動(dòng)畫使用 react-transition-group 庫。

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

修改 router.tsx。

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

// 所有的路由都在這里配置
const defaultRouters: RouteObject[] = [
  {
    index: true,
    path: "/",
    element: <Home />,
  },
  // 登錄注冊(cè)頁面
  {
    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;

因?yàn)?react-transition-group 是結(jié)合著 css-transition 一起使用的,使用 CSSTransition 組件,它會(huì)自動(dòng)地在頁面過渡時(shí),給節(jié)點(diǎn)加上:

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

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

// style.less
/* 路由前進(jìn)時(shí)的入場/離場動(dòng)畫 */
.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;
}

/* 路由后退時(shí)的入場/離場動(dòng)畫 */
.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 一樣的切換效果就都實(shí)現(xiàn)了。

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

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

react-transition-group: React Transition Group

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

虛擬任務(wù)棧是使用 react-activation 包來實(shí)現(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;

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

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

封裝一個(gè) 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é)點(diǎn)緩存
    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)前頁面就會(huì)從緩存中被清理掉,再將進(jìn)入頁面時(shí),會(huì)重新走 useEffect 等生命周期。

注意

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

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

相關(guān)文章

  • React學(xué)習(xí)筆記之高階組件應(yīng)用

    React學(xué)習(xí)筆記之高階組件應(yīng)用

    這篇文章主要介紹了React 高階組件應(yīng)用,詳細(xì)的介紹了什么是React高階組件和具體使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-06-06
  • react中的ajax封裝實(shí)例詳解

    react中的ajax封裝實(shí)例詳解

    這篇文章主要介紹了react中的ajax封裝實(shí)例詳解的相關(guān)資料,希望通過本文能幫助到大家,讓大家理解掌握這部分內(nèi)容,需要的朋友可以參考下
    2017-10-10
  • VSCode 配置React Native開發(fā)環(huán)境的方法

    VSCode 配置React Native開發(fā)環(huán)境的方法

    本篇文章主要介紹了VSCode 配置React Native開發(fā)環(huán)境的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-12-12
  • 為什么說form元素是React的未來

    為什么說form元素是React的未來

    這篇文章主要介紹了為什么說form元素是React的未來,本文會(huì)帶你聊聊React圍繞form的布局與發(fā)展,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • hooks中useEffect()使用案例詳解

    hooks中useEffect()使用案例詳解

    這篇文章主要介紹了hooks中useEffect()使用總結(jié),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-09-09
  • react自定義實(shí)現(xiàn)狀態(tài)管理詳解

    react自定義實(shí)現(xiàn)狀態(tài)管理詳解

    這篇文章主要為大家詳細(xì)介紹了react如何自定義實(shí)現(xiàn)狀態(tài)管理,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-01-01
  • React事件監(jiān)聽和State狀態(tài)修改方式

    React事件監(jiān)聽和State狀態(tài)修改方式

    這篇文章主要介紹了React事件監(jiān)聽和State狀態(tài)修改方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • React 條件判斷實(shí)例詳解

    React 條件判斷實(shí)例詳解

    在 React 中,可以通過 JavaScript 的條件語句來動(dòng)態(tài)渲染組件或元素,下面給大家分享幾種常用的在 React 中處理?xiàng)l件渲染的方法,感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • 一文詳解手動(dòng)實(shí)現(xiàn)Recoil狀態(tài)管理基本原理

    一文詳解手動(dòng)實(shí)現(xiàn)Recoil狀態(tài)管理基本原理

    這篇文章主要為大家介紹了一文詳解手動(dòng)實(shí)現(xiàn)Recoil狀態(tài)管理基本原理實(shí)例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • react基礎(chǔ)知識(shí)總結(jié)

    react基礎(chǔ)知識(shí)總結(jié)

    這篇文章主要介紹了react常用的基礎(chǔ)知識(shí)總結(jié),幫助大家更好的理解和學(xué)習(xí)使用react框架,感興趣的朋友可以了解下
    2021-04-04

最新評(píng)論

镇沅| 枞阳县| 松阳县| 金平| 静海县| 清镇市| 旌德县| 内江市| 西盟| 逊克县| 河东区| 驻马店市| 济宁市| 杂多县| 开平市| 会同县| 永川市| 赞皇县| 陇南市| 堆龙德庆县| 余庆县| 墨玉县| 舞钢市| 和硕县| 长海县| 娄底市| 宁都县| 会泽县| 静海县| 怀仁县| 大名县| 玉树县| 富顺县| 克山县| 安多县| 姚安县| 浮山县| 中江县| 海城市| 昌邑市| 安平县|