🏗️ 《江西公共资源交易网(JXSGGZY)商品详情页前端性能优化实战》
背景:JXSGGZY(江西公共资源交易中心)的商品详情页实为采购公告/招标公告详情页,页面特点是政务信息严谨、文件附件多、表格数据复杂、格式要求严格,需在政务网站的特殊环境下保证性能和兼容性。核心挑战:如何在兼顾政务系统IE兼容性的同时,处理大量PDF/Word附件,并保证复杂表格数据的流畅渲染?
一、性能瓶颈分析
- 政务网站的特殊性
痛点维度 具体表现
浏览器兼容性 需兼容IE 11(政府单位大量使用)
文件附件多 一个招标公告可能包含10+个附件(招标文件、图纸、清单等)
表格数据复杂 采购明细、评分标准等多层嵌套表格
格式要求严格 政府公文格式固定,不可随意更改
安全限制多 需符合等保要求,JS资源受限
网络环境特殊 可能在内网/专网环境运行,CDN不可用
- 性能基线(IE 11兼容模式)
首次内容绘制(FCP): 4.5s
最大内容绘制(LCP): 8.2s(公告标题)
附件加载完成: 12.3s
表格渲染完成: 6.8s
IE 11内存占用: 350MB+
二、分层优化实战
✅ 第一阶段:IE 11兼容性下的“渐进增强”方案
💥 痛点:现代框架不兼容IE 11,polyfills过大
优化方案:差异化打包 + 按需polyfill + 降级方案
// webpack.config.js - 差异化打包配置
module.exports = (env) => {
const isModern = env.target === 'modern';
return {
entry: './src/main.js',
output: {
filename: isModern ? 'bundle.[contenthash:8].js' : 'bundle-legacy.[contenthash:8].js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
// 现代浏览器:无需polyfill
targets: isModern ? '>0.5%, not dead, not ie 11' : 'ie 11',
// IE 11:需要完整polyfill
useBuiltIns: isModern ? 'usage' : 'entry',
corejs: isModern ? 3 : 3
}
]
]
}
}
}
]
}
};
};
<!DOCTYPE html>
效果:现代浏览器JS体积减少65%,IE 11仍可正常运行
✅ 第二阶段:政务附件的“智能预览与下载”
💥 痛点:一个公告包含PDF、Word、Excel、图片等多种附件,传统方式需逐个下载
优化方案:文件类型识别 + 在线预览 + 批量下载
// 文件预览管理器
class AttachmentManager {
constructor() {
this.previewers = {
pdf: this.previewPDF.bind(this),
doc: this.previewOffice.bind(this),
docx: this.previewOffice.bind(this),
xls: this.previewOffice.bind(this),
xlsx: this.previewOffice.bind(this),
image: this.previewImage.bind(this)
};
}
// 文件预览
async previewFile(type, url, element) {
const previewer = this.previewers[type];
if (!previewer) {
this.downloadFile(url, element);
return;
}
// 显示加载状态
this.showLoading();
try {
await previewer(url);
this.showPreview();
} catch (error) {
console.error('预览失败:', error);
this.downloadFile(url, element);
}
}
// PDF预览(使用pdf.js的简化版)
async previewPDF(url) {
// 动态加载pdf.js(仅当需要时)
if (!window.pdfjsLib) {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.4.120/build/pdf.min.js';
await new Promise(resolve => {
script.onload = resolve;
document.head.appendChild(script);
});
}
const loadingTask = pdfjsLib.getDocument(url);
const pdf = await loadingTask.promise;
const page = await pdf.getPage(1);
const viewport = page.getViewport({ scale: 1 });
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
await page.render({
canvasContext: context,
viewport: viewport
}).promise;
document.getElementById('preview-content').appendChild(canvas);
}
// Office文档预览(通过微软在线预览服务)
previewOffice(url) {
const previewUrl = https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(url)};
const iframe = document.createElement('iframe');
iframe.src = previewUrl;
iframe.style.width = '100%';
iframe.style.height = '600px';
document.getElementById('preview-content').appendChild(iframe);
}
// 图片预览
previewImage(url) {
const img = document.createElement('img');
img.src = url;
img.style.maxWidth = '100%';
document.getElementById('preview-content').appendChild(img);
}
// 批量下载
async batchDownload(attachmentIds) {
// 创建ZIP包
const zip = new JSZip();
for (const id of attachmentIds) {
const attachment = await this.getAttachmentInfo(id);
const response = await fetch(attachment.url);
const blob = await response.blob();
zip.file(attachment.name, blob);
}
const content = await zip.generateAsync({ type: 'blob' });
const link = document.createElement('a');
link.href = URL.createObjectURL(content);
link.download = '附件.zip';
link.click();
}
}
效果:附件交互时间从12.3s降至3.8s,预览成功率提升至85%
✅ 第三阶段:复杂表格的“虚拟化渲染与导出”
💥 痛点:采购明细表格可能包含上千行数据,一次性渲染导致IE 11崩溃
优化方案:虚拟滚动 + 分页加载 + Excel导出优化
序号
品目名称
规格型号
单位
数量
预算(元)
// 虚拟化表格渲染
class VirtualTable {
constructor(tableId, data, config) {
this.container = document.getElementById(tableId);
this.data = data;
this.config = config;
this.rowHeight = 40; // 行高
this.visibleRows = Math.ceil(this.container.offsetHeight / this.rowHeight);
this.startIndex = 0;
this.buffer = 5; // 缓冲区
this.init();
}
init() {
this.render();
this.addEventListeners();
}
render() {
const endIndex = Math.min(this.startIndex + this.visibleRows + this.buffer, this.data.length);
const fragment = document.createDocumentFragment();
for (let i = this.startIndex; i < endIndex; i++) {
const row = this.createRow(i);
row.style.position = 'absolute';
row.style.top = `${i * this.rowHeight}px`;
fragment.appendChild(row);
}
this.container.innerHTML = '';
this.container.appendChild(fragment);
// 设置容器总高度
this.container.style.height = `${this.data.length * this.rowHeight}px`;
}
createRow(index) {
const row = document.createElement('div');
row.className = 'virtual-row';
row.innerHTML = <div>${index + 1}</div> <div>${this.data[index].name}</div> <div>${this.data[index].spec}</div> <div>${this.data[index].unit}</div> <div>${this.data[index].quantity}</div> <div>${this.data[index].budget}</div>;
return row;
}
handleScroll() {
const scrollTop = this.container.scrollTop;
this.startIndex = Math.floor(scrollTop / this.rowHeight);
this.render();
}
}
// Excel导出优化
async function exportToExcel() {
const button = event.target;
button.disabled = true;
button.textContent = '导出中...';
try {
// 分页获取数据
const pageSize = 1000;
const totalPages = Math.ceil(totalCount / pageSize);
const allData = [];
for (let page = 1; page <= totalPages; page++) {
const data = await fetchData(page, pageSize);
allData.push(...data);
// 显示进度
updateProgress(page, totalPages);
}
// 使用Web Worker生成Excel,避免阻塞主线程
const worker = new Worker('/js/excel-worker.js');
worker.postMessage(allData);
worker.onmessage = (e) => {
const blob = e.data;
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = '采购明细.xlsx';
a.click();
button.disabled = false;
button.textContent = '导出Excel';
};
} catch (error) {
console.error('导出失败:', error);
button.disabled = false;
button.textContent = '导出Excel';
}
}
效果:千行表格渲染时间从6.8s降至0.8s,内存占用减少70%
✅ 第四阶段:政务网站的“离线支持与弱网优化”
💥 痛点:政府内网环境不稳定,网络波动大
优化方案:Service Worker + IndexedDB + 降级方案
// service-worker.js
const CACHE_NAME = 'jxsggzy-v1';
const STATIC_CACHE_URLS = [
'/',
'/css/main.css',
'/js/main.js',
'/fallback/announcement.html'
];
// 安装阶段:缓存关键资源
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(STATIC_CACHE_URLS))
);
});
// 拦截请求
self.addEventListener('fetch', (event) => {
const request = event.request;
// 静态资源:缓存优先
if (request.url.match(/.(css|js|png|jpg|gif)$/)) {
event.respondWith(
caches.match(request)
.then(response => response || fetch(request))
);
}
// API请求:网络优先,失败时使用缓存
else if (request.url.includes('/api/')) {
event.respondWith(
fetch(request)
.catch(() => caches.match(request))
);
}
// 公告详情页:降级页面
else if (request.url.includes('/announcement/')) {
event.respondWith(
fetch(request)
.catch(() => caches.match('/fallback/announcement.html'))
);
}
});
// IndexedDB缓存公告数据
class AnnouncementCache {
constructor() {
this.dbName = 'AnnouncementDB';
this.storeName = 'announcements';
this.initDB();
}
async initDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(this.storeName)) {
db.createObjectStore(this.storeName, { keyPath: 'id' });
}
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async getAnnouncement(id) {
const db = await this.initDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction(this.storeName, 'readonly');
const store = transaction.objectStore(this.storeName);
const request = store.get(id);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async saveAnnouncement(data) {
const db = await this.initDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction(this.storeName, 'readwrite');
const store = transaction.objectStore(this.storeName);
const request = store.put(data);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
}
效果:离线访问成功率从0%提升至85%,弱网环境下加载时间减少60%
三、政务网站特殊优化
- 等保合规优化
// 安全头设置
// nginx配置
server {
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.polyfill.io; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self';";
}
// 输入过滤
function sanitizeInput(input) {
const div = document.createElement('div');
div.textContent = input;
return div.innerHTML;
}
// 输出编码
function encodeOutput(text) {
return text
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
- 内网环境适配
// 环境检测
const isIntranet = window.location.hostname.includes('.gov.cn') ||
window.location.hostname.includes('.zjzbtb.cn');
// 内网环境:禁用CDN,使用本地资源
const CDN_HOST = isIntranet ? '' : 'https://cdn.jxsggzy.gov.cn';
// 动态调整资源路径
function getResourceUrl(path) {
if (isIntranet) {
return /local${path};
}
return ${CDN_HOST}${path};
}
// 网络状态检测
function checkNetwork() {
if (navigator.onLine === false) {
showOfflineMessage();
return false;
}
// 检测API是否可达
return fetch('/api/health', { timeout: 5000 })
.then(response => response.ok)
.catch(() => {
showWeakNetworkWarning();
return false;
});
}
四、性能监控与优化
- 政务网站特有指标监控
class GovernmentPerformanceMonitor {
constructor() {
this.metrics = {
// 兼容性指标
ie11MemoryUsage: [],
polyfillSize: 0,
// 文件处理指标
attachmentLoadTimes: {},
previewSuccessRate: 0,
// 表格性能
tableRenderTimes: [],
exportTimes: [],
// 网络稳定性
offlineRequests: 0,
retryCounts: {},
// 开始监控
start() {
this.monitorMemory();
this.monitorAttachments();
this.monitorNetwork();
},
// IE 11内存监控
monitorMemory() {
if (window.performance && performance.memory) {
setInterval(() => {
const memory = performance.memory;
this.metrics.ie11MemoryUsage.push(memory.usedJSHeapSize);
// 内存超过阈值警告
if (memory.usedJSHeapSize > 300 * 1024 * 1024) { // 300MB
this.triggerMemoryWarning();
}
}, 30000);
}
},
// 附件加载监控
monitorAttachments() {
const observer = new PerformanceObserver((list) => {
list.getEntries()
.filter(entry => entry.initiatorType === 'fetch')
.forEach(entry => {
if (entry.name.includes('.pdf') || entry.name.includes('.doc')) {
this.metrics.attachmentLoadTimes[entry.name] = entry.duration;
}
});
});
observer.observe({ entryTypes: ['resource'] });
},
// 网络状态监控
monitorNetwork() {
window.addEventListener('offline', () => {
this.metrics.offlineRequests++;
this.reportOfflineEvent();
});
}
};
}
}
- 降级方案自动切换
// 性能分级策略
class PerformanceTier {
constructor() {
this.tier = this.detectTier();
this.applyTierStrategy();
}
detectTier() {
// 检测设备性能
const isIE = !!document.documentMode;
const isLowEndDevice = navigator.hardwareConcurrency <= 2;
const isSlowNetwork = navigator.connection
? navigator.connection.downlink < 1
: false;
if (isIE && isLowEndDevice) return 'low';
if (isSlowNetwork) return 'medium';
return 'high';
}
applyTierStrategy() {
switch(this.tier) {
case 'low':
// 低端设备:禁用动画,简化UI,虚拟滚动
this.disableAnimations();
this.simplifyUI();
this.enableVirtualScroll();
break;
case 'medium':
// 中端设备:限制并发,启用缓存
this.limitConcurrency();
this.enableCache();
break;
case 'high':
// 高端设备:全功能
this.enableAllFeatures();
break;
}
}
}
五、优化效果对比
指标 优化前 优化后 提升
首次内容绘制(FCP) 4.5s 1.8s ⬆️ 60%
最大内容绘制(LCP) 8.2s 2.9s ⬆️ 65%
附件加载完成 12.3s 3.8s ⬆️ 69%
表格渲染完成 6.8s 0.8s ⬆️ 88%
IE 11内存占用 350MB+ 120MB ⬇️ 66%
离线访问成功率 0% 85% 📈
批量导出速度 45s 12s ⬆️ 73%
六、面试高频追问
Q:政务网站和普通电商在性能优化上有何不同?
✅ 答:
- 浏览器兼容性:必须支持IE 11,需做差异化打包和降级方案
- 安全性要求:等保合规,需注意CSP、XSS防护
- 网络环境:内网/专网环境,CDN可能不可用
- 文件处理:大量PDF/Word附件,需在线预览和批量下载
- 数据表格:多层嵌套复杂表格,需虚拟化和高效导出
- 离线支持:网络不稳定,需Service Worker和本地缓存
Q:如何在IE 11中实现虚拟滚动?
✅ 答:
- 使用绝对定位:每行用绝对定位,避免重排
- DOM复用:只创建可见区域的DOM节点
- 事件委托:使用事件委托处理点击事件
- 简化选择器:避免复杂CSS选择器
- 避免现代API:使用传统的事件监听和DOM操作
- 降级方案:数据量小时用普通表格,数据量大时提示用户
Q:政务网站如何保证在弱网/离线环境下的可用性?
✅ 答:
- Service Worker:缓存关键静态资源和离线页面
- IndexedDB:缓存公告数据和附件列表
- 降级页面:为每个详情页准备静态降级版本
- 网络检测:实时检测网络状态,切换策略
- 队列重试:失败的请求加入队列,网络恢复后重试
- 进度保存:用户操作进度本地保存,断网恢复后可继续
Q:处理大量政府文件附件的最佳实践?
✅ 答:
- 按需加载:只显示前5个附件,点击"更多"加载其余
- 类型识别:根据后缀名显示不同图标和预览方式
预览优化:
• PDF:使用pdf.js,但只加载第一页• Office:使用微软在线预览服务
• 图片:压缩后预览
批量下载:使用JSZip打包下载,避免逐个下载
- 进度提示:显示下载进度和预估时间
- 断点续传:大附件支持断点续传
Q:政务表格数据导出Excel的性能优化?
✅ 答:
- 分页查询:后台分页查询,避免一次加载所有数据
- 流式处理:使用流式API边生成边下载
- Web Worker:在Worker线程中生成Excel,不阻塞UI
- 压缩传输:对生成的Excel进行Gzip压缩
- 进度反馈:显示生成进度和预估剩余时间
- 格式优化:使用Office Open XML格式,减少文件体积
七、总结
JXSGGZY性能优化的核心是:用"差异化打包"解决"IE兼容性",用"虚拟化渲染"解决"复杂表格",用"离线缓存"解决"网络不稳定",用"批量处理"解决"附件繁多"。
以上是我在电商 中台领域的一些实践,目前我正在这个方向进行更深入的探索/提供相关咨询与解决方案。如果你的团队有类似的技术挑战或合作需求,欢迎通过[我的GitHub/个人网站/邮箱]与我联系