1 Star 1 Fork 0

望曦开源团队/JavaScript案例合集

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
6.html 17.75 KB
一键复制 编辑 原始数据 按行查看 历史
弦中落宫商 提交于 2023-07-20 12:30 +08:00 . 性能优化 - 代码优化 - 虚拟滚动
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>代码优化 - 虚拟滚动</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 15px;
padding: 30px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
h1 {
text-align: center;
color: #333;
margin-bottom: 10px;
}
.subtitle {
text-align: center;
color: #666;
margin-bottom: 30px;
}
.demo-section {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-bottom: 30px;
}
.demo-box {
border: 2px solid #e0e0e0;
border-radius: 10px;
padding: 20px;
background: #f9f9f9;
}
.demo-box h3 {
color: #667eea;
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 10px;
}
.demo-box.bad {
border-color: #ff6b6b;
}
.demo-box.good {
border-color: #51cf66;
}
.demo-box.bad h3::before {
content: '❌';
}
.demo-box.good h3::before {
content: '✅';
}
.list-container {
height: 400px;
overflow-y: auto;
border: 1px solid #ddd;
border-radius: 8px;
background: white;
position: relative;
}
.list-item {
padding: 15px;
border-bottom: 1px solid #eee;
display: flex;
align-items: center;
gap: 15px;
transition: background 0.2s;
}
.list-item:hover {
background: #f5f5f5;
}
.list-item .avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
flex-shrink: 0;
}
.list-item .content {
flex: 1;
}
.list-item .name {
font-weight: 600;
color: #333;
margin-bottom: 5px;
}
.list-item .email {
color: #666;
font-size: 14px;
}
.list-item .status {
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
}
.list-item .status.online {
background: #d4edda;
color: #155724;
}
.list-item .status.offline {
background: #f8d7da;
color: #721c24;
}
.virtual-content {
position: absolute;
top: 0;
left: 0;
right: 0;
}
.controls {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin-top: 15px;
}
button {
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
}
.btn-primary {
background: #667eea;
color: white;
}
.btn-primary:hover {
background: #5a6fd6;
transform: translateY(-2px);
}
.btn-danger {
background: #ff6b6b;
color: white;
}
.btn-danger:hover {
background: #fa5252;
}
.btn-success {
background: #51cf66;
color: white;
}
.btn-success:hover {
background: #40c057;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-top: 20px;
}
.stat-card {
background: white;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
text-align: center;
}
.stat-card h4 {
color: #666;
font-size: 14px;
margin-bottom: 10px;
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #667eea;
}
.stat-value.high {
color: #ff6b6b;
}
.stat-value.low {
color: #51cf66;
}
.code-section {
background: #f8f9fa;
border-radius: 10px;
padding: 20px;
margin-top: 30px;
}
.code-section h3 {
color: #333;
margin-bottom: 15px;
}
pre {
background: #2d2d2d;
color: #f8f8f2;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-size: 13px;
line-height: 1.5;
}
.tips {
background: #e7f3ff;
border-left: 4px solid #667eea;
padding: 15px;
margin-top: 20px;
border-radius: 0 8px 8px 0;
}
.tips h4 {
color: #667eea;
margin-bottom: 10px;
}
.tips ul {
margin-left: 20px;
color: #555;
}
.tips li {
margin: 5px 0;
}
.performance-info {
background: #fff3cd;
border-left: 4px solid #ffc107;
padding: 15px;
margin-top: 15px;
border-radius: 0 8px 8px 0;
}
.performance-info h4 {
color: #856404;
margin-bottom: 10px;
}
.performance-info p {
color: #856404;
font-size: 14px;
line-height: 1.6;
}
</style>
</head>
<body>
<div class="container">
<h1>代码优化 - 虚拟滚动</h1>
<p class="subtitle">虚拟滚动只渲染可视区域内的元素,大幅提升长列表性能</p>
<div class="demo-section">
<div class="demo-box bad">
<h3>普通滚动(全部渲染)</h3>
<div id="normalContainer" class="list-container"></div>
<div class="performance-info">
<h4>⚠️ 性能问题</h4>
<p>渲染10000个DOM节点,导致页面卡顿、内存占用高</p>
</div>
<div class="controls">
<button class="btn-danger" onclick="loadNormalList()">加载10000条数据</button>
<button class="btn-primary" onclick="clearNormal()">清空</button>
</div>
</div>
<div class="demo-box good">
<h3>虚拟滚动(按需渲染)</h3>
<div id="virtualContainer" class="list-container"></div>
<div class="performance-info" style="background: #d4edda; border-color: #28a745;">
<h4 style="color: #155724;">✅ 性能优化</h4>
<p>只渲染可视区域的元素,性能提升显著</p>
</div>
<div class="controls">
<button class="btn-success" onclick="loadVirtualList()">加载10000条数据</button>
<button class="btn-primary" onclick="clearVirtual()">清空</button>
</div>
</div>
</div>
<div class="stats">
<div class="stat-card">
<h4>普通渲染DOM数量</h4>
<div id="normalDomCount" class="stat-value high">0</div>
</div>
<div class="stat-card">
<h4>虚拟渲染DOM数量</h4>
<div id="virtualDomCount" class="stat-value low">0</div>
</div>
<div class="stat-card">
<h4>DOM减少比例</h4>
<div id="domReduction" class="stat-value">0%</div>
</div>
<div class="stat-card">
<h4>数据总量</h4>
<div id="totalData" class="stat-value">0</div>
</div>
</div>
<div class="code-section">
<h3>代码实现</h3>
<pre><code>// 虚拟滚动实现
class VirtualScroll {
constructor(container, options = {}) {
this.container = container;
this.itemHeight = options.itemHeight || 70;
this.totalItems = options.totalItems || 0;
this.visibleCount = Math.ceil(container.offsetHeight / this.itemHeight) + 2;
this.init();
}
init() {
// 创建虚拟内容容器
this.content = document.createElement('div');
this.content.className = 'virtual-content';
this.content.style.height = (this.totalItems * this.itemHeight) + 'px';
this.container.appendChild(this.content);
// 监听滚动事件
this.container.addEventListener('scroll', () => {
this.render();
}, { passive: true });
// 初始渲染
this.render();
}
render() {
const scrollTop = this.container.scrollTop;
const startIndex = Math.floor(scrollTop / this.itemHeight);
const endIndex = Math.min(startIndex + this.visibleCount, this.totalItems - 1);
// 清空当前内容
this.content.innerHTML = '';
// 渲染可见区域的元素
for (let i = startIndex; i <= endIndex; i++) {
const item = this.createItem(i);
item.style.position = 'absolute';
item.style.top = (i * this.itemHeight) + 'px';
item.style.left = '0';
item.style.right = '0';
this.content.appendChild(item);
}
}
createItem(index) {
const item = document.createElement('div');
item.className = 'list-item';
item.innerHTML = `
<div class="avatar">${index + 1}</div>
<div class="content">
<div class="name">用户 ${index + 1}</div>
<div class="email">user${index + 1}@example.com</div>
</div>
<div class="status ${index % 2 === 0 ? 'online' : 'offline'}">
${index % 2 === 0 ? '在线' : '离线'}
</div>
`;
return item;
}
setData(totalItems) {
this.totalItems = totalItems;
this.content.style.height = (this.totalItems * this.itemHeight) + 'px';
this.render();
}
destroy() {
this.container.removeEventListener('scroll', this.render);
this.content.remove();
}
}
// 使用示例
const virtualScroll = new VirtualScroll(document.getElementById('container'), {
itemHeight: 70,
totalItems: 10000
});</code></pre>
</div>
<div class="tips">
<h4>虚拟滚动的优势</h4>
<ul>
<li><strong>大幅减少DOM节点</strong>:只渲染可视区域的元素,DOM节点数量大幅减少</li>
<li><strong>提升渲染性能</strong>:减少重排重绘,提升页面流畅度</li>
<li><strong>降低内存占用</strong>:DOM节点减少,内存占用显著降低</li>
<li><strong>支持海量数据</strong>:可以轻松处理数万甚至数十万条数据</li>
<li><strong>滚动流畅</strong>:滚动性能不受数据量影响</li>
</ul>
<h4>适用场景</h4>
<ul>
<li>长列表展示(用户列表、订单列表等)</li>
<li>数据表格(大量行数据)</li>
<li>聊天记录(历史消息)</li>
<li>时间轴(大量时间节点)</li>
<li>任何需要展示大量数据的场景</li>
</ul>
<h4>实现要点</h4>
<ul>
<li><strong>计算可视区域</strong>:根据容器高度和项目高度计算可见数量</li>
<li><strong>设置总高度</strong>:使用占位元素设置正确的滚动高度</li>
<li><strong>动态渲染</strong>:根据滚动位置动态渲染可见元素</li>
<li><strong>性能优化</strong>:使用passive事件监听器,避免阻塞滚动</li>
<li><strong>预加载</strong>:多渲染几个元素,避免滚动时出现空白</li>
</ul>
</div>
</div>
<script>
let normalDomCount = 0;
let virtualDomCount = 0;
let totalDataCount = 0;
// 普通滚动 - 渲染所有元素
function loadNormalList() {
const container = document.getElementById('normalContainer');
clearNormal();
const startTime = performance.now();
const fragment = document.createDocumentFragment();
for (let i = 0; i < 10000; i++) {
const item = document.createElement('div');
item.className = 'list-item';
item.innerHTML = `
<div class="avatar">${i + 1}</div>
<div class="content">
<div class="name">用户 ${i + 1}</div>
<div class="email">user${i + 1}@example.com</div>
</div>
<div class="status ${i % 2 === 0 ? 'online' : 'offline'}">
${i % 2 === 0 ? '在线' : '离线'}
</div>
`;
fragment.appendChild(item);
}
container.appendChild(fragment);
const endTime = performance.now();
normalDomCount = 10000;
totalDataCount = 10000;
document.getElementById('normalDomCount').textContent = normalDomCount;
document.getElementById('totalData').textContent = totalDataCount;
updateStats();
}
// 虚拟滚动 - 只渲染可见元素
function loadVirtualList() {
const container = document.getElementById('virtualContainer');
clearVirtual();
const itemHeight = 70;
const totalItems = 10000;
const containerHeight = container.offsetHeight;
const visibleCount = Math.ceil(containerHeight / itemHeight) + 2;
// 创建虚拟内容容器
const content = document.createElement('div');
content.className = 'virtual-content';
content.style.height = (totalItems * itemHeight) + 'px';
container.appendChild(content);
// 渲染函数
function render() {
const scrollTop = container.scrollTop;
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(startIndex + visibleCount, totalItems - 1);
content.innerHTML = '';
for (let i = startIndex; i <= endIndex; i++) {
const item = document.createElement('div');
item.className = 'list-item';
item.style.position = 'absolute';
item.style.top = (i * itemHeight) + 'px';
item.style.left = '0';
item.style.right = '0';
item.innerHTML = `
<div class="avatar">${i + 1}</div>
<div class="content">
<div class="name">用户 ${i + 1}</div>
<div class="email">user${i + 1}@example.com</div>
</div>
<div class="status ${i % 2 === 0 ? 'online' : 'offline'}">
${i % 2 === 0 ? '在线' : '离线'}
</div>
`;
content.appendChild(item);
}
virtualDomCount = endIndex - startIndex + 1;
document.getElementById('virtualDomCount').textContent = virtualDomCount;
updateStats();
}
// 监听滚动事件
container.addEventListener('scroll', render, { passive: true });
// 初始渲染
render();
totalDataCount = 10000;
document.getElementById('totalData').textContent = totalDataCount;
}
function clearNormal() {
document.getElementById('normalContainer').innerHTML = '';
normalDomCount = 0;
document.getElementById('normalDomCount').textContent = 0;
updateStats();
}
function clearVirtual() {
const container = document.getElementById('virtualContainer');
container.innerHTML = '';
container.removeEventListener('scroll', () => {});
virtualDomCount = 0;
document.getElementById('virtualDomCount').textContent = 0;
updateStats();
}
function updateStats() {
if (normalDomCount > 0 && virtualDomCount > 0) {
const reduction = ((normalDomCount - virtualDomCount) / normalDomCount * 100).toFixed(1);
document.getElementById('domReduction').textContent = reduction + '%';
}
}
</script>
</body>
</html>
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
HTML
1
https://gitee.com/wangxios/workspace_javascript.git
git@gitee.com:wangxios/workspace_javascript.git
wangxios
workspace_javascript
JavaScript案例合集
master

搜索帮助