react基础概述

1. React入门概述

1.1 介绍

用于构建用户界面的 JavaScript 库(只关注于视图)

1.2 React的特点

1.采用组件化模式、声明式编码,提高开发效率及组件复用率
2.在 React Native中可以使用React语法进行移动端开发
3.使用虚拟DOM+Diff算法,尽量减少与真实DOM的交互

1.3 原生JavaScript的缺点

1.原生JavaScript操作DOM繁琐,效率低(DOM-API操作UI)
2.使用JavaScript直接操作DOM,浏览器会进行大量的重绘重排
3.原生JavaScript没有组件化编码方案,代码复用率很低

2. 初识React

2.1 相关库介绍

  1. react.js:React核心库。
  2. react-dom.js:提供操作DOM的React扩展库。
  3. babel.min.js:解析JSX语法代码转为JS代码。

2.2 使用JSX创建虚拟DOM

const VDOM = 

Hello,React

2.3 渲染虚拟DOM(元素)

  1. 语法: ReactDOM.render(virtualDOM, containerDOM)
  2. 作用: 将虚拟DOM元素渲染到页面中的真实容器DOM中显示
  3. 参数说明:
    参数一: 纯js或jsx创建的虚拟dom对象
    参数二: 用来包含虚拟DOM元素的真实dom元素对象(一般是一个div)




  
  hello_react



  
  
  1. 页面显示


    WeChatd5f371b9183a734eea8628f8b6bb6de8.png

2.4 创建虚拟DOM的两种方式

  1. React.createElement
  1. JSX方式

3. JSX入门

3.1 概述

  1. 全称: JavaScript XML
  2. React定义的一种类似于XML的JS扩展语法: JS + XML本质是React.createElement(component, props, ...children)方法的语法糖
  3. 作用: 用来简化创建虚拟DOM
    a.写法:const ele =

    Hello JSX!


    b.注意1:它不是字符串, 也不是HTML/XML标签
    c.注意2:它最终产生的就是一个JS对象

3.2 基本语法规则

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

补充:区分js表达式与js语句

  1. 表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
    a //变量a
    a + b
    demo() // 函数调用表达式
    arr.map()
    a ? a : b

  2. 语句
    if(){ }
    for(){ }

4. 组件

4.1 函数式组件

// 1. 创建函数式组件
function MyComponent(){
  return 

我是用函数定义的组件

} // 2. 渲染组件到页面 ReactDOM.render(, document.getElementById('root'))

4.2 类式组件

//1.创建类式组件
class MyComponent extends React.Component {
  render(){
    return 

我是用类定义的组件(适用于【复杂组件】的定义)

} } //2.渲染组件到页面 ReactDOM.render(,document.getElementById('root'))

补充:关于ES6中类的注意事项

  1. 类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
  2. 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
  3. 类中所定义的方法,都放在了类的原型对象上,供实例去使用。

5. 组件核心属性: state 状态

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

class Weather extends React.Component{
  constructor(props) {
    super(props)
    // 初始化状态
    this.state = {
      isHot: true
    }
  }
  render() {
    // 读取状态
    const {isHot} = this.state
    return 

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

} } // 2. 渲染组件到页面 ReactDOM.render(, document.getElementById('root'))

5.2 setState

  1. 作用:对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件
  2. 注意点:
    a. 不要直接修改state
    b. state 的更新可能是异步的
    c. state 的更新会被合并
// 1.创建组件
class Weather extends React.Component {
  // 构造器调用几次? ———— 1次
  constructor(props){
    console.log('constructor');
    super(props)
    // 初始化状态
    this.state = {isHot:false,wind:'微风'}
    // 解决 changeWeather 中 this 指向问题
    this.changeWeather = this.changeWeather.bind(this)
  }

