Author: 哇哇小仔
Date: 2021-03-19
Version: V 1.1.0
Description: 根据尚硅谷React视频教程总结的笔记
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库,引入了React对象 -->
<script type='text/javascript' src='./js/react.development.js'></script>
<!-- 引入react-dom,用于支持react操作DOM,引入了ReactDOM对象 -->
<script type='text/javascript' src='./js/react-dom.development.js'></script>
<!-- 引入babel,用于将jsx转为js -->
<script type='text/javascript' src='./js/babel.min.js'></script>
<!-- 此处一定要写babel,就是告诉浏览器,写的是jsx,需要babel转换成js -->
<script type='text/babel'>
// 1. 创建虚拟DOM
/* 此处一定不要写引号,因为不是字符串,这是JSX,
如果想清除一些使用缩进直接在最边加上一个括号 */
const VDOM = (
<h1 id='title'>
<span>Hello React!</span>
</h1>
);
// 2. 渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById('test'));
</script>
</body>
const VDOM = Hello React
const VDOM = React.createElement('h1', {id: 'title'}, 'Hello React')
实际上,JSX 仅仅只是 React.createElement(component, props, …children) 函数的语法糖。
)// 创建函数式组件
function MyComponent(){
console.log(this); // 此处this是undefined,因为babel编译后开启了严格模式
return <h2>我是用函数定义的组件(适用于简单组件的定义)</h2>
}
// 渲染组件到页面
ReactDOM.render(<MyComponent/>, document.getElementById('test'))
// 创建类式组件
class MyComponent extends React.Component {
render(){
// rende是放在哪里的?—— MyComponet的原型对象上的
// render中的this是谁?—— MyComponent组件的实例对象
console.log('render中的this是:', this);
return <h2>我是用类定义的组件(用于复杂组件的定义)</h2>
}
}
// 渲染组件到页面
ReactDOM.render(<MyComponent/>, document.getElementById('test'))
<script type='text/babel'>
// 1. 创建组件
class Weather extends React.Component {
// 构造器调用几次? —— 1次
constructor(props){
super(props);
// 初始化状态
this.state = {isHot: true, wind: '微风'}
// 等号左边的changeWeather是在实例自身创建一个changeWeather
// 等号右边的changeWeather是找到了原型对象上的changeWeather,也就是下边定义的
// bind方法改变了等号左边的函数对象的this,也就是传递的this(也就是Weather的实例对象)
/*
bind() 方法会创建一个新函数,当这个新函数被调用时,
它的 this 值是传递给 bind() 的第一个参数,
它的参数是 bind() 的其他参数和其原本的参数。
*/
this.changeWeather = this.changeWeather.bind(this)
// this.demo = this.changeWeather.bind(this)
}
// render 调用了几次? —— n+1次,1是初始化的那次,n是状态更新的次数
render(){
// 对象的解构赋值,寻找同名属性进行解构
// 读取状态
let {isHot} = this.state;
return (<h2 onClick={this.changeWeather}>
今天天气很{isHot?'炎热':'凉爽'}, {this.state.wind}
</h2>)
// return 今天天气很{isHot?'炎热':'凉爽'}
}
// changeWeather 调用了几次? —— 点几次调用几次
changeWeather(){
// changeWeather 放在了 Weather 的原型对象上,供实例使用
// 由于changeWeather是作为onClick的回调,不是通过实例调用的,是直接调用的
// 类中的方法默认开启了局部的严格模式,严格模式下,禁止this关键字指向全局对象。
// 所以changeWeather中的this为undefined
// 获取原来的isHot值
const isHot = this.state.isHot;
// 严重注意:状态state不能直接更改,下面这行就是直接更改
// this.state.isHot = !isHot; // 这是错误的写法
// 严重注意:状态state只能通过setState修改,且更新是一种合并,不是替换
// 不是替换,因此state里边的wind属性还在
this.setState({isHot: !isHot})
}
}
// 渲染组件到页面
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
<script type='text/babel'>
class Weather extends React.Componenent{
// 初始化状态
state = {isHot: true, wind: '微风'}
render(){
let {isHot} = this.state;
return (<h2 onClick={this.changeWeather}>
今天天气很{isHot?'炎热':'凉爽'}, {this.state.wind}
</h2>)
}
// 自定义方法 —— 要用赋值语句的形式+箭头函数
changeWeather = () => {
// 箭头函数this是静态的,this始终指向函数声明时所在作用域下的 this 的值
// 因此这里的this是Weather的实例对象
const isHot = this.state.isHot;
this.setState({isHot: !isHot})
}
}
// 渲染组件到页面
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
this.props.name
Person.propTypes = {
name: React.PropTypes.string.isRequired,
gender: React.PropTypes.string,
age: React.PropTypes.number,
sayName: React.PropTypes.func
}
//这种方式是写在类的外部
Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number
}
// 简写方式,写在类的内部
static propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number
}
const person = {name:'Danny', age:10}
<Person {...person}>
Person.defaultProps = {
age = 18,
gender = '未知'
}
5)组件类的构造函数
* 构造器是否接收props,是否传递给super,取决于是否希望在构造器中通过this访问props
* 如果构造器没有接收props,没有传递给super,则constructor中访问this.props为undefined
constructor(props){
super(props)
console.log(props) // 打印所有属性
}
<div id='test1'><div>
<div id='test2'><div>
<!-- 引入prop-types 用于对组件标签属性进行限制,全局会多了一个PropTypes对象 -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type='text/babel'>
// 创建虚拟DOM
class Person extends React.Component{
/* constructor(props){ // 一般构造器都不需要写
super(props)
}*/
static propTypes = { // 设置类型和必须性限制
name: PropTypes.string.isRequired,
age: PropTypes.number,
gender: PropTypes.string
}
static defaultProps = { // 设置默认值
age: 18,
gender: '未知'
}
render(){
return (<ul>
<li>名字:{name}</li>
<li>性别:{gender}</li>
<li>年龄:{age}</li>
</ul>)
}
}
const p = {name: 'Danny', age: 18, gender: 'Male'}
// 将虚拟DOM渲染到页面
ReactDOM.render(<Person {...p}/>, document.getElementById('test1'))
ReactDOM.render(<Person name='Jenny'/>, document.getElementById('test2'))
</script>
<script type='text/babel'>
function Person (props) {
const {name, age, gender} = props;
return (
<ul>
<li>姓名:{name}</li>
<li>年龄:{age}</li>
<li>性别:{gender}</li>
</ul>
)
}
Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
gender: PropTypes.string
}
Person.defaultProps = {
name: '',
age: 18,
gender: '未知'
}
ReactDOM.render(<Person name='Danny' age={22} gender='Male'/>, document.getElementById('test1'));
ReactDOM.render(<Person name='Jenny' age={12} />, document.getElementById('test2'));
</script>
ref='input1'
<script>
class Demo extends React.Component{
showData = () => {
// ref的键值对key-value保存在 this.refs这个对象中
// key就是'input1', value就是input那个真实DOM
const {input1} = this.refs; // 解构赋值
alert(input1.value);
}
render(){
// ref 直接是一个字符串,不推荐使用,今后可能会被React弃用
return (
<div>
<input ref='input1' type='text' />
<button onClick={this.showData}>点我显示输入内容</button>
</div>
)
}
}
</script>
ref = {c => this.input1 = c}
<script>
class Demo extends React.Component{
showData = () => {
const {input1} = this; // 解构赋值
alert(input1.value)
}
render(){
// 回调函数 接受一个参数 c (currentNode)
// 这个 c 实际就是这个真实的 DOM 节点 input
// ref 的回调中的 this 是 Demo 的实例对象
return (
<div>
<input ref={c => this.input1 = c} type='text' />
<button onClick={this.showData}>点我显示输入内容</button>
</div>
)
}
}
</script>
saveInput = (currentNode) => {
this.input1 = currentNode
}
// 将input的ref那里写成一个Demo的绑定函数(例如:saveInput)
<input ref={this.saveInput} type='text' />
myRef = React.createRef(); ref={this.myRef}
<script>
class Demo extends React.Component{
// React.createRef 调用后可以返回一个容器,该容器可以存储被ref所标识的节点
// 该容器是专“人”专用的,只能存一个节点,
// 需要标记几个节点就要创建几个React.createRef()
myRef = React.createRef()
myRef2 = React.createRef(
showData = () => {
// console.log(this.myRef); // {current: input}
// 这里的 current 是固定的,不能更改
alert(this.myRef.current.value)
}
render(){
return (
<div>
<input ref={this.myRef} type='text' />
<input ref={this.myRef2} type='text' />
<button onClick={this.showData}>点我显示输入内容</button>
</div>
)
}
}
</script>
<div id='test'>div>
<script>
// 创建虚拟DOM
class Demo extends React.Component{
// 发生事件的元素就是我们要操作的元素,此时ref可以使省略
// 下边input失去焦点,显示的input的值,此时ref就可以省略
showData = (event) => {
alert(event.target.value)
}
render(){
return (
<input onBlur={this.showData} type='text' placeholder='失去焦点显示数据'/>
)
}
}
ReactDOM.render(<Demo/>, document.getElementById('test'))
script>
<div id='test'>div>
<script>
class Login extends React.Component{
// 点击提示输入的内容
handleSubmit = (event) => {
event.preventDefault(); // 阻止表单提交的默认行为
const {username, password} = this;
alert(`输入的用户名是${username.value},密码是${password.value}`);
}
render(){
return (
// 表单绑定提交事件(form有onSubmit事件)
<form onSubmit={this.handleSubmit}>
用户名:<input ref={c => this.username = c} type='text' name='username'/>
密码:<input ref={c => this.password = c} type='password' name='password'/>
<button>提交</button>
</form>
)
}
}
ReactDOM.render(<Login/>, document.getElementById('test'));
script>
<div id='test'>div>
<script>
class Login extends React.Component{
// 状态state初始化
state = {username:'', password: ''}
// 将username存入状态中
saveUsername = (event) => {
this.setState({username: event.target.value});
}
// 将password存入状态中
savePassword = (event) => {
this.setState({password: event.target.value});
}
handleSubmit = (event) => {
event.preventDefault(); // 阻止表单提交的默认行为
const {username, password} = this.state;
alert(`您输入的用户名是${username},密码是${password}`);
}
render(){
// 为 form 绑定 onSubmit 事件
// 为 input 绑定 onChange 事件,发生改变就将改变保存到state
return (
<form onSubmit={this.handleSubmit}>
用户名:<input onChange={this.saveUsername} type='text' name='username'/>
密码:<input onChange={this.savePassword} type='password' name='password'/>
<button>提交</button>
</form>
)
}
}
ReactDOM.render(<Login/>, document.getElementById('test'));
script>
new Promise((reolsve, reject)=>{})
setTimeOut(function(){}, 1000)
<div id='test'>div>
<script>
class Login extends React.Component{
// 状态初始
state = {username:'', password:''}
// 高阶函数、函数的柯里化
saveFormData = (dataType) => {
return (event) => {
// 从对象中读取变量的值必须加上[]
this.setState({[dataType]: event.target.value})
}
handleSubmit = (event) => {
event.preventDefault(); // 阻止默认行为
const {username, password} = this.state;
alert(`您输入的用户名是${username},密码是${password}`);
}
render(){
// 内联函数写了括号就是这个函数的返回值作为回调函数
// 必须要把一个函数交个 onChange 作为回调
return (
<form onSubmit={this.handleSubmit}>
<input onChange={this.saveFormData('username')} type='text' name='username'/>
<input onChange={this.saveFormData('password')} type='password' name='password'/>
<button>提交</button>
</form>
)
}
}
ReactDOM.render(<Login/>, document.getElementById('test'))
script>
<div id='test'>div>
<script>
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(){
// 此时 onChange 的回调函数是一个箭头函数
return (
<form onSubmit={this.handleSubmit}>
<input onChange={e => this.saveFormData('username', e)} type='text' name='username'/>
<input onChange={e => this.saveFormData('password', e)} type='password' name='password'/>
<button>提交</button>
</form>
)
}
}
ReactDOM.render(<Login/>, document.getElementById('test'))
script>
constructor
=> componentWillMount
=> render
=> componentDidMount
=> componentWillUnmount
setState()
=> shouldComponentUpdate
=> componentWillUpdate
=> render
=> componentDidUpdate
=> componentWillUnmount
forceUpdate()
=> componentWillUpdate
=> render
=> componentDidUpdate
=> componentWillUnmount
.componentWillReceiveProps
=> shouldComponentUpdate
=> componentWillUpdate
=> render
=> componentDidUpdate
<html lang="en">
<head>
<title>getSnapshotBeforeUpdate应用title>
<style>
.list{
width: 200px;
height: 150px;
background-color: skyblue;
overflow: auto
}
.news{ height: 30px; }
style>
head>
<body>
<div id='test'><div>
<script>
// 需求:div中不断接收到新的新闻,滚动条
// 但是点击某一条新闻时,固定页面在那个新闻处
class NewsList extends React.Component{
state = {newsArr: []}; // 初始化状态,一个新闻的数组
// 开启定时器,每隔1s添加一个新闻
componentDidMount(){
setInterval(()=>{
const {newsArr} = this.state;
const news = '新闻' + (newsArr.length + 1)
this.setState({newsArr: [news, ...newsArr]})
}, 1000)
}
// 每次更新前,需要确定当前的scrollHeight
getSnapshotBeforeUpdate(){
return this.myRef.scrollHeight;
}
// 在每次新加入新闻后,需要确定div的scrollTop的值使其位置不发生变化
componentDidUpdate(prevProps, prevState, height){
this.myRef.scrollTop += this.myRef.scrollHeight - height
}
render(){
return (
<div ref={c=>this.myRef=c} className='list'>
{
this.state.newsArr.map((value, index)=>{
return <li key={index} className='news'>{value}</li>
})
}
</div>
)
}
}
ReactDOM.render(</NewsList>, document.getElementById('test'))
script>
body>