【前端页面缓存技术方案】

前端页面缓存技术探讨

关于页面缓存数据的纯前端技术方案

背景

为了优化用户的体验,可能会遇到这样的需求:在列表页跳到详情页然后又返回列表的时候,需要保持状态和滚动位置;或是页面内切换组件(比如切换 Tab )的时候,需要保持状态。但目前由于react router只保留当前匹配第一个的路由状态,卸载掉其他路由。切换页面或Tab时会销毁上个组件,所以现状是每次返回时都是当前页面的初始状态。

项目存在的现有方案

思路:将列表页的数据全部保存起来,再次返回列表页的时候,进行读取数据

  • 本地存储
    通过localStorage或者sessionStorage缓存列表页的状态,当再次返回列表页时,通过获取本地缓存数据来实现保留当前页面的状态。
    弊端:

    • 如果用户手动清除了本地缓存后,再次返回到列表页,将获取不到保存的数据;
    • 如果当前分页为2,返回列表页后再增加条件搜索,搜索结果数据不足达到2页,可能会有报错问题
  • redux存储
    对于react实现的SPA页面,我们可以通过创建store存储列表数据保留在一级页面路径下,然后再次返回列表页面后,通过react-redux的connect方法将state中的数据绑定到页面的props中,方便访问。
    弊端:

    • 列表数据较多时,需要存储多个数据状态,不易维护;
    • 在页面路径较深或子路径多时,其他页面也会访问到store中的数据,导致数据较多,出现冗余
  • url携带参数存储
    将列表页的数据状态作为参数保存在页面的url中,页面参数通过列表页面数据变化而实时改变。当详情页再次返回到列表页,通过解析url参数来展示页面相应状态。
    弊端:

    • 随着切换筛选条件次数增多,浏览器会记录每一次的url变化,当用户点击浏览器回退或前进按钮时,浏览器会从历史记录中获取返回url,用户多次点击返回,可能导致页面展示上一次筛选状态,而并非跳转页面,影响用户体验
    • 如果列表页的数据状态较多,url上的公共业务参数也较多,由于url参数长度有限,可能会出现丢失参数的问题
  • react context
    和redux相似,将列表页面数据保存到 context 中,context provider 放置在列表页和详情页共同的父组容器上。弊端也是会存在不易维护的问题,还有在子组件中更新 context 容易引发死循环的问题。(项目没有使用context,因此放弃)

思考🤔

除了以上存储页面各个数据的方式以外,是否还有其他方式解决呢?

联想到了在手机屏幕上进行操作App的页面切换,我们可以理解是在当前屏幕上做一层层叠加,用户看到的一直都是最上层,当用户返回页面,即看到下一层页面,每一层的页面数据都有所保留,因此在切换页面的时候并没有销毁,而是叠加。

通过移动端的实现,联想PC端是不是也可以不让当前页面销毁?达到一个伪销毁的状态,将页面保存到另一个容器里,然后当访问到该路由时,直接把容器里的数据取出再赋给真实的DOM组件中。

其他技术调研

react-activation

git地址:https://github.com/CJY0208/react-activation

import React, { Component, createContext } from 'react'

const { Provider, Consumer } = createContext()
const withScope = WrappedComponent => props => (
  <Consumer>{keep => <WrappedComponent {...props} keep={keep} />}</Consumer>
)

export class AliveScope extends Component {
  nodes = {}
  state = {}

  keep = (id, children) =>
    new Promise(resolve =>
      this.setState(
        {
          [id]: { id, children }
        },
        () => resolve(this.nodes[id])
      )
    )

  render() {
    return (
      <Provider value={this.keep}>
        {this.props.children}
        {Object.values(this.state).map(({ id, children }) => (
          <div
            key={id}
            ref={node => {
              this.nodes[id] = node
            }}
          >
            {children}
          </div>
        ))}
      </Provider>
    )
  }
}

@withScope
class KeepAlive extends Component {
  constructor(props) {
    super(props)
    this.init(props)
  }

  init = async ({ id, children, keep }) => {
    const realContent = await keep(id, children)
    this.placeholder.appendChild(realContent)
  }

  render() {
    return (
      <div
        ref={node => {
          this.placeholder = node
        }}
      />
    )
  }
}

export default KeepAlive