  // render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数
  render(){
    console.log('render');
    //读取状态
    const {isHot,wind} = this.state
    return 

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

} // changeWeather调用几次? ———— 点几次调几次 changeWeather(){ // changeWeather放在哪里? ———— Weather的原型对象上,供实例使用 // 由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用 // 类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined // 获取原来的isHot值 const isHot = this.state.isHot // 严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。 this.setState({isHot:!isHot}) // 严重注意:状态(state)不可直接更改,下面这行就是直接更改!!! // this.state.isHot = !isHot //这是错误的写法 } } //2.渲染组件到页面 ReactDOM.render(,document.getElementById('root'))

setState扩展

(1). setState(stateChange, [callback])------对象式的setState
    1.stateChange为状态改变对象(该对象可以体现出状态的更改)
    2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
                    
(2). setState(updater, [callback])------函数式的setState
    1.updater为返回stateChange对象的函数。
    2.updater可以接收到state和props。
    4.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
总结:
        1.对象式的setState是函数式的setState的简写方式(语法糖)
        2.使用原则:
    (1).如果新状态不依赖于原状态 ===> 使用对象方式
    (2).如果新状态依赖于原状态 ===> 使用函数方式
    (3).如果需要在setState()执行后获取最新的状态数据, 在第二个callback函数中读取
import React, { Component } from 'react'

export default class Demo extends Component {

    state = {count:0}

    add = ()=>{
        //对象式的setState
        /* //1.获取原来的count值
        const {count} = this.state
        //2.更新状态
        this.setState({count:count+1},()=>{
            console.log(this.state.count);
        })
        //console.log('12行的输出',this.state.count); //0 */

        //函数式的setState
        this.setState( state => ({count:state.count+1}))
    }

    render() {
        return (
            

当前求和为:{this.state.count}

) } }

注意:

组件自定义的方法中this为undefined,如何解决?
a. 强制绑定this: 通过函数对象的bind()
b. 箭头函数

补充:类中方法的this指向问题
类中定义的方法,在内部默认开启了局部的严格模式 开启严格模式,函数如果直接调用,this不会指向window,而是undefined

class Person {
  constructor(name,age){
    this.name = name
    this.age = age
  }
  study(){
    //study方法放在了哪里?——类的原型对象上,供实例使用
    //通过Person实例调用study时,study中的this就是Person实例
    console.log(this);
  }
}

const p1 = new Person('tom',18)
p1.study() //通过实例调用study方法  Person
const x = p1.study
x() // 直接调用 undefined

6. 组件核心属性: props

6.1 理解
每个组件对象都会有props(properties的简写)属性,组件标签的所有属性都保存在props中

6.2 作用

  1. 通过标签属性从组件外向组件内传递变化的数据
  2. 注意: 组件内部不可修改props数据,是只读的
// 创建组件 
class Person extends React.Component{
  render() {
    return (
      
  • 姓名:{this.props.name}
  • 性别:{this.props.sex}
  • 年龄:{this.props.age}
) } } // 渲染组件到页面上 ReactDOM.render(, document.getElementById('root'))

批量传递数据

const person = {name: 'yk', age: 18, sex: '男'}
ReactDOM.render(, document.getElementById('root'))

补充:展开运算符

let arr1 = [1, 3, 5, 7, 9]
let arr2 = [2, 4, 6, 8, 10]
// 1. 展开一个数组
console.log(...arr1); // 1 3 5 7 9

// 2. 连接数组
let arr3 = [...arr1, ...arr2] // [1,3,5,7,9,2,4,6,8,10]

// 3. 在函数中使用
function sum(...numbers) {
  return numbers.reduce((preValue, currentValue) => {
    return preValue + currentValue
  })
}
console.log(sum(1, 2, 3, 4)); // 10

// 4. 构造字面量对象时使用展开语法
let person = {
  name: 'tom',
  age: 18
}

// console.log(...person); // 报错,展开运算符不能展开对象
console.log({...person}) // {name: "tom", age: 18}

let person2 = { ...person } // 可以拷贝一个对象
person.name = 'jerry'
console.log(person2); // {name: "tom", age: 18}
console.log(person); // {name: "jerry", age: 18}

// 5. 合并对象
let person3 = {
  ...person,
  name: 'jack',
  address: "地球"
}
console.log(person3); // {name: "jack", age: 18, address: "地球"}

6.3 对props中的属性值进行类型限制,必要性限制和默认值设置




//对标签属性进行类型、必要性的限制
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
}

