第五课之初步认识react

react

课程回顾

开发环境和生产环境

开发环境和生产环境有很多通用的代码,可以将通用的代码提取出来,例如loaders,output等,所以可以形成三个文件,webpack.prod.config.js、webpack.dev.config.js、webpack.common.config.js,分离出来如何将他们分别合并,这时候就需要使用到webpack-merge,进行配置合并

课程内容

  • 1.课程回顾
  • 2.react介绍
  • 3.react安装
  • 4.生命周期、事件、数据流讲解
  • 5.父子组件传值

1.什么是React

React是Facebook推出的一个JavaScript库,它的口号就是“用来创建用户界面的JavaScript库”,所以它只是和用户界面打交道,可以把它看成MVC中的V(视图)层。

为什么使用react

  • 虚拟dom效率性能高,不需要操作dom(频繁操作dom很消耗性能)
  • 使用组件化开发方式,高复用、维护容易,逻辑清晰
  • 技术成熟,社区完善,插件齐全,适用于大型Web项目(生态系统健全),由Facebook专门的团队维护,技术支持可靠,是现在最主流的技术之一
  • 全局单向数据流,数据流向清晰,大型项目不在话下

组件化

  • 每一个ReactJS文件都是一个组件,含视图、逻辑操作、数据
  • 复用、封装性强

单向数据流

  • 子组件对于父组件传递过来的数据是【只读】的,不能直接更新父组件数据、这样组件更加简单易把握、只会在父组件修改数据,追查问题的时候可以跟快捷
  • 顶层组件某个props改变会递归遍历整棵组件树,重新渲染使用这个属性的插件

虚拟dom

  • React的设计中,开发者基本上无需操纵实际的DOM节点,每个React组件都是用Virtual DOM渲染的。
  • 用 JavaScript 对象结构表示 DOM 树的结构,然后用这个树构建一个真正的 DOM 树
  • 新的虚拟 DOM 与原来的虚拟 DOM进行比对时,它会进行同层比较,即相同的节点层进行比较,如果不同则直接将原始虚拟 DOM 中该节点层及以下的节点全部删除,重新生成新的虚拟 DOM 节点,而不会继续向下比对

jsx

  1. JSX 是 JavaScript 语言的一种语法扩展,长得像 HTML,但并不是 HTML。
  2. React.js 可以用 JSX 来描述你的组件长什么样的。
  3. JSX 在编译的时候会变成相应的 JavaScript 对象描述。
  4. react-dom 负责把这个用来描述 UI 信息的 JavaScript 对象变成 DOM 元素,并且渲染到页面上。

// 第一步:jsx代码

<button className="btn btn-blue">

<em>Confirm</em>

</button>

// 第二步解析成对象

{

type: 'button',

props: {

className: 'btn btn-blue',

children: {

type: 'em',

props: {

children: 'Confirm'

}

}

}

}

// 第三步:根据对象创建标签

// 参数1:元素名称

// 参数2:元素属性对象(null表示无)

// 参数3:当前元素的子元素string||createElement() 的返回值

React.createElement("button", {

className: "btn btn-blue"

},React.createElement("em", null, "Confirm"))

// 第四步:渲染

ReactDOM.render(dom, document.getElementById('app'))

key

  • 例如:在遍历数据时,推荐在组件中使用 key 属性

  • 循环需要加key:你在 JSX 模板中遍历 state 中某个数据时,为什么不加 key 值浏览器会报警告,这是因为你不再遍历的每条数据加上 key 值,更改 state 中那条数据的值,生成虚拟 DOM 后,React 就不知道原始遍历的数据和这次更新后遍历的数据一一对应的关系,就会再次重新渲染,而加上 key 值,它则能迅速比对出有差异的部分进行部分的更新。

  • 为什么不建议用 index 作为 key 值:因为当你插入、 删除中间的数据时,从改变的那个数据开始,后续每个数据的 index 值就会变,从而就导致了每个数据的 key 值相应变化了,这样依旧会引起大规模渲染,这就是其中的原因

  • 流程图

虚拟dom、jsx

  • 从 JSX 到页面到底经过了什么样的过程:

2.react安装

// 第一步:安装react react-dom,以及jsx编译

npm i -D react react-dom @babel/preset-react

// 第二步:.babelrc添加

"presets": [

"@babel/env",

"@babel/react"

],

// 第三步:添加react.js

// 1. 导入 react

import React, { Component } from 'react'

import ReactDOM from 'react-dom'

// 由于react实现机制,组件名称必须要大写

class MyComponet extends Component {

constructor(props) {

super(props);

this.state = {};

}

render() {

return (

// 此处注释的写法

<button className="btn btn-blue">

<em>Confirm</em>

</button>

)

}

}

// 第四步:渲染

// 参数1:虚拟dom对象 参数2:dom对象表示渲染到哪个元素内 参数3:回调函数