实现大致过程是将AliveScope组件通过上下文,把一个keep方法传递下去,将KeepAlive组件包裹需要缓存的组件,然后一个高阶组件获取到 keep 方法,并把 children 属性传入 KeepAlive 组件,在 KeepAlive 组件中调用 keep 方法,把 children 属性缓存到 AliveScope 的 state 中。在 state 更新后,把 ref (真实 DOM)返回给 KeepAlive 组件。KeepAlive 组件拿到真实 DOM 后,把它移动到自己组件内的某个占位中。
在这里插入图片描述主要思路:把 children 包裹起来并且传递出去,在缓存组件内被渲染,当前组件正常地更新卸载。当前组件卸载的时候,children 也被卸载了,但是它的虚拟 DOM 已经被缓存在了缓存组件中。这个组件重新被加载的时候,把缓存直接渲染后移入当前组件,就恢复了组件卸载前状态。

示例

import React from 'react';
import ReactDOM from 'react-dom';
import { AliveScope, KeepAlive } from 'react-activation'
import Test from './views/Test';

ReactDOM.render(
  <AliveScope>
    <KeepAlive name="Test">
      <Test />
    </KeepAlive>
  </AliveScope>,
  document.getElementById('root'),
);

弊端:

  • 适用场景不灵活,例如当用户只在点击浏览器回退按钮时读取缓存,其他场景返回不缓存时,该方案实现起来较难,因此在特定场景下缓存页面,不推荐使用

react-router-cache-route

git地址:https://github.com/CJY0208/react-router-cache-route
通过“重写“Switch、Route两个组件,控制页面组件的生命周期,使其在不匹配的时候,记录滚动位置并从dom上移除,在匹配的时候,重新挂载到dom上并恢复滚动位置,整个过程组件实例并未销毁,从而达到缓存实例数据状态的目的。

示例:

<AppContainer>
	<CustomHeader />
	<CacheSwitch>
		{
			routes.map((route: RouteType, index:number) => {
				return route.cache ? (
						<CacheRoute
							exact={true}
							path={`/${route.routerPath}`}
							key={index}
							component={route.component}
						/>
					) : (
						<Route 
							exact={true}
							path={`/${route.routerPath}`}
							key={index}
							component={route.component}
						/>
					)
			})
		}
		<Redirect to='/login' />
	</ CacheSwitch>
</AppContainer>

CacheRoute
给组件套上Route、CacheComponent等wrapper,配合computedMatchForCacheRoute欺骗Route组件,从而确保其Route内嵌套的CacheComponent会一直渲染。通过可传props参数项,可以在指定场景进行缓存,适用场景更加灵活 。
在这里插入图片描述
弊端:

  • 项目框架受限,当项目采用umi等一些对router组件进行封装的框架时,项目对router组件改装起来较难,因此不适合该方案

结论

  • 通过将页面数据进行保存数据,更容易让人理解,但是每次页面有此需求都需要再次开发需要存储的数据,开发成本比较大,不易维护;
  • 通过第三方插件将页面组件保存另一个容器里,开发成本低,但会因项目框架的设计,使用可能会受限制;
