React:基础入门

文章目录

  • React 基础
    • hello react
    • 虚拟DOM
      • 虚拟DOM与真实DOM
      • 创建虚拟DOM的两种方式
    • JSX语法
    • JSX小练习
    • 组件
      • 函数式组件
      • 类式组件
    • 组件实例的三大属性
      • state
      • props
        • 对标签属性进行类型、必要性的限制
        • 指定默认的标签属性
      • ref
        • 字符串形式的ref
        • 回调形式的ref
        • createRef创建ref容器
    • react中事件处理
    • 受控组件与非受控组件
    • 高阶函数与函数柯里化
    • 生命周期
      • 生命周期流程图(旧)
      • 生命周期流程图(新)
      • 小案例
    • 虚拟DOM中key的作用
      • react/vue中的key有什么作用?(key的内部原理是什么?)
      • 为什么遍历列表时,key最好不要用index?
      • 开发中如何选择key?:

React 基础

hello react


	
	

虚拟DOM

虚拟DOM与真实DOM

虚拟DOM:

  1. 本质是js中Object类型的对象(一般对象)
  2. 虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。
  3. 虚拟DOM最终会被React转化为真实DOM,呈现在页面上。

	
	

虚拟DOM节点属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D4Ct1ge7-1654314329582)(C:\Users\JZT\AppData\Roaming\Typora\typora-user-images\image-20220525210232894.png)]

部分真实DOM节点属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dZakPFSi-1654314329583)(C:\Users\JZT\AppData\Roaming\Typora\typora-user-images\image-20220525210757122.png)]

创建虚拟DOM的两种方式

js语法

可以看到用原生 js 创建虚拟 DOM 时,若是有多级嵌套结构,就需要嵌套调用下面的语法,显而易见这是非常麻烦的,而且没有相应的DOM结构。

React.createElement('span',{},'Hello,React')

	
	

jsx语法

使用此方法创建虚拟DOM可以保留DOM元素的结构,且更灵活,虽然在由虚拟DOM转为真实DOM的时候也会调用下面的方法,但是这个工作是由React帮我们完成的,留给我们的是更简单的工作。

React.createElement('span',{},'Hello,React')

	
	

JSX语法

既然提到了JSX语法,咱们就来聊聊它的语法规则吧。

jsx语法规则:

  1. 定义虚拟DOM时,不要写引号。
  2. 标签中混入JS表达式时要用{}。
  3. 样式的类名指定不要用class,要用className。
  4. 内联样式,要用style={{key:value}}的形式去写。
  5. 只有一个根标签
  6. 标签必须闭合
  7. 标签首字母
    • 若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。
    • 若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。

	
	

JSX小练习

一定注意区分:js语句(代码)与js表达式

  1. 表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方,下面这些都是表达式:

    • a
    • a+b
    • demo(1)
    • arr.map()
    • function test () {}
  2. 下面这些都是语句(代码):

    • if(){}
    • for(){}
    • switch(){case:xxxx}

	
	

组件

函数式组件


	
	

执行了ReactDOM.render(.......之后,发生了什么?

  1. React解析组件标签,找到了MyComponent组件。
  2. 发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。

类式组件

类的特性

  1. 类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。

  2. 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。

  3. 类中所定义的方法,都放在了类的原型对象上,供实例去使用。

  4. 构造器中的this是类的实例对象。

  5. 类中可以直接写赋值语句,如下代码的含义是:给Person的实例对象添加一个属性,名为name,值为jack。

    class Person {
        constructor(sex){
            this.sex = sex
        }
        name = 'jack'
    }
    

	
	

注意

render是放在MyComponent的原型对象上,供实例使用。
render中的this是MyComponent的实例对象 <=> MyComponent组件实例对象。

执行了ReactDOM.render(.......之后,发生了什么?

  1. React解析组件标签,找到了MyComponent组件。
  2. 发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法
  3. 将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。

组件实例的三大属性

state

state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合),组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)

严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!

this.state.isHot = !isHot //这是错误的写法

状态必须通过setState进行更新,且更新是一种合并,不是替换。

this.setState({isHot:!isHot})

还需要注意 changeWeather 函数放在 Weather 的原型对象上,供实例使用,但是由于changeWeather 是作为onClick的回调,如下:

今天天气很{isHot ? '炎热' : '凉爽'},{wind}

所以不是通过实例调用的,是直接调用。并且由于类中的方法默认开启了局部的严格模式,所以 changeWeather 中的 this 为 undefined,所以需要在构造函数中加上这一句解决changeWeather中this指向问题。

this.changeWeather = this.changeWeather.bind(this)

	
	

props

每个组件对象都会有props(properties的简写)属性,每个组件对象都会有props(properties的简写)属性。

通过标签属性从组件外向组件内传递变化的数据。

注意: 组件内部不要修改props数据

对标签属性进行类型、必要性的限制

方式1:(React v15.5 开始已弃用)

Person.propTypes = {
 name: React.PropTypes.string.isRequired,
 age: React.PropTypes.number
}

方式2:使用prop-types库进限制(需要引入prop-types库),然后在组建内添加如下代码

Person.propTypes = {
    name:PropTypes.string.isRequired, //限制name必传,且为字符串
    sex:PropTypes.string,//限制sex为字符串
    age:PropTypes.number,//限制age为数值
    speak:PropTypes.func,//限制speak为函数
}

指定默认的标签属性

Person.defaultProps = {
    sex:'男',//sex默认值为男
    age:18 //age默认值为18
}

	
	

函数式组件使用props


ref

组件内的标签可以定义ref属性来标识自己

