【2026】记录一次大数据请求时页面整体优化过程

前言

记录一次请求univer在线电子表格在进行上万条数据返回前端页面响应的优化过程

本次优化主要是针对大数据量下页面加载响应,通过前后端相关优化提高响应速度,增强用户体验。

一般我们要分析一个页面响应过慢,我们可以先通过按 F12 打开浏览器的开发者工具。切换到 Network 网络,点击一次刷新可以看到打开本次页面请求了哪些服务器接口,以及哪些静态文件,并且每次请求耗时,以及请求数据大小;下面还会显示本次页面请求全部完成用时多长等一些汇总信息。通过这些信息我们就可以有效的分析出这个页面打开过慢的原因,主要加载什么耗时过多.

通过下面两次测试可以看出,在优化前和优化后,打开页面耗时上提升了5到6倍,这样给用户的体验上就会提升很多
优化前:

优化后:

前端优化

可以看到上面优化前,打开页面加载耗时最长的就是请求webSocketConnect-VjaopYJK.js这个文件。这个文件是我的univer的在线sheet电子表格引擎依赖,他内部引用到了非常多的插件,并且全部默认打包到了一起造成这个js文件非常大,因此请求非常耗时。

1、通过代码配置优化减少.js静态文件体积

  1. 先通过代码优化,把相关的一些没有用到的依赖清除出去,减少整体的文件大小
  2. 既然默认打包到一起了,那可以通过配置vite.config.mtsbuild进行拆chunks,拆开打包,避免全部请求一个文件;原来一个,拆包后变为了几个,但拆包也不建议拆的过细,这样会造成依赖调用上的一些问题,而且拆的过细请求过多了也不好,效率反而会慢。

下面是我的vben构建配置文件

主要配置拆分打包配置

javascript 复制代码
	import type { UserConfig } from 'vite';
	
	import { defineConfig } from '@vben/vite-config';
	
	export default defineConfig(async (): Promise<UserConfig> => {
	  return {
	    application: {},
	    plugins: [
	      {
	        name: 'remove-preload-links',
	        transformIndexHtml(html) {
	          return html
	            .replaceAll(/<link[^>]+rel="preload"[^>]+as="style"[^>]*>/g, '')
	            .replaceAll(/<link[^>]+rel="modulepreload"[^>]*>/g, '');
	        },
	      },
	    ],
	    //开发服务器代理
	    vite: {
	      server: {
	        proxy: {
	          '/api': {
	            changeOrigin: true,
	            rewrite: (path) => path.replace(/^\/api/, ''),
	            target: 'http://localhost:9000',
	            ws: true,
	          },
	        },
	      },
	      //拆分打包
	      build: {
	        sourcemap: false,
	        minify: 'esbuild',
	        target: 'esnext',
	        modulePreload: false,
	        rollupOptions: {
	          external: ['bwipjs'],
	          output: {
	            chunkFileNames: 'assets/[name].[hash].js',
	            entryFileNames: 'assets/[name].[hash].js',
	            assetFileNames: (assetInfo) => {
	              if (assetInfo.name?.endsWith('.css')) {
	                return 'assets/[name].[hash].css';
	              }
	              return 'assets/[name].[hash].[ext]';
	            },
	            manualChunks(id, { getModuleInfo }) {
	              if (
	                id.includes('@univerjs/core') ||
	                id.includes('@univerjs/engine-render') ||
	                id.includes('@univerjs/ui') ||
	                id.includes('@univerjs/design') ||
	                id.includes('@univerjs/engine-formula') ||
	                id.includes('@univerjs/ui-adapter-vue3') ||
	                id.includes('@univerjs/rpc')
	              ) {
	                return 'univer-core'; // 稳定 chunk 名
	              }
	
	              if (
	                id.includes('@univerjs/sheets') ||
	                id.includes('@univerjs/sheets-formula') ||
	                id.includes('@univerjs/sheets-formula-ui') ||
	                id.includes('@univerjs/sheets-numfmt') ||
	                id.includes('@univerjs/sheets-numfmt-ui') ||
	                id.includes('@univerjs/sheets-find-replace') ||
	                id.includes('@univerjs/sheets-conditional-formatting') ||
	                id.includes('@univerjs/sheets-data-validation') ||
	                id.includes('@univerjs/sheets-sort') ||
	                id.includes('@univerjs/sheets-filter-ui')
	              ) {
	                return 'univer-sheet';
	              }
	
	              if (
	                id.includes('@univerjs/docs') ||
	                id.includes('@univerjs/docs-ui') ||
	                id.includes('@univerjs/find-replace')
	              ) {
	                return 'univer-doc';
	              }
	
	              if (id.includes('@univerjs/')) {
	                const pkgName = id
	                  .split('node_modules/@univerjs/')[1]
	                  ?.split('/')[0];
	                if (pkgName) return `univer-${pkgName}`;
	              }
	
	              return undefined;
	            },
	          },
	        },
	        chunkSizeWarningLimit: 2000,
	      },
	      optimizeDeps: { exclude: ['bwipjs'] },
	    },
	  };
	});
	
  1. 通过压缩静态文件,把文件进行gzip压缩传输,可以使得传输数据大大减少。压缩前和压缩后大小降低了5倍作用。压缩通过配置nginx进行压缩传输,使用vben预压缩也可以。
  2. 通过nginx设置静态文件存储浏览器缓存时间,默认浏览器缓存一般是几个小时,所以用户打开页面的时候都得去服务器请求下载静态文件渲染页面。但如果缓存在本地磁盘的话,渲染速度会指数级上升。

