1. 列表渲染
1.1 列表渲染说明:
- 列表渲染 先将列表数据转为React元素列表, 然后在渲染
- React会将React元素列表自动展开渲染
1.2 React元素列表渲染
说明:
- React元素列表是一个数组
- 只不过数组的每一项都是JSX语法的React元素
示例代码:
// 不使用到组件状态,因此定义函数组件测试
function MyCom(){
// 1. react元素列表,数组的每一项都是JSX语法的React元素
// 将react元素列表保存在变量中
let oList = [
<li>苹果</li>,
<li>香蕉</li>,
<li>梨子</li>
]
// 2.在react元素中使用{}语法将列表渲染
// react会自动遍历列表,并将列表中的react元素进行渲染
return <ul> { oList } </ul>
}
// 将组件渲染到页面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
1.3 渲染列表数据
说明:
- React会将react元素列表遍历渲染
- 因此如果要渲染列表数据,就可以先将列表数据转为react元素列表
- 遍历列表的方式有很多中,普通for循环,
for-in
,for-of
,forEach
,map
,reduce
都可以使用
1.3.1 使用普通for循环遍历列表数据
示例代码:
function MyCom(){
// 1. 列表数据
let listdata = ["苹果","香蕉","梨子"]
// 2. 普通for循环
let oList = [];
for(let i = 0; i< listdata.length ;i++){
// 循环列表数据,将每一个数据包裹一层react元素,
// 在push到新的oList数组中,oList就是react元素列表
oList.push(<li>{listdata[i]}</li>)
}
return <ul> { oList } </ul>
}
1.3.2 使用for-in遍历列表数据
示例代码:
function MyCom(){
// 1. 列表数据
let listdata = ["苹果","香蕉","梨子"]
// 2. for-in循环
let oList = [];
for(let index in listdata){
// index 为listdata列表的索引
// 通过索引取出数据包裹react元素,组成react元素列表
oList.push(<li>{listdata[index]}</li>)
}
return <ul> { oList } </ul>
}
// 将组件渲染到页面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
1.3.3 使用for-of遍历列表数据
示例代码:
function MyCom(){
// 1. 列表数据
let listdata = ["苹果","香蕉","梨子"]
// 2. for-of循环
let oList = [];
for(let value of listdata){
// for-of 直接取出数据,处理为react元素.列表
oList.push(<li>{value}</li>)
}
return <ul> { oList } </ul>
}
// 将组件渲染到页面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
1.3.4 使用forEach遍历列表数据
示例代码如下:
function MyCom(){
// 1. 列表数据
let listdata = ["苹果","香蕉","梨子"]
// 2. forEach循环
let oList = [];
// forEach循环将列表数据转为react元素列表
listdata.forEach(value => {
oList.push(<li>{value}</li>)
})
return <ul> { oList } </ul>
}
// 将组件渲染到页面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
1.3.5 使用map遍历列表数据
map会自动返回列表,因此不用使用push方法
示例代码如下:
function MyCom(){
// 1. 列表数据
let listdata = ["苹果","香蕉","梨子"]
// 2. map循环
// map 返回的就是回调函数return内容组成的数组
let oList = listdata.map(value => {
return <li>{ value }</li>
})
return <ul> { oList } </ul>
}
// 将组件渲染到页面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
1.3.6 使用reduce遍历列表数据
示例代码:
function MyCom(){
// 1. 列表数据
let listdata = ["苹果","香蕉","梨子"]
// 2. reduce循环
let oList = listdata.reduce((prev,value) => {
// 没事将数据包裹react元素后在push到数组中
prev.push(<li>{ value }</li>)
// 返回数组
return prev
},[])
return <ul> { oList } </ul>
}
// 将组件渲染到页面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
比较常用的方法是: map和reduce两种方法来渲染列表数据
2. 列表渲染的key属性
2.1 可以属性的认识
-
在列表渲染中使用key来标记每一项,让React能识别每一个元素,
-
这样如果遇到添加或删除,会很有帮助,key属性要这个元素成为唯一
-
key
属性的值最好是这个元素列表中独一无二的字符串,通常我们用id来 作为元素的key -
当元素没有id时,万不得已的情况下 可以使用index索引来作为key,通常不建议,因为这会导致性能的下降
-
如果你不指定可以,React将默认用索引index作为key,并在控制台提醒添加key属性
注意:
- key只在兄弟节点之间必须唯一
- key属性值会传递给React,不会传递给组件,如果需要 传递给组件,请使用其他属性传递
2.2 使用key属性渲染列表数据
// 1. 列表数据
let names = ["HTML,CSS","JavaScript","Node","Vue","React"]
// 2. 创建虚拟DOM
const ul = (
<ul>
{ names.map( (item,index) => <li key={index}>{item}</li>) }
</ul>
)
// 3. 渲染虚拟DOM
ReactDOM.render(ul, document.getElementById('list'))
3. 受控组件与非受控组件
3.1 受控组件与非受控制组件说明
- 受控组件是指可以被rect状态控制的组件
- 非受控组件是可以任意输入内容,不受组件状态的控制,默认是就是非受控组件
3.2 非受控组件
说明:
- 非受控组件不受react状态影响
- 如果需要获取非受控组件的数据,使用ref属性
示例代码:
class MyCom extends React.Component{
// 组件状态
state = {
username:"",
password:""
}
handleClick = () =>{
// console.log(this);
// 获取数据
let username = this.userInput.value.trim();
let password = this.passwordInput.value.trim();
// 判断输入是否为空
if(!username || !password){
console.log("用户名或者密码不能为空")
return;
}
// console.log(username,":",password)
// 将数据添加到状态中
this.setState(() => ({
username,password
}), () => {
console.log("数据已经添加到状态中");
})
}
render(){
return (
<div>
<h2>非受控组件获取数据</h2>
<p>
用户名:
<input
type="text"
ref={input => this.userInput = input}
/>
</p>
<p>
密 码:
<input type="password"
ref={input => this.passwordInput = input}
/>
</p>
<button onClick={ this.handleClick }>点击</button>
</div>
)
}
}
// 将组件渲染到页面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
3.3 受控组件
说明:
- 受控组件是指将表单绑定到状态上,
- 状态的值一但发生变化,输入空的内容也会发生变化,
- 状态的值不变,输入框不能输入任何内容
- 也就是说输入框中的内容被状态所控制
示例代码如下:
class MyCom extends React.Component{
// 组件状态
state = {
username:"",
password:""
}
userChangeHandle = (event) =>{
// // 获取用户名数据
let username = event.target.value.trim();
// 判断输入是否为空
if(!username){
console.log("用户名不能为空")
return;
}
// console.log(username,":",password)
// 将数据添加到状态中
this.setState(() => ({
username
}), () => {
console.log("用户名数据已经添加到状态中");
})
}
pwChangeHandle = (event) =>{
// console.log(this);
// // 获取密码数据
let password = event.target.value.trim();
// 判断输入是否为空
if(!password){
console.log("密码不能为空")
return;
}
// console.log(username,":",password)
// 将数据添加到状态中
this.setState(() => ({
password
}), () => {
console.log("密码数据已经添加到状态中");
})
}
render(){
// 1. 获取状态中的数据
let {username, password} = this.state
// 2. 将状态中的数据绑定到输入框,此时输入框就是受控组件
return (
<div>
<h2>非受控组件获取数据</h2>
<p>
用户名:
<input
type="text"
value={username}
onChange = { this.userChangeHandle }
/>
</p>
<p>
密 码:
<input type="password"
value={password}
onChange = { this.pwChangeHandle }
/>
</p>
<button onClick={ this.handleClick }>点击</button>
</div>
)
}
}
// 将组件渲染到页面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
- 示例中标签输入内容没有任何限制,不被状态控制,
- 在通过ref
示例说明:
- 输入框的value值绑定到了状态中的数据,因此输入框就被状态控制
- 此时输入框不能输入任何内容, 因为是状态单向影响输入框,而且此时还会报错
- 如果我们希望能在输入框输入的内容,就需要绑定onChange事件
- 当输入内容时,通过onChange绑定的事件函数获取输入的内容,修改状态值
- 状态值一但发生变化,表单就会显示最新的数据. 看起来好像可以输入了
4. 收集表单数据
4.1 收集标签数据说明:
-
受控制和非受控组件都可以收集表单数据
-
但是非受控组件是在操作DOM,
-
受控组件不是在操作DOM, 按照react思想减少DOM操控,所以官网推荐受控组件
4.2 收集标签数据代码
使用受控组件收集标签数据
需求
- 收集用户输入的用户名,密码
- 收集性别爱好,单选复选数据
- 收集select数据
示例代码如下
class MyCom extends React.Component{
// 组件状态
state = {
username:"",
password:"",
sex:"0", // 0为男,1为女
likes:[],
province:""
}
// 处理用户名
userChangeHandle = (event) =>{
// // 获取用户名数据
let username = event.target.value.trim();
// 判断输入是否为空
if(!username){
console.log("用户名不能为空")
return;
}
// console.log(username,":",password)
// 将数据添加到状态中
this.setState(() => ({
username
}))
}
// 处理密码
pwChangeHandle = (event) =>{
// console.log(this);
// // 获取密码数据
let password = event.target.value.trim();
// 判断输入是否为空
if(!password){
console.log("密码不能为空")
return;
}
// console.log(username,":",password)
// 将数据添加到状态中
this.setState(() => ({
password
}))
}
// 处理性别 单选
changeSex = (event) => {
let sex = event.target.value;
this.setState(() => ({
sex
}))
}
// 处理爱好 多选
changeLike = (event) => {
// 获取复选框改变的数据
let like = event.target.value;
// 获取状态中收集爱好的数据
let {likes} = this.state
// 判断状态中是否存在,存在就删除,不存在就添加
if(!likes.includes(like)){
likes.push(like)
}else{
likes = likes.filter(value => {
return like != value
})
}
// 更新数据
this.setState(() => ({
likes
}))
}
// 获取省份数据
changeProvince = (event) => {
let province = event.target.value;
this.setState(() => ({
province
}))
}
render(){
// 1. 获取状态中的数据
let {
username,
password,
sex,
likes,
province
} = this.state
// 2. 将状态中的数据绑定到输入框,此时输入框就是受控组件
return (
<div>
<h2>非受控组件获取数据</h2>
<p>
用户名:
<input
type="text"
value={username}
onChange = { this.userChangeHandle }
/>
</p>
<p>
密 码:
<input type="password"
value={password}
onChange = { this.pwChangeHandle }
/>
</p>
<p>
性别:
<label htmlFor="male">
<input
id="male"
type="radio"
value="0"
checked={sex == 0 }
onChange = { this.changeSex }
/>
男
</label>
<label htmlFor="female">
<input
id="female"
type="radio"
value="1"
checked={sex == 1 }
onChange = { this.changeSex }
/>
女
</label>
</p>
<p>
爱好:
<label htmlFor="like1">
<input
id="like1"
type="checkbox"
value="游泳"
checked={ likes.some(like => like == "游泳") }
onChange = { this.changeLike }
/>
游泳
</label>
<label htmlFor="like2">
<input
id="like2"
type="checkbox"
value="篮球"
checked={ likes.some(like => like == "篮球") }
onChange = { this.changeLike }
/>
篮球
</label>
<label htmlFor="like3">
<input
id="like3"
type="checkbox"
value="足球"
checked={ likes.some(like => like == "足球") }
onChange = { this.changeLike }
/>
足球
</label>
</p>
<p>
省份:
<select
value={province}
onChange={this.changeProvince }
>
<option value="">请选择</option>
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广东">广东</option>
<option value="安徽">安徽</option>
<option value="湖南">湖南</option>
</select>
</p>
<button onClick={ this.handleClick }>点击</button>
</div>
)
}
}
// 将组件渲染到页面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
示例说明:
- 用户名,密码表单受控组件直接绑定value值
- 单选复选通过checked判断是否选择中,因此将状态中用来数据对应数据字段的数据跟value对比
- select受控组件收集数据,将value绑定在select标签上
5. 虚拟DOM与DOM Diff算法

初始化显示界面
- 创建虚拟DOM树(一般JS对象)
- 真实DOM树
- 将虚拟DOM添加真实DOM绘制界面显示
初始化看不出虚拟DOM的优势,优势主要体现在更新,因为页面大部分情况下都是在更新
更新界面
- setState() 更新状态
- 重新创建虚拟DOM树
- 新/旧树比较差异
- 更新差异对应的真实DOM
- 局部界面重绘
为什么状态不能直接就改,而是要通过setState,因为直接修改没法比较,setState会重新创建新状态,生成新的虚拟DOM,可以与原来状态和虚拟DOM进行对比
例子演示;
// 1. 定义组件
class Hello extends React.Component {
constructor (){
super()
// 定义状态
this.state = {
date: new Date()
}
}
componentWillMount(){
setInterval(()=>{
this.setState({
date: new Date()
})
},1000)
}
render(){
console.log('render()')
let {date} = this.state;
return (
<div>
输入内容: <input type="text" /> <span>{date.toTimeString()}</span>
</div>
)
}
}
// 2. 渲染组件标签
ReactDOM.render(
<Hello />,
document.getElementById('box')
)
网友评论