<think>好的,我现在需要帮助用户寻找前端自定义页面的设计方案和技术指导。根据用户的问题,他们可能希望了解如何构建灵活、可定制的前端页面,可能需要涉及架构设计、技术选型以及具体实现方法。 首先,我应该回顾用户提供的引用内容,看看是否有相关的信息可以利用。引用[3]提到微前端架构,这可能与自定义页面设计相关,因为它涉及多个独立团队协作开发,模块化设计。引用[4]提到开放源代码,可能涉及到如何利用现有开源项目或自主开发。引用[1]中的文件结构可能对组织代码有帮助,而引用[2]关于滚动条定制的问题可能属于样式自定义的一部,但用户的问题更偏向整体页面设计。 接下来,我需要考虑用户可能的实际需求。自定义页面可能包括动态布局、主题定制、组件化开发等。可能需要步骤来讲解设计思路,比如架构设计、技术方案选择、具体实现步骤,以及优化和扩展。 架构设计方面,模块化是关键。微前端架构(如引用[3]所述)可以将页面为独立模块,每个模块可独立开发和部署。使用Web Components或框架如React、Vue的组件化开发能提高复用性。配置化设计允许通过JSON配置动态生成页面,这需要设计配置规范和解析引擎。 技术方案,动态路由和按需加载可以提升性能,例如React Router或Vue Router。状态管理使用Redux或Vuex来处理全局状态。样式主题系统可以利用CSS变量或CSS-in-JS库,如styled-components。插件机制允许第三方扩展功能,可能需要设计API接口和沙箱环境。 具体实现步骤需要从项目结构开始,如引用[1]中的示例,合理组织images、components、styles等目录。然后开发基础组件,如布局容器和动态表单组件。配置解析引擎需要加载和解析JSON配置,渲染对应组件。实现拖拽生成器可能需要使用库如react-dnd或vue-draggable。最后,集成主题切换功能,通过CSS变量或预处理器变量实现。 优化和扩展方面,性能优化包括懒加载和代码割。可扩展性方面,提供自定义组件注册和API扩展。安全性需要注意沙箱机制和输入校验,防止XSS攻击。 在相关问题部,需要涵盖微前端的具体实现、动态主题切换、配置化页面生成、性能优化以及安全性措施。这些问题能帮助用户进一步深入各个技术点。 需要确保回答中正确引用提供的参考文献,比如引用[3]关于微前端,引用[4]关于开源代码的可能应用,引用[1]的文件结构示例。同时,按照用户的系统级指令,数学公式使用正确的LaTeX格式,但在这个问题中可能涉及较少,主要集中在代码和架构描述上。 最后,要确保回答结构清晰,点说明,技术方案具体,并自然地融入引用内容。同时生成的相关问题要针对用户可能感兴趣的后续步骤,帮助他们继续探索。</think>### 前端自定义页面实现方案与技术指导 #### 一、设计思路与核心原则 1. **模块化架构** 推荐采用微前端技术拆页面功能模块,实现独立开发、动态加载和版本隔离[^3]。例如,将导航栏、表单、图表等封装为独立子应用,通过主框架动态组合。 $$ \text{主框架} + \sum_{i=1}^{n} \text{子模块}_i \rightarrow \text{完整页面} $$ 2. **配置驱动设计** 通过JSON/YAML配置定义页面结构,实现动态渲染: ```json { "layout": "grid-12", "components": [ { "type": "Header", "props": { "title": "控制台" }}, { "type": "Chart", "position": [0,0,6,4] } ] } ``` 3. **可扩展性** 支持插件机制和自定义组件注册,参考开源项目的代码自主可控特性[^4],可设计组件沙箱环境保障安全性。 --- #### 二、技术实现方案 1. **基础技术栈选择** - 框架:React/Vue3(支持Composition API) - 状态管理:Redux Toolkit/Pinia - 样式方案:CSS Modules + CSS变量(实现动态主题) 2. **动态加载关键技术** ```javascript // 动态组件加载示例 const loadComponent = async (componentName) => { const module = await import(`./components/${componentName}.vue`); return defineAsyncComponent(module); } ``` 3. **可视化搭建实现** 结合拖拽库(如react-dnd)与配置解析引擎: ```typescript interface ComponentMeta { type: string; configSchema: ZodSchema; // 类型校验 defaultProps: Record<string, any>; } ``` --- #### 三、性能优化策略 1. **按需加载** 使用Webpack动态导入或Vite的glob导入实现代码割: ```js const modules = import.meta.glob('./components/*.vue'); ``` 2. **缓存机制** 对页面配置数据采用localStorage + 版本号校验缓存: $$ T_{\text{load}} = T_{\text{network}} \times P_{\text{cache-miss}} + T_{\text{cache}} \times P_{\text{cache-hit}} $$ 3. **渲染优化** 对高频更新区域使用虚拟滚动(如vue-virtual-scroller),避免重复渲染问题[^2]。 --- #### 四、参考实现路径 1. **初始化项目结构** 参考课程设计文件结构[^1]扩展为: ``` /src /core # 框架核心 /plugins # 插件系统 /themes # 样式主题 /prefabs # 预设模板 ``` 2. **开发流程示例** ```mermaid graph TD A[定义组件元数据] --> B[创建可视化编辑器] B --> C[生成配置JSON] C --> D[配置解析引擎] D --> E[动态渲染页面] ``` ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值