赞
踩
keep-alive是vue中的内置组件,使用keep-alive包裹组件可以在该组件的切换过程中将页面的状态缓存在内存中,界面切换后被包裹的组件实例不会被销毁,防止界面重复渲染DOM,并且可以防止在页面切换过程中组件的数据丢失
如下图所示,想要实现以下效果:
1.第一步: 在user路由下的输入框中输入内容
2.第二步: 切换至home路由
3.第三步:再切换回来,保留上次输入的值
即:切换路由时缓存之前的组件,保留之前输入的值不被清空
vue项目可以使用内置的keep-alive组件包裹实现。react可以通过目前市面上的其他库来实现,例如React Activation和react-keep-alive等等,我们也可以手动实现。
/KeepAlive.js
包裹在需要缓存的组件外侧,通过Provider把状态和方法传递给子组件,如果判断出reactElement已经传过来了,但是对应的nodes数组还是为null的话就触发相应的reducer把该nodes重新赋值
import { useCallback, useReducer } from "react" import {KeepAliveReducer} from './KeepAliveReducer' import * as actionTypes from './types' import { KeepAliveContext } from "./KeepAliveContext" function KeepAlive(props) { console.log('KeepAlive props=>', props); /** * { * home: { * keepAliveId: 'home', * reactElement: reactElement, * nodes: nodes, * status: create | created * }, * user: { * keepAliveId: 'user', * reactElement: reactElement, * nodes: nodes, * status: create | created * } * } */ const [keepAliveStates, dispatch] = useReducer(KeepAliveReducer, {}) const setKeepAliveState = useCallback(({ reactElement, keepAliveId }) => { if(!keepAliveStates[keepAliveId]) { dispatch({ type: actionTypes.CREATING, payload: { keepAliveId, reactElement } }) } }, [keepAliveStates]) return ( <KeepAliveContext.Provider value={{ keepAliveStates, setKeepAliveState, dispatch }}> { props.children } { Object.values(keepAliveStates).map(({ keepAliveId, reactElement }) => ( <div key={ keepAliveId } ref={(node) => { console.log(keepAliveStates); if(node && !keepAliveStates[keepAliveId].nodes) { dispatch({ type: actionTypes.CREATED, payload: { keepAliveId, nodes: [...node.childNodes] } }) } }}>{ reactElement }</div> )) } </KeepAliveContext.Provider> ) } export default KeepAlive
/keepAliveReducer.js
不同时机更新state的reducer
import * as actionTypes from './types' /** * * @param {*} state keepAliveStates * @param {*} action { type, payload } */ export function KeepAliveReducer(state, action) { const { type, payload } = action const { keepAliveId, reactElement, nodes } = payload switch(type) { case actionTypes.CREATING: console.log(state); return { ...state, [keepAliveId]: { keepAliveId, reactElement, status: type, nodes: null } } case actionTypes.CREATED: console.log(state); return { ...state, [keepAliveId]: { ...state[keepAliveId], status: type, nodes } } default: return state } }
/keepAliveContext.js
自定义context
import { createContext } from 'react'
export const KeepAliveContext = createContext()
/transfer.js
每次进入判断有无节点数组,没有则触发reducer把当前的组件赋值给state的
reactElement属性,如果有则通过appendChild方法把节点添加至当前组件下
/* eslint-disable react-hooks/rules-of-hooks */ import { useContext, useEffect, useRef } from "react" import { KeepAliveContext } from "./keepAliveContext" export function KeepAliveTransfer(KeepAliveComponent, keepAliveId) { return function(props) { const _ref = useRef(null) const { keepAliveStates, setKeepAliveState } = useContext(KeepAliveContext) // console.log('keepAliveStates', keepAliveStates); // console.log('setKeepAliveState', setKeepAliveState); useEffect(() => { const state = keepAliveStates[keepAliveId] if(state && state.nodes) { console.log('有节点', state); state.nodes.forEach(node => _ref.current.appendChild(node)) } else { console.log('无节点', state); setKeepAliveState({ reactElement: <KeepAliveComponent { ...props }/>, keepAliveId }) } }, [keepAliveStates, setKeepAliveState, props]) return ( <div ref={ _ref }></div> ) } }
/types.js
export const CREATING = 'CREATING'
export const CREATED = 'CREATED'
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。