React总结

135 阅读10分钟

react是一个将数据渲染成HTML的JavaScript框架。

JSX

  • jsx是react定义的一种语法,用来简化创建虚拟DOM的语法,是React.createElement()方法的语法糖
  • 语法:
    • 可以像先HTML元素的写法直接书写结构,但不要用引号包裹,因为JSX不是字符串
    • 书写多层结构时,可以使用‘()’包裹JSX语句,这样可以识别到是一个整体
    • 在JSX中引用 JS 表达式,则使用‘{}’包裹,但 jsx 有很多 js 语句不支持,例如 if语句 for循环
    • 在JSX的元素结构中想要添加 class 属性,要使用 className 来添加,在编译到真实DOM时会变成 class 属性
    • 在JSX中元素结构中想要添加内联样式 style,只能通过 js 方式传入一个对象,对象的属性名使用小驼峰命名规则,属性值是字符串类型。
    • 只能存在一个根标签:可以使用 <></> 这种空标签做根标签
    • 标签需要闭合
    • 在jsx中定义的小写开头的标签名,会将标签转换为HTML中同名的元素,如果HTML中不存在该标签对应的元素,则会报错
    • 在jsx中定义的大写开头的标签名,react就会渲染对应的组件,如果组件未定义就报错
const myHi = 'Hello React'
const id = 'test'

const VDOM = (
    <div className="title" > // 添加calss属性,要使用className添加
        <span>{myHi}</span> // js 语句使用{}包裹
        <span id={id} style={{color: 'f6c', fontSize: '25px'}}></span> // 内联样式 style,只能通过 js 方式传入一个对象
    </div>
)

JSX 防止注入攻击

你可以安全地在 JSX 当中插入用户输入内容:

const title = response.potentiallyMaliciousInput;
// 直接使用是安全的:
const element = <h1>{title}</h1>;

React DOM 在渲染所有输入内容之前,默认会进行转义它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本)攻击。

函数式组件

  1. 定义一个函数,函数返回值为 JSX 语法的结果,且注意 函数名一定要以大驼峰命名
  2. 函数式组件中的this值为undefined
// 函数名也就是组件名
function MyCom() {
    return 
}

类式组件

  1. 创建一个类式组件一定要继承react定义的父类
  2. render 函数一定要属性,返回值为 JXS 语法
  3. 类式组件不需要自己去创建实例对象,在react使用时,react会帮我们 new 一个实例对象,再去调用 render方法,将虚拟DOM渲染到页面中的真实DOM
  4. 类式组件中的this值为组件实例对象
  5. 在JSX中绑定事件,事件名还是原生js的事件名,但要修改成小驼峰的命名规则
  6. 在JSX中绑定事件后,还需要绑定处理函数,绑定处理函数后不可以直接跟(),因为这样函数会被直接调用,而不是在事件触发时调用
  7. 而事件中调用处理函数时,处理函数的this指向并不是组件实例对象自己,这时候就需要使用箭头函数的方式将 this 的指向,指向组件实例对象自己
  8. 或者在 state 中定义一个变量储存自定义的函数,该函数通过 .bind(this) 传入组件实例对象
// 创建一个类式组件
calss MyComp extends React.Component {
    // 构造函数 prpos 父组件传递的数据
    // 构造函数一般不使用
    constructor(prpos) { // 只会在初始化调用一次
        super(prpos) // 继承
        this.state = {} // 定义初始化状态,数据的储存和管理
    }
    
    // 想要创建 state 管理数据的状态,可以直接创建
    state = {
        myHi: 'hello react'
    }

    // 在类中可以直接自定义处理函数
    // 这里用函数表达式的方式定义的函数,函数直接绑定在了组件的实例对象上
    // 而使用箭头行数的好处是它没有自己的this,this是指向外部调用的对象,也就是组件实例对象自己
    handlerClick = () => {
        console.log('被点击了')
    }
    
    // render 返回虚拟DOM
    // 触发 render 时机,初始化调用一次,每次更新调用一次
    render() {
        return (
            <div className="title" >
                <span onClick={this.handlerClick}>{this.state.myHi}</span>
            </div>
        )
    }
}

组件的组成属性

state

  • 值为对象
  • 定义初始化状态,数据的储存和管理
  • state 的属性的状态不可以直接修改。
  • 修改 state 的属性的状态,要通过组件实例对象的方法:this.setState({state属性: 新值})
  • 调用 setState 方法更新,是合并更新,就是state中的其他属性不变,只改变了修改的属性。

props

  • 值是一个对象,props对象中的属性为使用组件时在组件上绑定的属性,属性值为绑定的属性传递的值
  • 在 props 中还有一个特殊的属性为:children,可以获取到组件体(就是开始标签和结束标签直接)中的内容
  • 所以组件体的内容也可以通过在使用组件时,在标签上通过 children 属性传递到组件内部
  • 在组件中 render 函数中可以访问到 this.props
  • 在组件内 props 的属性是只读的,不可修改
  • propTypes:对传入的值进行类型限制,需要单独的插件 PropTypes
    • 语法:propTypes = {属性: PropTypes.数据类型.isRequired}
    • 数据类型:是js中数据的类型,但要写小写形式:string number 等,而限制为函数则是:func
    • isRequired 是可选的,如果使用则该属性为必传
  • defaultProps:传入一个对象,用于定义组件中使用的数据的默认值,如果没有传入值,则使用默认值,属性名为组件使用的变量
    • 语法:defaultProps = {属性: '默认值'}
    calss MyComp extends React.Component {
        render() {
            const {name, 属性2, ...} = this.props
            return
        }
        
        // 对 props 的属性进行限制和默认值,通过 static 添加静态属性
        static propTypes = {
            name:  PropTypes.string.isRequired
        }
        static defaultProps = {
            name: 'jason'
        }
    }
    
    <MyComp>组件体内容</MyComp>
    
    // 第一种传递方式
    ReactDOM.render(<MyComp name='张三' age={20} />, document.getElementById(''))
    
    // 第二种传递方式:在react中有自己独特的解构对象的方式去传递,其他框架没有
    const obj = {name: 'jason', age: 18}
    ReactDOM.render(<MyComp {...obj} />, document.getElementById(''))

