React初识(四月技术分享)

259 阅读7分钟

一、React简介

React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。

二、React的特点和优势

(1) 虚拟DOM

我们以前操作dom的方式是通过document.getElementById()的方式,这样的过程实际上是先去读取html的dom结构,将结构转换成变量,再进行操作。而reactjs定义了一套变量形式的dom模型,一切操作和换算直接在变量中,这样减少了操作真实dom,极大地提高了性能。

(2) 组件化编码

react最核心的思想是将页面中任何一个区域或者元素都可以看做一个组件 component 那么什么是组件呢? 组件指的就是同时包含了html、css、js、image元素的聚合体 使用react开发的核心就是将页面拆分成若干个组件,并且react一个组件中同时耦合了css、js、image,这种模式极大地复用了代码, 简化项目编码, 提高运行效率

(3) Diffing算法

react采用diffing最小化重绘页面

(4) JSX 语法

在react中,我们使用render函数来构建组件的dom结构性能较高,因为省去了查找和编译模板的过程,但是在render中利用createElement创建结构的时候代码可读性较低,较为复杂,此时可以利用jsx语法来在render中创建dom,解决这个问题,但是前提是需要使用工具来编译jsx

三、编写第一个react应用程序

全局安装create-react-app

$ npm install -g create-react-app

创建一个项目

$ create-react-app my-react-project

运行

npm start

Snipaste_2021-04-28_23-02-49.png

四、React基础知识

1、虚拟Dom的两种创建方式

(1)、使用js创建虚拟DOM

//1.创建虚拟DOM
const VDOM = React.createElement('h1',{id:'title'},React.createElement('span',{},'Hello,React'))
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))

(2)、使用jsx创建虚拟DOM

//1.创建虚拟DOM
const VDOM = (  /* 此处一定不要写引号,因为不是字符串 */
	<h1 id="title">
		<span>Hello,React</span>
	</h1>
)
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))

2、组件的两种创建方式

(1)、函数式创建组件

function App () {
	return <h1>我是第一个React项目</h1>
}
ReactDOM.render(<App />,document.getElementById('test'))

/* 
注意:这里的App需大写,并且render的第一个参数是个<App />,而不是App。
执行了ReactDOM.render(<App/>.......之后,发生了什么?
    1.React解析组件标签,找到了App组件。
    2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。*/

(2)、类式创建组件

class MyComponent extends React.Component {
    render(){
            //render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
            //render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
            console.log('render中的this:',this);
            return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
    }
}
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/* 
执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
	1.React解析组件标签,找到了MyComponent组件。
	2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
	3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。*/

3、组件实例的三大属性

state(状态)

(1)、理解
state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合),我们可以通过更新组件的state来更新对应的页面显示(重新渲染组件)

(2)、定义state的两种方式

class Weather extends React.Component{
    //初始化状态
    state = {isHot:false}

    render(){
            const {isHot} = this.state
            return <h1>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
    }
}
class Weather extends React.Component{
    constructor(props){
            super(props)
            //初始化状态
            this.state = {isHot:false}
    }
    render(){
            const {isHot} = this.state
            return <h1>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
    }
}

(3)、setState
setState有两个参数,第一个参数可以是对象,也可以是方法return一个对象

this.setState({
  isLiked: !this.state.isLiked
})
this.setState((prevState, props) => {
  return {
    isLiked: !prevState.isLiked
  }
})

setState是异步的,所以想要获取到最新的state,没有办法获取,就有了第二个参数,这是一个可选的回调函数

this.setState((prevState, props) => {
  return {
    isLiked: !prevState.isLiked
  }
}, () => {
  console.log('回调里的',this.state.isLiked)
})
console.log('setState外部的',this.state.isLiked)

props(属性)

props是正常是外部传入的,组件内部也可以通过一些方式来初始化的设置,属性不能被组件自己更改,但是你可以通过父组件主动重新渲染的方式来传入新的 props

(1)、设置默认的props

class Title extends Component {
  static defaultProps = {
    name: 'React'
  }
  render () {
    return (
  		<h1>欢迎进入{this.props.name}的世界</h1>
  	)
  }
}

(2)、props.children

class Title extends Component {
  render () {
    return (
  		<h1>欢迎进入{this.props.children}的世界</h1>
  	)
  }
}

const Content = (props) => {
  return (
    <p>{props.children}</p>
  )
}

class App extends Component {
  render () {
    return (
  		<Fragment>
      	<Title>React</Title>
        <Content><i>React.js</i></Content>
      </Fragment>
  	)
  }
}

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