ReactDOM.render(<MyComponet />, document.getElementById('root'))

// 第五步:修改入口文件

entry: './src/js/react.js', //指定打包的入口文件

  • 组件名称命名用大驼峰
  • 如果在 JSX 中给元素添加类, 需要使用 className 代替 class
  • 在 JSX 中可以直接使用 JS代码,直接在 JSX 中通过 {} 中间写 JS代码即可

生命周期

  1. 挂载卸载过程

  • constructor():React数据的初始化,它接受两个参数:props和context,当想在函数内部使用这两个参数时,需使用super()传入这两个参数。
  • componentWillMount():它代表的过程是组件已经经历了constructor()初始化数据后,但是还未渲染DOM时
  • componentDidMount():组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染
  • componentWillUnmount ():在此处完成组件的卸载和数据的销毁。(移除计时器)

  1. 更新过程

  • componentWillReceiveProps (nextProps):接受一个参数nextProps,通过对比nextProps和this.props是否改变,判断要做的操作,只要父组件改变都会造成子组件componentWillReceiveProps接收,也会增加组件的重绘次数,浪费性能,写法不好容易死循环
  • shouldComponentUpdate(nextProps,nextState):主要用于性能优化(阻止组件渲染),因为react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断
  • componentWillUpdate (nextProps,nextState):shouldComponentUpdate返回true以后,组件进入重新渲染的流程,进入componentWillUpdate,这里同样可以拿到nextProps和nextState。
  • componentDidUpdate(prevProps,prevState):react只会在第一次初始化成功会进入componentDidmount,之后每次重新渲染后都会进入这个生命周期,这里可以拿到prevProps和prevState,即更新前的props和state
  • render():render函数会插入jsx生成的dom结构,react会生成一份虚拟dom树,在每一次组件更新时,在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的DOM节点,并重新渲染

  1. React新增的生命周期

  • getDerivedStateFromProps(nextProps, prevState):代替componentWillReceiveProps(),获取props并修改state
  • getSnapshotBeforeUpdate(prevProps, prevState):一般的用法就是获取更新前的DOM

  1. React17版本废弃的生命周期

  • componentWillMount
  • componentWillReceiveProps(可以用getDerivedStateFromProps和componentDidUpdate搭配使用替代)
  • componentWillUpdate

修改后的生命周期

// 之前

componentWillReceiveProps(nextProps) {

if (nextProps.isLogin !== this.props.isLogin) {

this.setState({

isLogin: nextProps.isLogin,

});

this.handleClose();

}

}

// 现在

static getDerivedStateFromProps(nextProps, prevState) {

if (nextProps.isLogin !== prevState.isLogin) {

return {

isLogin: nextProps.isLogin,

};

}

return null;

}

componentDidUpdate(prevProps, prevState) {

if (prevState.isLogin !== this.state.isLogin) {

this.handleClose();

}

}

使用方式

class MyComponet extends Component {

constructor(props) {

super(props);

this.state = {};

}

// 日常使用的生命周期

// 新增的生命周期

static getDerivedStateFromProps(nextProps, prevState) {

console.log('判断前后两个 props 是否相同------getDerivedStateFromProps')

return null;

}

componentDidUpdate() {

console.log('数据已经更新---------componentDidUpdate')

}

// 组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染

componentDidMount() {

console.log('组件加载后--------componentDidMount')

}

// 即将废弃

// componentWillReceiveProps(nextProps) {

// console.log('接收props--------componentWillReceiveProps')

// }

// 性能优化

// 你可以通过这个方法控制组件是否重新渲染。如果返回 false 组件就不会重新渲染。

shouldComponentUpdate(nextProps, nextState){

// 值没有发生过变化,不需要重新触发render

if(nextState.Number == this.state.Number){

return false

}

}

render() {

return (

// 此处注释的写法

<button className="btn btn-blue">

<em>Confirm</em>

</button>

)

}

}

render(不要在render里setState)

  1. 一个组件类必须要实现一个 render 方法。
  2. 这个 render 方法必须要返回一个 JSX 元素。
  3. 必须要用一个外层的 JSX 元素把所有内容包裹起来。

// 错误

render () {

return (

<div>第一个</div>

<div>第二个</div>

)

}

// 正确

render () {

return (

<div>

<div>第一个</div>

<div>第二个</div>

</div>

)

}

react事件

// 绑定事件,this指向会改变所以我们通过代码控制不改变this的指向

// 写法一:构造函数里绑定事件,效率高

constructor(props) {

super(props);

this.state = {};

this.showInfo = this.showInfo.bind(this);

}

showInfo() {

console.log(2312321)

}

<button className="btn btn-blue" onClick={this.showInfo}>

<em>Confirm</em>

</button>

// 写法二:使用bind this

showInfo() {

console.log(2312321)

}

<button className="btn btn-blue" onClick={this.showInfo.bind(this)}>

