关于react基础及组件化编程的可以参考本篇笔记
对照代码查看笔记: 代码详情
- 有详细的ReadMe文档
- 欢迎star+fork到自己的仓库
- 后续将进行todolist案例的详细讲解:vue2,vue3,react
文章目录
一、 React中的事件处理
建议使用非受控组件
- 不要过度使用ref
1-1 例子: 不要过度使用ref
- 通过onXxx属性指定事件处理函数(注意大小写)
- a.React使用的是自定义(合成)事件,而不是使用的原生DOM事件 —为了更好的兼容性
- b.React中的事件是通过事件委托方式处理的 (委托给组件最外层的元素)—为了高校
- 通过event.target得到发生事件的DOM元素对象----不要过度使用ref
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="test"></div>
<div id="test2"></div>
<div id="test3"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script
type="text/javascript"
src="../js/react-dom.development.js"
></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
// 创建组件
class Demo extends React.Component {
showData2 = (event) => {
// alert(this.myRef2.current.value);
alert(event.target.value);
};
render() {
return (
<div>
<input type="text" onBlur={this.showData2} />
</div>
);
}
}
ReactDOM.render(<Demo />, document.getElementById("test"));
</script>
</body>
</html>
1-2 受控组件与非受控组件
1-2-1 受控组件
- 类似vue中的双向数据绑定
- 所有输入类的DOM,将输入维护在state中,使用时直接从状态中取出来
<script type="text/babel">
class Login extends React.Component {
// 初始化状态
state = {
username: "", // 用户名
password: "", // 密码
};
saveUsername = (event) => {
this.setState({ username: event.target.value });
};
savePassword = (event) => {
this.setState({ password: event.target.value });
};
handleSubmit = (event) => {
event.preventDefault(); // 组织表单提交
const { username, password } = this;
alert(
`你输入的用户名是: ${username.value},你输入的密码是:${password.value}`
);
};
render() {
return (
<form action="http" onSubmit={this.handleSubmit}>
用户名:
<input type="text" onChange={this.saveUsername} name="username" />
密码:
<input
type="password"
name="password"
onChange={this.savePassword}
/>
<button>登录</button>
</form>
);
}
}
// 渲染组件
ReactDOM.render(<Login />, document.getElementById("test"));
</script>
1-2-2 非受控组件
现用现取
<script type="text/babel">
class Login extends React.Component {
handleSubmit = (event) => {
event.preventDefault(); // 阻止表单提交
const { username, password } = this;
alert(`你输入的用户名是${username.value},密码是${password.value}`);
};
render() {
return (
<form onSubmit={this.handleSubmit}>
用户名:
<input
ref={(c) => (this.username = c)}
type="text"
name="username"
/>
密码:
<input
ref={(c) => (this.password = c)}
type="text"
name="password"
/>
<button>登录</button>
</form>
);
}
}
// 渲染组件
ReactDOM.render(<Login />, document.getElementById("test"));
</script>
1-3 高阶函数
- 如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数
- 若A函数,接收的参数是一个函数,那么A就可以成为高阶函数
- 若A函数,调用的返回值依然是一个函数,那么A就可以称为高阶函数
- 常见的高阶函数: Promise/setTimeout/arr.map()等等
saveFormData = (dataType) => {
// 无关的输出
console.log(dataType);
// 真正返回的值是函数
return (event) => {
console.log(dataType, event.target.value);
// this.setState({ password: event.target.value });
};
};
1-3-1 函数的柯里化
- 函数的柯里化: 通过函数调用继续返回函数的方式。实现多次接收参数最后统一处理的函数编码形式
function(a) {
function(b) {
function(c) {
return a+b+c;
}
}
}
1-3-2 不用柯里化的写法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="test"></div>
<div id="test2"></div>
<div id="test3"></div>
<script type="text/javascript" src="../../js/react.development.js"></script>
<script
type="text/javascript"
src="../../js/react-dom.development.js"
></script>
<script type="text/javascript" src="../../js/babel.min.js"></script>
<script type="text/babel">
class Login extends React.Component {
// 初始化状态
state = {
username: "", // 用户名
password: "", // 密码
};
// 保存表单数据到状态中
saveFormData = (dataType, event) => {
this.setState({ [dataType]: event.target.value });
};
handleSubmit = (event) => {
event.preventDefault(); // 组织表单提交
const { username, password } = this.state;
alert(`你输入的用户名是: ${username},你输入的密码是:${password}`);
};
render() {
return (
<form action="http" onSubmit={this.handleSubmit}>
用户名:
<input
type="text"
// 些事件回调的时候不要加小括号,加了就是将saveFormData的返回值作为onChange的回调;返回值是undefined
onChange={(event) => this.saveFormData("username", event)}
name="username"
/>
密码:
<input
type="password"
name="password"
onChange={((event) => this.saveFormData("password"), event)}
/>
<button>登录</button>
</form>
);
}
}
// 渲染组件
ReactDOM.render(<Login />, document.getElementById("test"));
</script>
</body>
</html>
二、生命周期
2-1 引出生命周期
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="test"></div>
<div id="test2"></div>
<div id="test3"></div>
<script type="text/javascript" src="./js/react.development.js"></script>
<script type="text/javascript" src="./js/react-dom.development.js"></script>
<script type="text/javascript" src="./js/babel.min.js"></script>
<script type="text/babel">
class Life extends React.Component {
state = { opacity: 0.5 };
death = () => {
// 卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById("test"));
// 消除定时器
clearInterval(this.timer);
};
// action = () => {
// setInterval(() => {
// let { opacity } = this.state;
// // 减小0.1
// opacity -= 0.1;
// if (opacity <= 0) opacity = 1;
// // 设置新的透明度
// this.setState({ opacity });
// }, 200);
// };
// 初始化渲染、状态更新之后
// 该状态就重新改变render
render() {
console.log("render");
// setInterval(() => {
// let { opacity } = this.state;
// opactiy -= 0.1;
// if (opacity <= 0) {
// opacity = 1;
// }
// this.setState({ opacity });
// }, 200);
return (
<div>
<h2 style={{ opacity: this.state.opacity }}>
React学不会怎么办?
</h2>
<button onClick={this.death}>不活了</button>
</div>
);
}
// 组件挂载完毕
componentDidMount() {
console.log("@");
this.timer = setInterval(() => {
let { opacity } = this.state;
opacity -= 0.1;
if (opacity <= 0) {
opacity = 1;
}
this.setState({ opacity });
}, 200);
}
// 组件将要卸载
componentWillUnmount() {
// 消除定时器
clearInterval(this.timer);
}
}
// 渲染组件
ReactDOM.render(<Life />, document.getElementById("test"));
</script>
</body>
</html>
2-2 组件生命周期
2-2-1 生命周期钩子【新旧】
react生命周期(旧)
react生命周期新
2-2-2 生命周期的三个阶段(旧)
2-2-2-1 基本介绍
-
初始化阶段: 由ReactDOM.render()触发----初次渲染
-
constructor()
-
componentWillMount() : 组件将要挂载的钩子
-
render()
-
componentDidMount()
- 一般在这个钩子中做一些初始化的事,例如: 开启定时器、发送网络请求、订阅消息
- 组件挂载完毕的钩子
-
-
componentWillReceiveProps :
- 组件将要接收props调用
-
更新阶段: 由组件内部this.setState()或父组件重新render触发
- shouldComponentUpdate()
- 阀门:组件是否应该被更新
- 不写持续返回true
- componentWillUpdate()
- render()
- componentDidUpdate()
- shouldComponentUpdate()
-
卸载组件:由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount()
- 关闭定时器、取消订阅消息
2-2-2-2 例子 setState触发更新
强制更新不受阀门控制
- 代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="test"></div>
<div id="test2"></div>
<div id="test3"></div>
<script type="text/javascript" src="./js/react.development.js"></script>
<script type="text/javascript" src="./js/react-dom.development.js"></script>
<script type="text/javascript" src="./js/babel.min.js"></script>
<script type="text/babel">
// 创建组件
class Count extends React.Component {
// 构造器
constructor(props) {
console.log("Count-constructor");
super(props);
}
// 初始化状态
state = { count: 0 };
// 加1按钮的回调
add = () => {
// 获取原状态
const { count } = this.state;
// 更新状态
this.setState({ count: count + 1 });
};
// 卸载组件按钮的回调
death = () => {
ReactDOM.unmountComponentAtNode(document.getElementById("test"));
};
//强制更新按钮的回调
force = () => {
this.forceUpdate();
};
// 组件将要挂载的钩子
componentWillMount() {
console.log("Count---componentWillMount");
}
// 组件挂载完毕的钩子
componentDidMount() {
console.log("Count---componentDidMount");
}
// 组件将要卸载的钩子
componentWillUnmount() {
console.log("Count--componentWillUnmount");
}
// 控制组件更新的阀门
shouldComponentUpdate() {
console.log("Count---shouldComponentUpdate");
return true;
}
// 组件将要更新的钩子
componentWillUpdate() {
console.log("Count--componentWillUpdate");
}
componentDidUpdate() {
console.log("Count---componentDidUpdate");
}
render() {
console.log("Count---render");
const { count } = this.state;
return (
<div>
<h2>当前求和为: {count}</h2>
<button onClick={this.add}>点我—+1</button>
<button onClick={this.death}>卸载组件</button>
</div>
);
}
}
// 渲染组件
ReactDOM.render(<Count />, document.getElementById("test"));
</script>
</body>
</html>
- 展示
2-2-2-3 例子 componentWillReceiveProps :
- 组件将要接收props调用
- 第一次传递的props不算
- 还可以接收参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="test"></div>
<div id="test2"></div>
<div id="test3"></div>
<script type="text/javascript" src="./js/react.development.js"></script>
<script type="text/javascript" src="./js/react-dom.development.js"></script>
<script type="text/javascript" src="./js/babel.min.js"></script>
<script type="text/babel">
// 父组件render流程
class A extends React.Component {
// 初始化状态
state = { carName: "奔驰" };
changeCar = () => {
this.setState({ carName: "奥托" });
};
render() {
return (
<div>
<div>我是A组件</div>
<button onClick={this.changeCar}>换车</button>
<B carName={this.state.carName} />
</div>
);
}
}
class B extends React.Component {
componentWillReceiveProps() {
console.log("B---componentWillReceiveProps");
}
render() {
return <div>我是B组件,接收到的车是: {this.props.carName}</div>;
}
}
// 渲染组件
ReactDOM.render(<A />, document.getElementById("test"));
</script>
</body>
</html>
2-2-3 生命周期的三个阶段(新)
getDerivedStateFormProps(得到派生的状态)
- UNSAFE_componentWillMount():组件将要挂载的钩子
- UNSAFE_componentWillUpdate: 组件将要更新的钩子
-
初始化阶段: 由ReactDOM.render()触发----初次渲染
-
constructor()
-
getDerivedStateFormProps
-
render()
-
componentDidMount()
- 一般在这个钩子中做一些初始化的事,例如: 开启定时器、发送网络请求、订阅消息
- 组件挂载完毕的钩子
-
-
更新阶段: 由组件内部this.setState()或父组件重新render触发
- getDerivedStateFormProps
- shouldComponentUpdate()
- render()
- getSnapshotUpdate()
- componentDidUpdate()
-
卸载组件:由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount()
2-2-4 DOM的diffing算法
- 经典面试题:
- react/vue中的key有什么作用?(key的内部原理是什么?)
- 为什么遍历列表,key最好不要用index
- 虚拟DOM中key的作用:
- key是虚拟DOM对象的标识,在更新显示时key起着极其重要的作用
- 当状态中的数据发生改变时,react会根据【新数据】生成【新的虚拟DOM】,随后React进行【新虚拟DOM】和【旧虚拟DOM】的diff比较
- 旧虚拟DOM中找到了与新虚拟DOM相同的key :
- 若虚拟DOM中内容没变,直接使用之前真实DOM
- 若虚拟DOM中内容变了,则生成新的真实DOM
- 旧虚拟DOM中未找到与新虚拟DOMi相同的key
- 根据数据创建新的真实DOM,随后渲染到页面
- 旧虚拟DOM中找到了与新虚拟DOM相同的key :
- 用index作为key可能会引发的问题:
- 弱队数据进行:逆序添加、逆序删除等破坏顺序操作
- 会产生没有必要的真实DOM更新 ===> 界面效果没问题,但效率低
- 如果结果还包含输入类DOM :
- 会产生错误DOM更新–> 界面有问题
- 如果不存在对数据的逆序添加、逆序删除等破环删除操作
- 仅用于渲染列表用于展示,使用index作为key没有问题
- 弱队数据进行:逆序添加、逆序删除等破坏顺序操作