或者在组件内部通过static关键字进行简写

//对标签属性进行类型、必要性的限制
static propTypes = {
   name:PropTypes.string.isRequired, //限制name必传,且为字符串
   sex:PropTypes.string,//限制sex为字符串
   age:PropTypes.number,//限制age为数值
}

//指定默认标签属性值
static defaultProps = {
   sex:'男',//sex默认值为男
   age:18 //age默认值为18
}

6.4 函数组件使用props,并对props进行校验,设置默认值

//创建组件
function Person (props){
  const {name,age,sex} = props
    return (
      
  • 姓名:{name}
  • 性别:{sex}
  • 年龄:{age}
) } //对标签属性进行类型、必要性的限制 Person.propTypes = { name:PropTypes.string.isRequired, //限制name必传,且为字符串 sex:PropTypes.string, //限制sex为字符串 age:PropTypes.number, //限制age为数值 } // 指定默认标签属性值 Person.defaultProps = { sex:'男',// sex默认值为男 age:18 // age默认值为18 } //渲染组件到页面 ReactDOM.render(,document.getElementById('root'))

7. 组件核心属性: refs

7.1 理解
组件内的标签可以定义ref属性来标识自己
7.2 使用
7.2.1 字符串形式(过时的api,不推荐使用!)

//创建组件
class Demo extends React.Component{
  //展示左侧输入框的数据
  showData = () => {
    const {input1} = this.refs
    alert(input1.value)
  }
  //展示右侧输入框的数据
  showData2 = () => {
    const {input2} = this.refs
    alert(input2.value)
  }
  
  render(){
    return(
      
   
) } } //渲染组件到页面 ReactDOM.render(,document.getElementById('test'))

7.2.2 回调形式的ref

//创建组件
class Demo extends React.Component{
  //展示左侧输入框的数据
  showData = ()=>{
    const {input1} = this
    alert(input1.value)
  }
  //展示右侧输入框的数据
  showData2 = ()=>{
    const {input2} = this
    alert(input2.value)
  }
  
  render(){
    return(
      
this.input1 = c } type="text" placeholder="点击按钮提示数据"/>    this.input2 = c } type="text" placeholder="失去焦点提示数据"/> 
) } } //渲染组件到页面 ReactDOM.render(,document.getElementById('test'))

补充:关于回调 refs 的说明
如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。

7.2.3 使用createRef创建

//创建组件
class Demo extends React.Component{

  myRef = React.createRef()
  myRef2 = React.createRef()
  //展示左侧输入框的数据
  showData = ()=>{
    alert(this.myRef.current.value);
  }
  //展示右侧输入框的数据
  showData2 = ()=>{
    alert(this.myRef2.current.value);
  }
  
  render(){
    return(
      
     
) } } //渲染组件到页面 ReactDOM.render(,document.getElementById('root'))

补充:react中事件处理

  1. 通过onXxx属性指定事件处理函数(注意大小写)
    a.React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
    b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效
  2. 通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref

8. 表单处理

8.1 非受控组件
页面中所有输入类DOM的值,都是现用现取的

// 创建组件
class Login extends React.Component {
  handleSubmit = (event) => {
    event.preventDefault() // 阻止表单提交
    const {username, password} = this
    alert(`您输入的用户名是 ${username.value},您输入的密码是:${password.value}`)
  }
  render() {
    return (
      
用户名: this.username = c} type="text" name="username" /> 密码: this.password = c} type="password" name="password" />
) } } // 渲染组件 ReactDOM.render(, document.getElementById('test'))

8.2 受控组件
页面中输入类的DOM,随着输入的过程,将数据存储在状态state中,需要用的时候在从状态state中取(有点类似Vue中的双向数据绑定)

// 创建组件
class Login extends React.Component {
  // 初始化状态
  state = {
    username: '',
    password: ''
  }
  // 保存用户名到状态中
  saveUsername = (event) => {
    this.setState({username: event.target.value})
  }
  // 保存密码到状态中
  savePassword = (event) => {
    this.setState({password: event.target.value})
  }
  // 表单提交的回调
  handleSubmit = (event) => {
    event.preventDefault()
    const {username, password} = this.state
    alert(`您输入的用户名是 ${username},您输入的密码是:${password}`)
  }