nginx.conf配置文件

主要配置gzip压缩和设置静态文件缓存

powershell 复制代码
worker_processes  auto;



error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    ##############################
    # gzip 压缩
    ##############################
    gzip  on;
    gzip_static off;
    gzip_vary on;                     # 添加 Vary: Accept-Encoding 头,避免 CDN 缓存问题
    gzip_min_length 1024;             # 小于 1KB 的文件不压缩(避免小文件压缩反而变大)
    gzip_proxied any;                 # 对代理请求也启用压缩(如 /api/)
    gzip_disable "msie6";             # 旧 IE6 不支持 gzip,禁用
    gzip_comp_level 6;                # 压缩级别(1~9),6 是速度与压缩率平衡点
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/javascript
        application/x-javascript
        application/json
        application/xml
        application/xhtml+xml
        application/rss+xml
        application/vnd.ms-fontobject
        application/x-font-ttf
        font/opentype
        image/svg+xml

        ;

    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }


    sendfile        on;
    keepalive_timeout  65;

    server {
        listen 91;
        server_name localhost;
        client_max_body_size 20m;
        root   /usr/share/nginx/d2o-web/v3/prod/;


        location = /index.html {
          
            try_files $uri $uri/ /index.html;
            add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate" always;
            add_header Pragma "no-cache" always;
            add_header Expires "0" always;
        }

        # 所有 HTML 文件禁止缓存
        location ~ \.html$ {
            add_header Cache-Control "no-store, no-cache, must-revalidate" always;
            add_header Pragma "no-cache" always;
            add_header Expires "0" always;
        }

        # 带哈希的静态资源缓存60天
        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2|woff|ttf)$ {
            if ($request_filename ~ "\.[a-f0-9]{8}\.(js|css|png|jpg|jpeg|gif|ico|svg|woff2|woff|ttf)$") {
                expires 60d;
                add_header Cache-Control "public, immutable" always;
            }
            # 无哈希文件短缓存(如 favicon.ico)
            if ($request_filename !~ "\.[a-f0-9]{8}\.") {
                expires 7d;
                add_header Cache-Control "public" always;
            }
        }

