todolist相信大家在学js的时候都做过,今天结合一下性能优化来处理todolist
编写todolist
主要功能: 1.todo list 由若干项组成,每一项包含一个勾选框,文本,编辑按钮和删除按钮。点击编辑按钮可以编辑该项的文本,点击删除可以删除该项 2. 顶部有新增,删除(删除所有勾选项),全选三个按钮
整体效果如下
编辑:
要实现上面的功能第一步就是对组件进行拆分:当然对于这个demo你也可以选择不拆,直接在一个组件中写,这样就不存在数据的共享了,我这里是拆成了四个组件,不同的功能对应一个组件,如下图所示,红色的是整个外层容器,绿色的主要是一个input输入框,用来添加todolist项以及删除,全选。黄色的是整个列表,紫色的每一个的项。
组件拆分完后就要进行数据的共享问题了,因为存在兄弟组件,所以组件的通信要么用redux等其他状态库,要么就只有使用props将数据存在他们公共的父组件了,也就只有最外层的红色组件中了,
**项目结构:
App中
Content中
性能优化
下一步就是需要对10000条数据的性能优化了,这里我使用的是虚拟列表的方式,也就是只展示可视区域中的数据的dom,这样浏览器的解析速度就会更快一点
布局:
关于虚拟列表的实现网上已经有很多好的文章,我这里就不讲了。最后附上代码:
App:
import "./App.css";
import React, { PureComponent } from "react";
import Header from "./Header";
import Content from "./Content";
class App extends PureComponent {
constructor() {
super();
this.state = {
data:new Array(10000).fill(1).map((item,index)=>{
return {id:index,value:`标题${index}`,checked:false}
})
};
}
//添加
addData = (value) => {
const { data } = this.state;
const newData = {
value,
id: Math.random().toString().split(5),
checked: false,
};
this.setState({
data: [...data, newData],
});
};
//删除
deleteItem = (id) => {
console.log(id);
const { data } = this.state;
const deleteList = data.filter((item) => item.id !== id);
this.setState({
data: deleteList,
});
};
//编辑
edit = (e) => {
const { data } = this.state;
this.setState((prevState) => ({
data: prevState.data.map((item) => {
if (item.id === e.id) {
return { ...item, value: e.value };
}
return item;
}),
}));
};
//多选
changeCheck = (e) => {
console.log(e);
this.setState((prevState) => ({
data: prevState.data.map((item) => {
if (item.id === e.id) {
return { ...item, checked: e.checked };
}
return item;
}),
}));
};
//全选
allChecked = () => {
this.setState((prevState) => ({
data: prevState.data.map((item) => {
return { ...item, checked: true };
}),
}));
};
//批量删除
deleteCheckedItem = () => {
const { data } = this.state;
const deleteList = data.filter((item) => item.checked !== true);
this.setState({
data: deleteList,
});
};
render() {
const { data } = this.state;
return (
<div>
<Header
addData={this.addData}
allChecked={this.allChecked}
deleteCheckedItem={this.deleteCheckedItem}
/>
<Content
data={data}
deleteItem={this.deleteItem}
edit={this.edit}
changeCheck={this.changeCheck}
/>
</div>
);
}
}
export default App;
Header
import React, { PureComponent } from "react";
export default class Header extends PureComponent {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
addItem = () => {
const { addData } = this.props;
const { value } = this.inputRef.current;
addData(value);
this.inputRef.current.value = "";
};
render() {
const { allChecked, deleteCheckedItem } = this.props;
return (
<div>
<input type="text" ref={this.inputRef} />
<button onClick={this.addItem}>新增</button>
<button onClick={deleteCheckedItem}>删除</button>
<button onClick={allChecked}>全选</button>
</div>
);
}
}
Content:
import React, { PureComponent } from "react";
import Item from "./Item";
export default class Content extends PureComponent {
constructor() {
super();
this.state = {
start: 0, //开始位置
end: 0, //结束位置
offset: 0, //偏移量
};
this.screenHeight = 180;
}
componentDidMount() {
this.setState({
end: Math.ceil(this.screenHeight / 30), //初次渲染dom的个数
});
}
scroll = (e) => {
//位置
console.log(e.target.scrollTop);
let scrollTop = e.target.scrollTop;
//开始索引
const start = Math.ceil(scrollTop / 30);
//结束索引
const end = start + Math.ceil(this.screenHeight / 30);
//偏移量
let offset = scrollTop - (scrollTop % 30);
console.log(scrollTop, start, end, offset);
this.setState({
start,
end,
offset,
});
};
render() {
const { start, end, offset } = this.state;
console.log("xxx", start, end, offset);
console.log(this.props);
const { data, deleteItem, edit, changeCheck } = this.props;
const listHeight = data.length * 30;
return (
<div
style={{
position: "relative",
height: this.screenHeight,
overflow: "auto",
width: 300,
border: "1px solid #ccc",
}}
onScroll={this.scroll}
>
<div
style={{
height: listHeight + "px",
position: "absolute",
left: 0,
top: 0,
right: 0,
zIndex: -1,
}}
></div>
<div
style={{
transform: `translate(0,${offset}px)`,
left: 0,
right: 0,
top: 0,
position: "absolute",
}}
>
{data.slice(start, Math.min(end, data.length)).map((item) => (
<Item
offset={offset}
item={item}
edit={edit}
key={item.id}
deleteItem={deleteItem}
changeCheck={changeCheck}
/>
))}
</div>
</div>
);
}
}
Item:
import React, { PureComponent } from "react";
export default class Item extends PureComponent {
constructor(props) {
super(props);
this.state = {
editState: false,
// checked:false
};
this.itemInputRef = React.createRef();
}
//编辑
editItem = (e) => {
console.log(e);
this.setState({
editState: true,
});
};
//多选
changeCheck = (status) => {
console.log(status.target);
const { item, changeCheck } = this.props;
const checkObj = {
checked: !item.checked,
id: item.id,
};
changeCheck(checkObj);
};
//编辑确定
confimEdit = () => {
const { edit, item } = this.props;
console.log(this.props);
const { value } = this.itemInputRef.current;
edit({
id: item.id,
value,
});
this.setState({
editState: false,
});
};
//删除
deleteItem = () => {
const {
item: { id },
deleteItem,
} = this.props;
console.log(id);
deleteItem(id);
};
render() {
const { editState } = this.state;
const { item, changeCheck, offset } = this.props;
return (
<div
style={{
height: 30,
}}
>
<input
type="checkbox"
checked={item.checked}
onChange={this.changeCheck}
/>
{editState ? (
<div style={{ display: "inline-block" }}>
<input
type="text"
defaultValue={item.value}
ref={this.itemInputRef}
/>
</div>
) : (
<span>{item.value}</span>
)}
{editState ? (
<button onClick={this.confimEdit}>确定</button>
) : (
<button onClick={this.editItem}>编辑</button>
)}
<button onClick={this.deleteItem}>删除</button>
</div>
);
}
}