React全家桶学习笔记 (一)
目录
一、jsx基本语法
(react核心库 react , react-dom(虚拟dom), babel(jsx转js))
一.定义虚拟dom时不要使用引号
二.标签使用表达式使用{}
(1)在{}中只能写表达式(注意:语句与表达式的区别)
-
(一)表达式:一个表达式会产生一个值
例: 1.a 2.a+b, 3.a(), 4.arr.map(), 5.function a(){}
-
(二)语句:
例: 1.if(){}else{} 2.for(){}, 3.switch(){case xx : xxx}
三.样式类名使用className(不使用class,避免与es6中类的命名冲突)
四.内联样式,使用style={{key:value}}
五.jsx中虚拟DOM只允许有一个根标签
六.标签必须闭合
七.标签首字母如果是小写字母开头则是普通HTML标签, 如果是大写字母开头则是一个组件
二、组件
在react中组件分为两种,函数组件 , 类组件。
函数组件的函数名就是组件名,类组件的类名就是组件名。
1.函数组件
function MyFun(){
return (
<span>我是函数组件</span>
)
}
2.类组件
在讲类组件之前需要先了解一些关于es6中类的基础知识
class Person{
constructor(a,b){//构造器
// 构造器中的this指的是实例对象(谁new()了就指向谁,构造器中的属性都是私有的)
this.a = a;
this.b = b;
}
// 1.方法是公有的 是放在这个类上的
// 2.通过实例调用方法时,方法中的this指向实例对象
eat(){
console.log('我想吃饭'+this.a);
// console.log(this);
}
run(){
console.log(this);
}
}
let p1 = new Person(1,2);
let p2 = new Person(3,4);
// console.log(p1); //Person { a: 1, b: 2 }
// console.log(p2); //Person { a: 3, b: 4 }
p1.eat(); //我想吃饭1
// p2.eat(); //我想吃饭3
// 类中的继承
class Car extends Person{
constructor(a,b,price){
super(a,b);//如果Car继承于Person 写状态机时必须使用super来接收父类中的属性 super必须放在最前面
this.price = price;
}
}
let c1 = new Car('宝马','五系');
let c2 = new Car('奥迪','rs6')
let c3 = new Car('奔驰','s级','200w')
// console.log(c1); //Car { a: '宝马', b: '五系' }
// console.log(c2); //Car { a: '奥迪', b: 'rs6' }
// console.log(c3); //Car { a: '奔驰', b: 's级', price: '200w' }
// c3.eat(); //我想吃饭奔驰 eat方法继承于父类中的方法(原型链 通过 _proto_ 连接)
// 在类中是可以直接写赋值语句的
class Food{
constructor(name){
this.name = name
}
age=18//实例对象上
static type=1 //直接将type写到实例上
}
let f1 = new Food('小明');
console.log(f1);//Food { age: 18, name: '小明' }
console.log(Food);//[class Food] { type: 1 }
(1).类组件必须继承
(2).且必须有render
(3).render必须有返回值 , 你想渲染什么就返回什么
class MyFunTwo extends React.Component{
render(){
return(
<span>我是类组件</span>
)
}
}
ReactDOM.render(<MyFunTwo/>,document.getElementById(''));
}
3.函数组件与类组件的区别
1.函数组件中没有this 类组件有
2.函数组件中没有生命周期 类组件有
3.函数组件中没有状态state(新增hooks) 类组件有
组件分为简单组件和复杂组件(有状态组件又称复杂组件),当状态机发生改变时,组件会重新渲染
三、组件的三大核心属性
在react中组件内会自带三大属性props:{} refs:{} state:null 这些值都在组件的实例上
1.state
state是组件中一个很重要的属性,可以包含多个key:value,是组件中的状态机 state必须是一个对象。
class MyFunTwo extends React.Component{
constructor(props){
super(props);
this.state = {
isMen:true
}
}
render(){
return (
<span>我{this.state.isMen?'是':'不是'}函数组件</span>
//这里的this与constructor中的this都指向react内new的实例对象(组件的实例对象)
)
}
状态的修改
注意:组件中的状态(state)不能直接修改,需要使用内置API --setState
setState是一个合并的操作 不是覆盖
class MyFunTwo extends React.Component{
constructor(props){
super(props);
this.state = {
isMen:true,
number:0
}
this.demo = this.demo.bind(this); //修改demo的this指向 否则将会是undefined
}
demo(){
console.log(this.state.isMen) //true
if(this.state.number == 0){
this.setState({
number:1,
})
}else{
this.setState({
number:0,
})
}
}
render(){
return (
<span onClick={this.demo}>我{this.state.isMen?'是':'不是'}函数组件{this.state.number}</span>
//这里的this与constructor中的this都指向react内new的实例对象,
//因为demo方法是挂载在实例下所以这里要使用this.demo才能访问到,
//注意:此处的demo不要加()否则render会直接执行此函数并return一个undefined
)
}
}
ReactDOM.render(<MyFunTwo/>,document.getElementById('test'));
//简写方式
class MyFunTwo extends React.Component{
state={
Number:1
}
demo=()=>{
this.setState({
Number:2
})
}
render(){
return (
<span onClick={this.demo}>{this.state.Number}</span>
)
}
}
ReactDOM.render(<MyFunTwo/>,document.getElementById('test'));
state总结:
1.在组件render中的this指向实例对象
2.组件中自定义的方法中this指向undefined 解决方案(通过bind改变this 、箭头函数)。
3.状态不能直接修改,需要使用setState({key:value})
2.props
主要用于接收组件外部传入的数据
1.类组件使用props
class MyFunTwo extends React.Component{
state={
Number:1,
name:''
}
demo=()=>{
const {name} = this.props; //接受传过来的props值
this.setState({
Number:2,
name,
})
}
render(){
return (
<span onClick={this.demo}>{this.state.Number}{this.state.name}</span>
)
}
}
ReactDOM.render(<MyFunTwo name='张三'/>,document.getElementById('test'));
//如果数据过多就会显得很繁琐,这时候可以使用展开运算符
//ReactDOM.render(<MyFunTwo {...data}>,document.getElementById('test'));
2.函数组件使用props
// 函数组件使用props
function Funcprops(props){
console.log(props);
return(
<>
<span>函数接收props{props.name}</span>
</>
)
}
ReactDOM.render(<Funcprops name='张三'/>,document.getElementById('test'));
如果需要对props的数据类型进行限制则需要使用propTypes(在16版本以后被弃用了,如果需要使用需要下载依赖)
注意:props是只读的
3.refs
refs的作用是拿到被标记的节点
1.字符串形式的refs(不推荐使用–效率不高)
class Demo extends React.Component{
click = ()=> {
const {input1} = this.props.refs;
console.log(input1.value);
}
render(){
return(
<>
<input ref = 'input1' type="text" placeholder="请输入姓名" />
<button onClick={this.click}>xx</button>
<input type="text" placeholder="请输入年纪"/>
</>
)
}
}
ReactDOM.render(<Demo name='张三'/>,document.getElementById('test'));
2.回调函数的refs
如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。
class Demo extends React.Component{
click = ()=> {
const {input1} = this;
console.log(input1.value);
}
render(){
return(
<>
<input ref = { currentNode => this.input1 = currentNode } type="text" placeholder="请输入姓名" />
<button onClick={this.click}>xx</button>
<input type="text" placeholder="请输入年纪"/>
</>
)
}
}
ReactDOM.render(<Demo name='张三'/>,document.getElementById('test'));
//内联函数问题展示
class Demo extends React.Component{
state={
isMen:true
}
click = ()=> {
const {input1} = this;
const { isMen } = this.state;
this.setState({
isMen:!isMen
});
}
render(){
const { isMen } = this.state;
return(
<>
<span>我{isMen?'是':'不是'}一个男孩子</span>
<input ref = { currentNode => {this.input1 = currentNode;console.log('@',currentNode) } } type="text" placeholder="请输入姓名" />
<button onClick={this.click}>xx</button>
</>
)
}
}
ReactDOM.render(<Demo name='张三'/>,document.getElementById('test'));
解决上述问题的写法-绑定函数的方式
class Demo extends React.Component{
state={
isMen:true
}
click = ()=> {
const {input1} = this;
const { isMen } = this.state;
this.setState({
isMen:!isMen
});
}
inputFunc = (currentNode)=>{
const {input1} = this;
this.input1 = currentNode;
console.log('@',currentNode)
}
render(){
const { isMen } = this.state;
return(
<>
<span>我{isMen?'是':'不是'}一个男孩子</span>
<input ref = {this.inputFunc} type="text" placeholder="请输入姓名" />
<button onClick={this.click}>xx</button>
</>
)
}
}
ReactDOM.render(<Demo name='张三'/>,document.getElementById('test'));
3.createRef(推荐使用)
React.createRef();调用后会返回一个容器,该容器可以存储ref所标识的节点。
注意:只能存储一个
class Demo extends React.Component{
demoRef = React.createRef();
inputFunc = ()=>{
console.log(this.demoRef.current.value);
}
render(){
return(
<>
<input ref = {this.demoRef} type="text" placeholder="请输入姓名" />
<button onClick={this.inputFunc}>xx</button>
</>
)
}
}
ReactDOM.render(<Demo name='张三'/>,document.getElementById('test'));
四、受控组件与非受控组件
1、受控组件
在 HTML 中,表单元素(如、 和 )通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新。我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。
通过事件去改变状态state的叫受控组件 类似于vue中的双向数据绑定
//受控组件
class Index extends React.Component {
state = {
username:''
}
submit = (e) => {
e.preventDefault();
console.log(this.state.username);
}
changeUser = (e)=>{
this.setState({
username:e.target.value,
})
}
render(){
return(
<>
<form onSubmit={this.submit}>
用户名:<input onChange={this.changeUser} type="text"/>
<button>登陆</button>
</form>
</>
)
}
}
2、非受控组件
要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你可以 使用 ref 来从 DOM 节点中获取表单数据。
现用现取就是非受控组件
class Index extends React.Component {
submit = (e) => {
e.preventDefault();
console.log(this.username.value);
}
render(){
return(
<>
<form onSubmit={this.submit}>
用户名:<input ref = {currentNode =>{ this.username = currentNode}} type="text"/>
<button>登陆</button>
</form>
</>
)
}
}
五、生命周期
旧版本的生命周期
-
1.组件初始化阶段
- (1)constructor() 构造器
- (2)componentWillMount() 组件将要挂载
- (3)render() 渲染
- (4)componentDidMount() 组件完成挂载 ->常用(一般用于初始化的事件 ,例如:数据请求,定时器,订阅消息)
-
2.组件更新阶段 / 父组件render
- (1)shouldComponentUpdate() 组件是否将要更新 返回true或者false
- (2)componentWillUpdate() 组件将要更新
- (3)render() 渲染
- (4)componentDidMount() 组件完成挂载
-
3.卸载组件(由ReactDOM.unmountComponentAtNode()触发)
- (1)componentWillUnmount() 组件将要卸载 ->常用(删除定时器,取消订阅)
forceUpdate()强制更新 (很少使用)
新版本生命周期
相比老版本的生命周期 ,新版本的生命周期中减少了componentWillMount,componentWillUpdate,componentWill ReceiveProps 新增了getDerivedStateFromProps,getSnapshot BeforeUpdate。
static getDerivedStateFromProps(){}如果state的值在任何时候都取决于props那么你可以使用这个钩子 必须返回state或者null 否则会报警告 ,但是可能会导致代码冗余–不推荐使用此钩子
getSnapshotBeforeUpdate(prevProps, prevState) 在最近一次渲染输出(提交到 DOM 节点)之前调用。应返回 snapshot 的值(或 null)。此用法并不常见,但它可能出现在 UI 处理中,如需要以特殊方式处理滚动位置的聊天线程等。
六、DOM的diff算法
react在渲染组件到页面时会先生成虚拟DOM再转成真实DOM渲染到页面,在更新时会先对比虚拟DOM(每一个虚拟DOM对应一个虚拟DOM),它会将新的虚拟DOM与旧的虚拟DOM进行一次对比,再将新的虚拟DOM转换为真实DOM,如果对比发现没有区别即真实DOM不会发生改变。
七、key的作用
1.虚拟DOM中key的作用:
当状态中的数据发生改变时,react会根据新数据生成新的虚拟DOM,随后react进行新虚拟DOM与旧虚拟DOM的diff比较。
1.虚拟DOM中key的作用
- 旧虚拟DOM中找到了与新虚拟DOM相同的key
- 若虚拟DOM中的内容没变,直接使用之前的真实DOM
- 若虚拟DOM中内容变了,则生成新的真实DOM,随后替换之前页面中的真实DOM
- 旧虚拟DOM中未找到与新虚拟DOM相同的key
- 根据数据创建新的真实DOM,随后渲染到页面
2.用index作为key可能引发的问题
- 1.若对数据进行:逆序添加,逆序删除等破坏顺序操作(会产生没有必要的真实DOM更新 ,界面效果没有问题,但是效率很低)
- 2.如果结构中还包含输入类的DOM input等(会产生错误DOM更新,界面有问题)
class Demo extends React.Component {
state = {
data:[
{id:1,name:'张三'},
{id:2,name:'王五'}
]
}
clickFunc = ()=>{
const {data} = this.state;
const newDate = {id:3,name:"李四"}
this.setState({
data:[newDate,...data]
})
}
render(){
return(
<>
{
this.state.data.map(i => {
return <li key={i.id}>我的名字叫{i.name}</li>
})
}
<button onClick={this.clickFunc}>点我</button>
</>
)
}
}
ReactDOM.render(<Demo />,document.getElementById('test'));
class Demo extends React.Component {
state = {
data:[
{id:1,name:'张三'},
{id:2,name:'王五'}
]
}
clickFunc = ()=>{
const {data} = this.state;
const newDate = {id:3,name:"李四"}
this.setState({
data:[newDate,...data]
})
}
render(){
return(
<>
{
this.state.data.map((item,index) => {
return <li key={index}>我的名字叫{item.name}<input type="text"/></li>
})
}
<button onClick={this.clickFunc}>点我</button>
</>
)
}
}
点击之后会导致输入内容与li内容显示不匹配问题
持续更新ing…