(3)、使用prop-types检查props
React其实是为了构建大型应用程序而生, 在一个大型应用中,根本不知道别人使用你写的组件的时候会传入什么样的参数,有可能会造成应用程序运行不了,但是不报错。为了解决这个问题,React提供了一种机制,让写组件的人可以给组件的props设定参数检查,需要安装和使用prop-types

$ npm i prop-types -S

ref

(1)、字符串形式的ref

<input ref="input1"/>
// 通过this.refs.input1获取

(2)、回调形式的ref
ref属性可以设置为一个回调函数,这也是官方强烈推荐的用法;这个函数执行的时机为: 组件被挂载后,回调函数被立即执行,回调函数的参数为该组件的具体实例。 组件被卸载或者原有的ref属性本身发生变化时,回调也会被立即执行,此时回调函数参数为null,以确保内存泄露

<input ref={(c)=>{this.input1 = c}}

(3)、使用React.createRef()
Refs是使用属性创建的,React.createRef()并通过ref属性附加到React元素。在构造组件时,通常将Refs分配给实例属性,以便可以在整个组件中引用它们。

myRef = React.createRef() 
<input ref={this.myRef}/>
// this.myRef.current  

4、react中的事件处理

绑定事件

采用on+事件名的方式来绑定一个事件,注意,这里和原生的事件是有区别的,原生的事件全是小写onclick, React里的事件是驼峰onClick,React的事件并不是原生事件,而是合成事件

事件handler的写法

直接在render里写行内的箭头函数(不推荐)

在组件内使用箭头函数定义一个方法(推荐)

直接在组件内定义一个非箭头函数的方法,然后在render里直接使用onClick={this.handleClick.bind(this)}(不推荐)

直接在组件内定义一个非箭头函数的方法,然后在constructor里bind(this)(推荐)

class Weather extends React.Component{
    constructor(props){
            super(props)
            //初始化状态
            this.state = {isHot:false}
            //解决handleClick中this指向问题
            this.handleClick = this.handleClick.bind(this)
    }
    render(){
            const {isHot} = this.state
            return <h1 onClick={this.handleClick}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
    }
    handleClick(){
            const isHot = this.state.isHot
            this.setState({isHot:!isHot})
    }
}
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))

(3)、事件的参数传递

class Login extends React.Component{
    //初始化状态
    state = {
            username:'', //用户名
    }

    //保存表单数据到状态中
    saveFormData = (dataType)=>{
        return (event)=>{
            this.setState({[dataType]:event.target.value})
        }
    }
    render(){
        return(
            <input onChange={this.saveFormData('username')} type="text" name="username"/>
        )
    }
}
//渲染组件
ReactDOM.render(<Login/>,document.getElementById('test'))

5、react中的组件传参

(1)、父子组件 props

(2)、兄弟组件 消息订阅/发布 集中式管理(redux)

(3)、祖孙组件(跨级组件)消息订阅/发布 集中式管理(redux) conText(生产者消费者模式)

例一、子向父传参

image.png

image.png

image.png

例二、pubsub

安装

npm i pubsub-js --save

引入

import PubSub from 'pubsub-js'

使用

 componentDidMount(){
    //订阅消息
    this.token = PubSub.subscribe('aaa',(msg,data)=>{
        this.setState(data)
    })
  }  
  componentWillUnmount(){
      //取消订阅
      PubSub.unsubscribe(this.token)
  }
//发布
PubSub.publish("aaa", { isLoading: false, users: res.data.items });

6、react的生命周期

生命周期流程图(旧)

image.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. 卸载组件: componentWillUnmount()

生命周期流程图(新)

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

1.constructor()  
2.getDerivedStateFromProps   
3.render()  
4.componentDidMount()   

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

1.getDerivedStateFromProps  
2.shouldComponentUpdate()  
3.render()  
4.getSnapshotBeforeUpdate  
5.componentDidUpdate()  

** 3. 卸载组件: componentWillUnmount()

重要的勾子

  1. render:初始化渲染或更新渲染调用
  2. componentDidMount:开启监听, 发送ajax请求
  3. componentWillUnmount:做一些收尾工作, 如: 清理定时器 2.6.6. 即将废弃的勾子
  4. componentWillMount
  5. componentWillReceiveProps
  6. componentWillUpdate 注意:现在使用会出现警告,需要加上UNSAFE_前缀使用,以后可能会被彻底废弃,不建议使用

完事!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!