我們經常會遇到這種需求,根據資料展示列表。這種程式碼估計你已經擼過成百上千次了。
但如果你需要同時展示成千上萬條資料呢,必然會造成瀏覽器卡頓,丟幀,甚至卡死的問題。
本文將介紹利用react-virtualized來高效渲染大資料量列表。
開始吧!
首先建立一個React 應用
create-react-app virtualization
複製程式碼
應用將展示1000條如下這樣的評論:
我們引入第三方庫lorem-ipsum來生成模擬資料:
cd virtualization
npm install --save lorem-ipsum
複製程式碼
在 src/App.js
中引入 lorem-ipsum
:
import loremIpsum from 'lorem-ipsum';
複製程式碼
接下來創造一個1000條資料的陣列:
const rowCount = 1000;
class App extends Component {
constructor() {
super();
this.list = Array(rowCount).fill().map((val, idx) => {
return {
id: idx,
name: 'John Doe',
image: 'http://via.placeholder.com/40',
text: loremIpsum({
count: 1,
units: 'sentences',
sentenceLowerBound: 4,
sentenceUpperBound: 8
})
}
});
}
//...
}
複製程式碼
以上每條資料都包含 id
、使用者名稱
、圖片
、隨機生成4~8個字評論
。
在 render()
中使用這個陣列:
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<div className="list">
{this.list.map(this.renderRow.bind(this))}
</div>
</div>
);
}
複製程式碼
增加 renderRow()
來創造列:
renderRow(item) {
return (
<div key={item.id} className="row">
<div className="image">
<img src={item.image} alt="" />
</div>
<div className="content">
<div>{item.name}</div>
<div>{item.text}</div>
</div>
</div>
);
}
複製程式碼
在 src/App.css
中加點樣式:
.list {
padding: 10px;
}
.row {
border-bottom: 1px solid #ebeced;
text-align: left;
margin: 5px 0;
display: flex;
align-items: center;
}
.image {
margin-right: 10px;
}
.content {
padding: 10px;
}
複製程式碼
好了,啟動專案 yarn start
,可以看到如下畫面:
檢視元素,我們可以看到 DOM
上掛載了非常多的 div
:
我們來測測效能
如果你用的是 Chrome,只需幾步,快速測試效能:
- 開啟開發者工具
- 按Command+Shift+P (Mac) or Control+Shift+P (Windows, Linux) 來開啟命令選單
- 輸入
render
,下拉框中選擇Show Rendering
。 - 點選
render
頁籤,FPS Meter
前打鉤。 - 滾動列表
我們可以看到,當滾動條滾動的時候,幀率從60掉到了38左右。這還是隻有1000條資料情況,如果再增大資料,瀏覽器會出現卡頓,甚至卡死。
接著我們來看看react-virtualized是如何提高效能的?
react-virtualized原理
核心原理:只渲染你所見的。
上面的應用渲染了1000條評論,但螢幕只為你展示了10來條資料,那另外990條的渲染就是浪費的。
如果我們只渲染可見的評論,當滑鼠滾動檢視更多的時候,將新的節點替換舊的節點。這樣就完美解決了效能瓶頸的問題。
怎麼用呢?
首先在 src/App.js
中,引入 List
元件:
import { List } from "react-virtualized";
複製程式碼
替換 render()
原有程式碼:
<div className="list">
{this.list.map(this.renderRow.bind(this))}
</div>
複製程式碼
使用 List
元件
const listHeight = 600;
const rowHeight = 50;
const rowWidth = 800;
//...
<div className="list">
<List
width={rowWidth}
height={listHeight}
rowHeight={rowHeight}
rowRenderer={this.renderRow.bind(this)}
rowCount={this.list.length} />
</div>
複製程式碼
改寫 renderRow()
:
renderRow({ index, key, style }) {
return (
<div key={key} style={style} className="row">
<div className="image">
<img src={this.list[index].image} alt="" />
</div>
<div className="content">
<div>{this.list[index].name}</div>
<div>{this.list[index].text}</div>
</div>
</div>
);
}
複製程式碼
啟動應用 yarn start
:
檢視元素:
可以看到只渲染了可見的元素。
效能怎麼樣呢?
用上面相同的方法測試:
可以看到基本維持在60幀左右。效能槓槓的(^_^)
以上只是對react-virtualized的簡單應用,感興趣可以去試試!
感謝閱讀!
本文翻譯自Esteban Herrera的Rendering large lists with React Virtualized