摆了半年摊,好久没写代码了,今天有人问我怎么实现React-Router-dom类似标签页缓存。后面看了一下router的官网。很久以前用的是react-router v5那个比较容易实现。v6变化挺大,但了解react的机制和react-router的机制就容易了.
想做到切换标签保留页面的内容不变,就要了解react的机制
首先虚拟DOM的机制就是对比,如果找不到就会重新挂载,找到了就更新。
React-Router的机制就是匹配路径,找到了就返回对应的路由组件,找不到返回为null
思路就是v6版本提供了Outlet这个输出子路元素的组件
一般我们会这样写:
但如果要实现标签的话,就不能这样写。但是切换路由,地址一变它就替换了原来的了。所以要做的就是保留所有打开的标签的子路由元素:
主要目的就是保留所有的元素,隐藏路由就行,这样react diff时,还是会找到对应key的路由,这样它只是会更新路由的组件,而不会重新挂载。
如这样写:
新建一个html复制进去就可以运行了
- DOCTYPE html>
-
-
-
React-router-dom tabs -
-
-
-
-
-
-
-
-
-
-
-
- src="https://cdn.jsdelivr.net/npm/react-router-dom@6.17.0/dist/umd/react-router-dom.production.min.js">
-
-
- const { useCallback, useMemo, useEffect, useRef, useState } = React
- const { ProLayout } = ProComponents
- const { createHashRouter, useOutlet, Navigate, RouterProvider, useLocation, Link, useNavigate } = ReactRouterDOM
- const { Tabs, Button, Space, Row, Col, Input, } = antd
-
- const Home = () => {
- return Home
- }
- const DemoA = () => {
- return DemoA
- }
- const DemoB = () => {
- return DemoB
- }
- const ViewPage = (props) => {
- const outlet = useOutlet()
- return props.render(outlet)
- }
- const BasicLayout = () => {
- const nav = useNavigate()
- const location = useLocation()
- const route = routes[0]
- const [menuDataMap] = useState(() => new Map())
- const cacheOutletElements = useRef({}).current
- const [tabActiveKey, setTabActiveKey] = useState('')
- const [tabItems, setTabItems] = React.useState([])
-
- const addTab = useCallback((item) => {
- const existItem = tabItems.find(it => it.key === item.key)
- if (!existItem) {
- setTabItems([...tabItems, {
- key: item.key,
- path: item.path,
- label: item.name
- }])
- }
- setTabActiveKey(item.key)
- }, [tabItems])
- const handleSelectMenu = useCallback((selectedKeys) => {
- console.log('handleSelectMenu', selectedKeys)
- let menuKey = selectedKeys[selectedKeys.length - 1]
- let item = menuDataMap.get(menuKey)
- if (item && item.path) {
- addTab(item)
- }
- }, [addTab])
- const handleTabChange = useCallback((activeKey) => {
- setTabActiveKey(activeKey)
- const item = tabItems.find(d => d.key === activeKey)
- nav(item.path)
- }, [tabItems, nav])
- const handleTabEditChange = useCallback((activeKey, action) => {
- if (action === 'remove') {
- delete cacheOutletElements[activeKey]
- const newItems = tabItems.filter(d => d.key !== activeKey)
- setTabItems(newItems)
- if (newItems.length) {
- handleTabChange(newItems[0].key)
- }
- }
- }, [tabItems, handleTabChange])
-
- const renderView = useCallback((routeElement) => {
- if (!cacheOutletElements[tabActiveKey]) {
- cacheOutletElements[tabActiveKey] = {routeElement}
- }
- return Object.keys(cacheOutletElements).map(key => {
- const element = cacheOutletElements[key]
- if (key === tabActiveKey) {
- return React.cloneElement(element, {
- key: key,
- style: {
- display: 'block'
- }
- })
- } else {
- return React.cloneElement(element, {
- key: key,
- style: {
- display: 'none'
- }
- })
- }
- })
- }, [cacheOutletElements, tabActiveKey])
- return
{ - if (item.children) {
- return defaultDom
- }
- menuDataMap.set(item.path, item)
- return {defaultDom}
- }}>
-
-
-
-
- }
-
- const routes = [{
- path: '/',
- element:
, - children: [
- {
- index: true,
- element:
- }, {
- path: 'home',
- name: "Home",
- element:
- }
- , {
- path: 'a',
- name: "DemoA",
- element:
- }
- , {
- path: 'b',
- name: "DemoB",
- element:
- }
- ]
- }]
- const router = createHashRouter(routes, {
-
- })
- const App = () => {
- return
- }
- ReactDOM.createRoot(document.getElementById('app')).render(
) -
-