类组件中this的问题

你必须谨慎对待 JSX 回调函数中的 this,在 JavaScript 中,class 的方法默认不会绑定 this。如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined

这并不是 React 特有的行为;这其实与 JavaScript 函数工作原理有关。通常情况下,如果你没有在方法后面添加 (),例如 onClick={this.handleClick},你应该为这个方法绑定 this

如果觉得使用 bind 很麻烦,这里有两种方式可以解决。如果你正在使用实验性的 public class fields 语法,你可以使用 class fields 正确的绑定回调函数:(通常用以下这种方法

class LoggingButton extends React.Component {
  // 此语法确保 `handleClick` 内的 `this` 已被绑定。  // 注意: 这是 *实验性* 语法。
  handleClick = () => {    console.log('this is:', this);  }
  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}

Create React App 默认启用此语法。

如果你没有使用 class fields 语法,你可以在回调中使用箭头函数。(这种方式不推荐

class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // 此语法确保 `handleClick` 内的 `this` 已被绑定。    
    return (
        <button onClick={() => this.handleClick()}>
            Click me
        </button>
    );
  }
}

此语法问题在于每次渲染 LoggingButton 时都会创建不同的回调函数。在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题。

refs

字符串形式的ref

  • 获取多个绑定 ref 属性的 真实DOM 元素对象
  • 可以通过组件实例对象的 this.refs.'绑定ref时的赋值' 访问到每一个绑定 ref 的元素

回调函数形式的ref

  • 语法:ref={currentNode => this.自定义属性 = currentNode }
  • 然后在组件中就可以直接访问属性,获取到真实DOM

createRef() 创建

  • 语法:定义一个容器 myref = react.createRef(),在节点中给ref绑定容器
  • 访问绑定的ref真实DOM:this.自定义容器.current
calss MyComp extends React.Component {

    myref = react.createRef() // 每一个容器只能绑定一个节点
    myref2 = react.createRef()
    
    myClick = () => {
        this.myref.current
    }
    

    render() {
        return (
            // 字符串形式
            <div ref='dom1'></div>
            
            // 回调函数形式
            <div ref={currentNode => this.div1 = currentNode}></div>
            
            // createRef() 创建
            <div ref={this.myref} onClick={this.myClick}></div>
            
        )
    }
}

setState 修改 state 状态

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

获取 DOm 的方式

  • 可以通过事件绑定的函数 event.target 访问 真实DOM元素
  • 通过元素绑定 ref 属性,获取真实DOM

JSX中事件绑定函数的注意事项

  1. 第一种绑定方式:函数绑定不需要传递参数时,可以直接绑定,但函数后面不能跟随()
    • onClick={this.myFun} // 这种形式会在事件触发时调用函数,函数后面如果加了(),则组件初始化时,函数就调用了
    • 在上述自定义的函数中,可以通过设置形参,来获取当前绑定事件的 DOM 元素:myFun=(e)=>{e.target},e.target 就是当前绑定事件的DOM元素
  2. 第二种绑定方式(函数柯里化):函数绑定需要传递参数时,需要在函数中再返回一个函数,这样每次触发事件就会执行返回的函数
    • 语法:
  3. 第三种方式:绑定一个箭头函数,函数体里面再调用自定义函数
calss MyComp extends React.Component {
    
    // 第二种方式:函数中返回函数
    myClick = (str) => { // str的值有调用函数传递的实参决定
        return (event) => {
            console.log(event.target) // 这里就可以获取到 DOM 元素
        }
    }
    
    // 第三种方式绑定
    myClick2 = (str, event) => {
        
    }
    
    render() {
        return (
            <div onClick={this.myClick('我被点击了')}></div>
            
            // 第三种方式
            <div onClick={event => this.myClick2('我被点击了', event)}></div>
        )
    }
}

calss 组件中常用生命周期

  • componentDidMount() 挂载完成执行
    • componentDidMount() 方法会在组件已经被渲染到 DOM 中后运行,所以,最好在这里设置计时器
  • render() 初始化执行,数据更新时执行
  • componentWillUnmount() 将要卸载前执行
  • 调用 this.setState() 和父组件的render 会触发组件更新阶段

兄弟组件传值-消息订阅与发布

  • 需要安装 'pubsub-js' 包
  • 在要使用的文件中引入 import Pubsub from 'pubsub-js'
  • 通过在组件中发布消息:Pubsub.publish('自定义名称', data)
  • 在组件中订阅消息:const sub = Pubsub.subscribe('发布消息的自定义名称', (msg, data) => {})// data 为发布消息时传递过来的数据。
  • 而在订阅的组件中,在卸载前的生命周期中去清除订阅。减少内存使用

render props

  • 如何向组件内部动态传入带内容的结构(标签)
// 在 A 组件传入一个 C 组件,使用 render 属性
<A render={(data) => <C data={data}></C>}></A>

// 在A组中就可以通过 {this.props.render(内部state数据)} 渲染出 C 组件
// A组件: {this.props.render(内部state数据)}
// C组件: 读取A组件传入的数据显示 {this.props.data}