搭建开发环境
create-react-app
,下载好之后通过npm start
或者yarn start
(需要安装npm i yarn -g
)默认加载index.js
.- react技术栈安装
yarn add redux redux-actions redux-thunk react-redux redux-logger react-router-dom
JSX语法
创建一个ul列表,通过render函数添加到id为root里
jsx => babel(通过babel解析) => 浏览器
import React from 'react';
import { render } from 'react-dom';
const ulEle = (
<ul>
<li className="title">HTML</li>
<li style={{color:"red"}}>CSS</li>
<li onClick={()=>console.log(1)}>JS</li>
</ul>
);
render(ulEle,document.querySelector("#root"));
组件
特点:颗粒更细,复用性更高
继承核心组件React,通过ES6的class方式创建一个组件,可以定义状态,有生命周期函数
class Toos extends React.Component{
return(
<Fragment>
<input type="text"/>
<button onClick={this.add}>+1</button>
<button onClick={this.reduce}>-1</button>
<h1>you clicked {this.state.count} items !</h1>
</Fragment>
);
}
通过函数式创建一个组件,函数式组件称为功能性组件,无生命周期,在16.8版本之前没有办法在函数内部定义自身状态,也称为无状态组件。16.8之后可以通过HOOK来模拟自身状态
const Filter =()=>{
const href= "#";//让a标签的href静态的
return(
<div className="filter-container">
<span>All</span>
<a href={href}>completed</a>
<a href={href}>unfinished</a>
</div>
);
}
MVC模式
M: model数据。
V:view视图,每次调用render,只是更新数据改变的节点,用assign方法去合并,react内部有自己算法。
C:controllen控制器,操作数据的方法。
m => v => c =>m,这样的一个相互的过程,m发生改变自动通知v更新。
state & props
1.state:可变 ,组件内部状态
2.props:不可变, 组件对外的接口
单项数据流,至上而下的,这里的可变指的是操作层面,mvc的思想,state刷新props会自动更新。修改数据的时候不能直接在state数据进行修改,需要给一个全新的数据。因为前后做合并的时候比较的是地址是否发生更改,如果在原有的基础之上做修改,地址就没有发生变化,所以需要返回一个新的地址。
class User extends React.PureComponent {//PureComponent内部自己去判断数据是否发生更改
render() {
console.log("user render");
console.log(this.props.user);
return (
<ul>
<li>{this.props.user.name}</li>
<li>{this.props.user.age}</li>
</ul>
);
}
}
3.成员变量:内部的属性,必须通过实例来访问。
4.static:修饰的静态,直接通过类访问 ,组件默认值 static defaultProps={ }
5.React.createRef(),真实节点和虚拟节点之间的桥梁
textIput = React.createRef();
render(){
return(
<input type="text" ref={this.textIput} onClick={()=>{
//获取input里的值
cosole.log(this.textIput.current.value);
}} />
);
}
6.setState
setState() 将对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。这是用于更新用户界面以响应事件处理器和处理服务器数据的主要方式。
this.setState({
arr: this.state.arr.map(item => {//修改状态,返回一个的一个数组
if (id === item.id) {
// return Object.assign({},item,{completed:!item.completed})
return {...item, completed: !item.completed }//新写法
}
return item
})
});
//有时直接这样也可以,this.props.setCount(count);
7.key
react内部使用,稳定、唯一。
生命周期
class具有生命周期,函数式不具备生命周期
不能在render里使用setState,会陷入循环三个阶段
创建时(挂载时)=>更新时=>卸载时
1.constructor()
创建时在constructor函数会掉用一次,注意: constructor() 函数中不要调用 setState() 方法。
constructor(props) {
super(props);
// 不要在这里调用 this.setState()
this.state = { counter: 0 };//初始化state
}
2.componentDidMount()
更新完成后触发,组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。还是属于挂载时。
3.componentDidUpdate()
componentDidUpdate() 会在更新后会被立即调用。首次渲染不会执行此方法。
componentDidUpdate(prevProps,prevState, snapshot) {
// 典型用法(不要忘记比较 props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
注意:
- shouldComponentUpdate() 返回值为 false,则不会调用 componentDidUpdate()。
- 以在 componentDidUpdate() 中直接调用 setState(),但请注意它必须被包裹在一个条件语件里,正如上述的例子那样进行处理,否则会导致死循环。
4.shouldComponentUpdate()
shouldComponentUpdate(nextpros, nextState) {
//返回t,可以更新页面,返回f会阻止更新页面
if (nextpros.count > 10) {
return false;
}
return true;
}
注意:后续版本,React 可能会将 shouldComponentUpdate 视为提示而不是严格的指令,并且,当返回 false 时,仍可能导致组件重新渲染。
5.static getDerivedStateFromProps()
getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续 ,更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。
static getDerivedStateFromProps(props, state) {//静态
if (props.count > 5) {
return { color: "red" }
}
return state;
}
6.componentWillUnmount()
卸载时会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。
合成事件
1.事件绑定方式
<button onClick={() => { console.log(1) }}>点击显示</button>
2.在合成事件系统中,所有的事件都是绑定在 document 元素上,即,虽然我们在某个 react 元素上绑定了事件,但是,最后事件都委托给 document 统一触发。
3.合成事件,React 中,默认的事件传播方式为冒泡。原生的阻止事件方式,会连react的合成事件一起阻止了,但是react的合成事件阻止传播,原生的还是会被触发。
<div
style={style.box3}//默认还是冒泡型
onClick={e => {
// 异步分为宏任务 和微任务(优先级更高,先执行),时间函数就是宏任务,Promise是微任务
const { clientX, clientY } = e;
Promise.resolve().then(() => {
console.log(clientX, clientY);
});
}}></div>
4.react合成事件里只有一个共享e(事件对象),事件完成后会初始化e。先绑定上先触发。
表单组件
在 react 中,表单元素的表现形式和其他标准元素有所不同,它除了可以表现一些数据以外,还可以用来接收用户
的输入:
import React, { Component } from "react"
import ReactDOM from "react-dom"
class Forms extends Component {
state = {
name: "Sara"
};
render() {
const { name } = this.state;
return (
<div>
<ul>
<li>name: {name}</li>
</ul>
<hr />
<div>
<input type="text" />
<button>save</button>
</div>
</div>
);
}
}
ReactDOM.render(<Forms />, document.querySelector("#root"))
在该示例中, input 元素用于修改 user 的 name 属性,如果希望在页面刷新时,将 name 的值填充在 input 元素
中,我们可以使用 input 提供的 defaultValue ,为其设置默认值:
<input type="text" defaultValue={name} />
在 react 中,将组件分成了受控组件和非受控组件,所谓受控,就是指当 state 更新时,组件会被更
新。表单组件都是非受控组件,这也就意味着当 state 更新时,这些组件中的状态不会被更新。因此,如果希望将非受控组件变成受控组件的话,我们就要为其设置 value 的属性,搭配onChang使用。
<input type="text" value={name} />
事务
在用户的操作里阻止重复刷新
export default class extends React.Component {
state = {
count: 1
}
componentDidMount() {
document
.querySelector("#btn")
.addEventListener("click", () => {
this.setState({
count: this.state.count + 2
});
});
}
render() {
console.log("render");
return (
<div>
<h1>{this.state.count}</h1>
<button id="btn" onClick={() => {
// this.setState({
// count: this.state.count + 2
// });
// this.setState({
// count: this.state.count + 1
// })
// this.setState({
// count: this.state.count + 1
// });//通过assgin合并这三个操作
// Promise.resolve().then(() => {
// // 异步环境里就会三个依次去执行了
// this.setState({
// count: this.state.count + 1
// });
// this.setState({
// count: this.state.count + 1
// });
// this.setState({
// count: this.state.count + 1
// })
// });
}}>+</button>
</div>
);
}
}
同步时:执行函数之前React内部在之前之后做了一些处理,执行函数时给了一个类似全局的变量(开关),开关打开之后setState执行,把结果放在一个任务队列里,此时开关含没有关闭,继续执行,下一次setState又放在任务队列里,同理,执行最后一次完成后,开关关闭了,React把任务队列里的结果做了一次合并,得到最终的结果,才render。
异步时:把操作全部丢在一个异步任务池里,事务结束后,开关为关闭状态,执行一次任务,判断一下开关(发现开关是关闭状态,就会render),这样就会执行多次render
HOOK
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性,会在后面的博客里详细写一篇总结