字符串形式的ref


回调形式的ref

使用该方法会产生一个问题,当你点击切换天气按钮后,状态发生改变,由于 react 是状态驱动页面显示,所以当状态改变,视图改变,会重新调用 render 方法,那么此时就会遇到下面代码,因为 ref 是函数式,所以在初始化执行后就释放了,现在再次遇到因为不确定该节点是否发生改变,所以 react 会先执行一次该函数将 c(触发事件的节点) 置为 null,再调用一次该函数将新的 c 传进去,所以在更新是会调用两次该函数。

{this.input1 = c;console.log('@',c);}} type="text"/>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YlEh6bNg-1654314329584)(C:\Users\JZT\AppData\Roaming\Typora\typora-user-images\image-20220526202928163.png)]


createRef创建ref容器

React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的。


react中事件处理

react重写了一套事件处理。

  1. 通过onXxx属性指定事件处理函数(注意大小写)

    React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性

    React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效

  2. 通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref

受控组件与非受控组件

概念:表单内输入类型的的DOM使用的时候才去取输入的值就为非受控组件。

非受控组件

实现一个登录模拟事件。


	
	

受控组件

随着输入的变化就维护了最新的状态就是受控式组件。


高阶函数与函数柯里化

高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。

​ 1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。

​ 2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。

​ 常见的高阶函数有:Promise、setTimeout、arr.map()等等

函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。

 ```js
 function sum(a){
     return(b)=>{
         return (c)=>{
             return a+b+c
         }
     }
 }
 ```

受控组件中可以看到保存用户名和密码到状态中是两个函数的功能和写法都非常相似,那么我们能不能将这两个代码合并成一个呢?此时就可以进行一下转变,通过传入的 datatype 将用户名和密码分别保存进状态里。

//保存表单数据到状态中
saveFormData = (dataType)=>{
    return (event)=>{
        this.setState({[dataType]:event.target.value})
    }
}

注意:这里就用到了函数柯里化,这里 onChange 事件对应的就是this.saveFormData('username')的返回值,即

// 伪代码,只是为了更好的说明问题
(event)=>{
    this.setState({[dataType]:event.target.value})
}

当然这里也是用到了闭包,当 onChange 事件触发时就会执行该函数,并拿到 dataType 更新状态。


	
	

这里不用函数柯里化也能实现,只选做如下更改。在绑定事件处理函数时,将当前DOM元素对象也传给函数就可以处理当前元素的值得改变,并将其保存到状态里。

//保存表单数据到状态中
saveFormData = (dataType,event)=>{
    this.setState({[dataType]:event.target.value})
}
 this.saveFormData('username',event) } type="text" name="username"/>

生命周期

组件从创建到死亡它会经历一些特定的阶段。React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。

生命周期流程图(旧)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-faI1WbnY-1654314329584)(C:\Users\JZT\AppData\Roaming\Typora\typora-user-images\image-20220528203440463.png)]

生命周期的三个阶段(旧)

  1. 初始化阶段:由ReactDOM.render()触发—初次渲染

    1. constructor()

    2. componentWillMount()

    3. render()

    4. componentDidMount() =====> 常用

      一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息

  2. 更新阶段:由组件内部this.setSate()或父组件重新render触发

    1. shouldComponentUpdate()
    2. componentWillUpdate()
    3. render() =====> 必须使用的一个
    4. componentDidUpdate()
  3. 卸载组件:由ReactDOM.unmountComponentAtNode()触发

    componentWillUnmount() =====> 常用

    一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

注意:shouldComponentUpdate函数时控制组件更新的阀门,若该函数返回的是false,那么一般的组件更新是无法完成的,也就说还有特殊的更新咯,当然,forceUpdate函数是强制更新,即使 shouldComponentUpdate 函数返回false,组件也能正常更新。

//控制组件更新的“阀门”
shouldComponentUpdate(){
    console.log('Count---shouldComponentUpdate');
    return true
}

生命周期流程图(新)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UZHy4Tbk-1654314329585)(file:///C:\Users\JZT\AppData\Local\Temp\ksohtml15052\wps1.jpg)]

生命周期的三个阶段(新)

  1. 初始化阶段:由ReactDOM.render()触发—初次渲染

    1. constructor()

    2. getDerivedStateFromProps

    3. render()

    4. componentDidMount() =====> 常用

      一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息

  2. 更新阶段:由组件内部this.setSate()或父组件重新render触发

    1. getDerivedStateFromProps
    2. shouldComponentUpdate()
    3. render()
    4. getSnapshotBeforeUpdate
    5. componentDidUpdate()
  3. 卸载组件:由ReactDOM.unmountComponentAtNode()触发

    componentWillUnmount() =====> 常用

    一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

小案例

效果展示:一个区域,每个一秒钟就在前方插入一个 li ,但是只显示当前显示的几条 li 。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DWfT0zPr-1654314329586)(C:\Users\JZT\Desktop\test.gif)]


	
	

虚拟DOM中key的作用

react/vue中的key有什么作用?(key的内部原理是什么?)

虚拟DOM中key的作用:

  • 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。
  • 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】, 随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
    • 旧虚拟DOM中找到了与新虚拟DOM相同的key:
    • 若虚拟DOM中内容没变, 直接使用之前的真实DOM

为什么遍历列表时,key最好不要用index?

用index作为key可能会引发的问题:

  • 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
  • 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
  • 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

开发中如何选择key?:

  • 最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
  • 如果确定只是简单的展示数据,用index也是可以的。

你可能感兴趣的:(算法,react.js,javascript,前端)