# index.html不缓存
        location / {
      
            try_files $uri $uri/ /index.html;
            index  index.html index.htm;

        }

        location /api/ {
            gunzip on;
            
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header REMOTE-HOST $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            # 根据前端请求代理指向后端的接口地址
            proxy_pass http://192.168.6.190:9100/;
            proxy_set_header Accept-Encoding "";   

        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

上面就是前端的一些优化方案,可以有效的提高用户的响应数据,特别是针对一些必须得大数据渲染的页面来说。当然这些只是我想到的一些方案,如果还有好的方案方案欢迎评论区留言。

后端优化

前端主要是侧着于页面渲染,后端则侧重于逻辑计算包裹最终返回前端需要的结构数据。

后端的优化上主要可以拆分为两块一块是数据库的SQL优化,一块就是代码方面的优化。

1、SQL优化

SQL优化,一般我们在执行SQL的时候总共分为两种,一种类型增、删、改,还有就是查询。

1.1、增删改类 SQL 优化(INSERT/UPDATE/DELETE)

  1. 新增优化
  • 多条数据插入的时候,可以用批量插入代替单条循环插入。一般可以一次性插入1000条左右去循环插入,一次性插入太多了效率也不太好。
  • 并且可以关闭自动提交,全部写入后在手动提交。
  1. 更新/删除(UPDATE/DELETE)优化
  • 在操作的时候尽量用主键/唯一索引筛选,避免全表更新/删除
  • 还有在大批量更新/删除时也可以1000左右分批执行。

1.2、查询类 SQL 优化(SELECT)

上面的增删改上需要优化的场景一般比较少,基本上平常在使用到的场景80%是查询。查询的核心主要就是精准筛选 + 索引利用。

  1. 对于一些常用的查询的字段可以为这些字段建立对应的索引。
  2. 查询的时候也尽量减少查询返回字段。避免使用SELECT * 这种全量返回字段的语句;尽量只返回我们需要的字段就行
  3. 多表查询的时候合理使用JOIN,尽量小表驱动大表;并且不建议去一次性JOIN连接太多的表一起查询
  4. 不要再SQL里加入计算相关的数据,尽量在代码层面执行
  5. 还有就是尽量限制返回的数据数量。限制返回的结果集,避免一次性返回大量数据

一般SQL优化做好了可以解决平常80%的场景。毕竟用户一次性也就只能看那么多条数据。这里只提供大致的思路,具体的SQL相关优化网上太多资料了,可以自行翻阅。

2、代码方面

一般考虑使用代码进行优化,针对的就是数据比较多的情况。通过SQL优化已经不能满足响应效率了。

  1. 配置<select>标签里的fetchSize参数(Oracle数据库尤其需要)。

    • fetchSize:为每次从数据库加载多少条数据到服务里来。MYSQL是一次性全部加载过来;Oracle是一次性加载10条。如果一次性查询的数据量很大的话,一次性加载数万 / 数十万行数据到 JVM 堆内存可能会出现内存溢出的情况。而如果是Oracle则一次性加载10条数据则会去请求加载上万次会造成速度非常慢。一般配置 fetchSize="2000" 效率会比较合适
  2. 如果是一些需要计算类型的数据。则可以采用多线程进行并行执行。

    • 异步并行执行可以使用下面的一些方式执行异步并行等任务计算
      • Stream.parallel()
      • CompletableFuture.runAsync(() -> {})
      • ThreadPoolExecutor

3.在大数据返回前端的时候,可以采用压缩GZIP压缩。可以有效的降低网络传输耗时,提升前端 / 客户端接收数据的速度。毕竟1M和100M的数据传输不是一个量级。

如果是Springboot项目可以添加下面的配置。如果是微服务项目Gateway服务也要加

yaml 复制代码
server:
  compression:
    enabled: true  # 开启HTTP响应压缩
    #指定需要压缩的 MIME 类型
    mime-types: > 
      text/html,
      text/xml,
      text/plain,
      text/css,
      text/javascript,
      application/javascript,
      application/json,
      application/x-javascript,
      text/x-component,
      application/x-chrome-extension,
      application/x-opera-extension,
      application/x-oauth2-credentials,
      application/x-web-app-manifest+json,
      application/json; charset=utf-8,
      application/atom+xml,
      application/rss+xml,
      application/json
    min-response-size: 1024 #压缩最小阈值

通过上面这些配置下来,基本上可以解决绝大部分的应用场景了。

相关推荐
天远云服1 天前
Go语言高并发实战:集成天远手机号码归属地核验API打造高性能风控中台
大数据·开发语言·后端·golang
管理快车道1 天前
连锁零售利润增长:我的实践复盘
大数据·人工智能·零售
Elastic 中国社区官方博客1 天前
使用 LangGraph 和 Elasticsearch 构建人机交互 Agents
大数据·人工智能·elasticsearch·搜索引擎·langchain·全文检索·人机交互
智慧化智能化数字化方案1 天前
数据资产管理进阶——解读数据资产管理体系建设【附全文阅读】
大数据·人工智能·数据资产管理·数据资产管理体系建设·数据要素入表
城数派1 天前
2001-2024年全球500米分辨率逐年土地覆盖类型栅格数据
大数据·人工智能·数据分析
Hubianji_091 天前
[SPIE] 2026年计算机网络、通信工程与智能系统国际学术会议 (ISCCN 2026)
大数据·人工智能·计算机网络·国际会议·论文投稿·国际期刊
触想工业平板电脑一体机1 天前
【触想智能】工业视觉设备与工控一体机进行配套需要注意的五大事项
android·大数据·运维·电脑·智能电视
运维行者_1 天前
跨境企业 OPM:多币种订单与物流同步管理,依靠网络自动化与 snmp 软件
大数据·运维·网络·数据库·postgresql·跨境企业
TDengine (老段)1 天前
TDengine C/C++ 连接器入门指南
大数据·c语言·数据库·c++·物联网·时序数据库·tdengine