<em>Confirm</em>

</button>

// 写法三:方法使用箭头函数,this指向不会改变,简单易用,效率高,建议使用

showInfo=()=> {

console.log(2312321)

}

<button className="btn btn-blue" onClick={this.showInfo}>

<em>Confirm</em>

</button>

// 写法四:在标签上调用函数,每次渲染会调用,不建议使用

<button className="btn btn-blue" onClick={()=>this.showInfo()}>

<em>Confirm</em>

</button>

数据讲解

1.state:内部定义,它只是用来控制这个组件本身自己的状态,页面渲染通过setState进行完成。

2.setState:

  • 当我们调用这个函数的时候,React.js 会更新组件的状态 state ,并且重新调用 render 方法,然后再把 render 方法所渲染的最新的内容显示到页面上
  • React.js为了批次与效能并不会马上修改state。而是把这个对象放到一个更新队列里面,稍后才会从队列当中把新的状态提取出来合并到 state 当中,然后再触发组件更新。

// 想要即时获得改变后的state

// 方案一:使用回调函数

this.setState({ val: this.state.val + 1 }, () => {

console.log(this.state.val);

})

// 方案二:setTimeout是异步方法,react无法直到开发者异步方法中想要渲染的顺序,所以在异步方法中调用setTimeout,react会即时渲染,不会使用批量更新

setTimeout(_ => {

this.setState({

num: ++num

})

console.log(this.state.num);

}, 0)

3.props:外部传入,包括父子组件之间的通信,全局数据流的传递

  • 父子组件之间的通信,子组件无法直接修改父组件的props,通过this.props接收
  • 全局数据流的传递(dva)

父子组件传值

  • 父组件使用子组件并添加属性进行传值
  • 子组件接收属性或者方法
  • 实现父组件state的使用,以及子组件props的使用

案例

// 第一步:添加一个子组件ChildCom.js,组件首字母大写

import React, { Component } from 'react'

export default class ChildCom extends Component {

constructor(props) {

super(props);

this.state = {

};

}

clickBtn = () => {

// 使用父组件的方法

this.props.addChildNum();

}

render() {

// 使用父组件的值

const { childNum } = this.props;

return (

<div>

<div>子组件值:{childNum}</div>

<button onClick={this.clickBtn}>子组件点击调用父组件方法</button>

</div>

)

}

}

// 第二步父组件添加

// 1. 导入 react

import React, { Component } from 'react'

import ReactDOM from 'react-dom'

// 导入子组件

import ChildCom from './ChildCom';

// 2. 创建 虚拟DOM

// 参数1:元素名称 参数2:元素属性对象(null表示无) 参数3:当前元素的子元素string||createElement() 的返回值

class MyComponet extends Component {

constructor(props) {

super(props);

this.state = {

num: 1,

childNum: 1,

};

}

...

addNum = () => {

let { num } = this.state;

this.setState({

num: ++num

})

}

addChildNum = () => {

let { childNum } = this.state;

this.setState({

childNum: ++childNum

})

}

render() {

const { num, childNum } = this.state;

return (

<div>

<div>父组件值:{num}</div>

<button className="btn btn-blue" onClick={this.addNum}>

<em>Confirm</em>

</button>

<ChildCom childNum={childNum} addChildNum={this.addChildNum} />

</div>

)

}

}

ref

不借助插件的情况下,react中如何获取获取dom节点

方式一

使用原生js,需要在componentDidMount(组件渲染完成后获取)

// 原生js获取获取dom节点

document.getElementById('');

~

~

~

方式二

react提供的一个特殊的属性ref,可以绑定到render()输出的任何组件上,这个特殊的属性允许你引用 render() 返回的相应的dom节点

// 第一步:添加ref属性

<input value={num} ref="myIpt" readOnly />

// 第二步:获取节点

getDom = () => {

var dom = this.refs.myIpt;

dom.style.background='red';

dom.focus();

}

父组件如何获取子组件的数据(props,state,event)

// 第一步:父组件使用并子组件添加onRef={(child) => this.child = child}(可以取别的名字)

// 作用是设置当前父组件的this.child等于返回的对象

<ChildCom childNum={childNum} addChildNum={this.addChildNum} onRef={(child) => this.child = child} />

// 第二步:子组件ChildCom添加

componentDidMount() {

// 将child传递给this.props.onRef()方法

this.props.onRef && this.props.onRef(this);

}

// 第三步父组件使用

console.log(this.child); // ChildCom {props: {…}, context: {…}, refs: {…}, updater: {…}, clickBtn: ƒ, …}

  • 百分之99的情况在不使用ref的情况下,react都能进行完成,频繁的dom操作是比较消耗性能的,所以在任何不需要ref去操作页面数据的的情况下,都不要使用ref去操作dom

以上是 第五课之初步认识react 的全部内容, 来源链接: utcz.com/z/381456.html

回到顶部