  render() {
    return (
      
用户名: 密码:
) } } // 渲染组件 ReactDOM.render(, document.getElementById('test'))

补充:高阶函数与函数的柯里化

  1. 高阶函数
    如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数
    a. 若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
    b. 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
    常见的高阶函数有:Promise、setTimeout、arr.map()等等

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

function sum1(a, b, c){
  return a + b + c;
}
sum1(1, 2, 3)

// 柯里化后
function sum(a){
  return(b)=>{
    return (c)=>{
      return a+b+c
    }
  }
}
sum(1)(2)(3)

利用高阶函数和函数柯里化改下受控组件

//创建组件
class Login extends React.Component{
  //初始化状态
  state = {
    username:'', //用户名
    password:'' //密码
  }

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

  //表单提交的回调
  handleSubmit = (event)=>{
    event.preventDefault() //阻止表单提交
    const {username,password} = this.state
    alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
  }
  render(){
    return(
      
用户名: 密码:
) } } //渲染组件 ReactDOM.render(,document.getElementById('test'))

或者使用以下方式,也可以修改多个表单的值

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

render(){
  return(
    
用户名: this.saveFormData('username',event) } type="text" name="username"/> 密码: this.saveFormData('password',event) } type="password" name="password"/>
) }

9. 生命周期

9.1 理解

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

旧版本生命周期

react生命周期(旧).png

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

  1. constructor() —— 类组件中的构造函数
  2. componentWillMount() —— 组件将要挂载 【即将废弃】
  3. render() —— 挂载组件
  4. componentDidMount() —— 组件挂载完成 比较==常用==
    一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息

更新阶段

第1种情况:父组件重新render触发

  1. componentWillReceiveProps() —— 接收属性参数(非首次)【即将废弃】
  2. shouldComponentUpdate() —— 组件是否应该被更新(默认返回true)
  3. componentWillUpdate() —— 组件将要更新【即将废弃】
  4. render() =====> 必须使用的一个,组件更新
  5. componentDidUpdate() —— 组件更新完成

第2种情况:由组件内部this.setSate()

  1. shouldComponentUpdate() —— 组件是否应该被更新(默认返回true)
  2. componentWillUpdate() —— 组件将要更新【即将废弃】
  3. render() =====> 必须使用的一个,组件更新
  4. componentDidUpdate() —— 组件更新完成

第3种情况:强制更新 forceUpdate()

  1. componentWillUpdate() —— 组件将要更新【即将废弃】
  2. render() =====> 必须使用的一个,组件更新
  3. componentDidUpdate() —— 组件更新完成

卸载阶段

  1. componentWillUnmount() —— 组件即将卸载



    
    react生命周期(旧)


    

新版本生命周期

3_react生命周期(新).png

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

  1. constructor() —— 类组件中的构造函数
  2. static getDerivedStateFromProps(props, state) 从props得到一个派生的状态【新增】
  3. render() —— 挂载组件
  4. componentDidMount() —— 组件挂载完成 比较==常用==

更新阶段

由组件内部this.setSate()或父组件重新render触发或强制更新forceUpdate()

  1. static getDerivedStateFromProps(props, state) —— 从props得到一个派生的状态 【新增】
  2. shouldComponentUpdate() —— 组件是否应该被更新(默认返回true)
  3. render() —— 挂载组件
  4. getSnapshotBeforeUpdate() —— 在更新之前获取快照【新增】
  5. componentDidUpdate(prevProps, prevState, snapshotValue) —— 组件完成更新

卸载阶段

由ReactDOM.unmountComponentAtNode()触发

  1. componentWillUnmount() —— 组件即将卸载



    
    react生命周期(新)


    

即将废弃的钩子函数

  1. componentWillMount
  2. componentWillReceiveProps
  3. componentWillUpdate

你可能感兴趣的:(react基础概述)