加载中...
  • React的使用 loading

    React 的三大特性:

    • 数据驱动 -> 单向数据流
    • 函数式编程 = 组件化 + JSX
    • 虚拟 DOM -> 跨平台

    setState是同步还是异步?

    异步npm

    可能会执行多次setState

    无法规定、限制用户如何使用setState

    没必要每次setState都要重新渲染,考虑性能

    即便是每次重新渲染,用户也看不到中间的效果(js单线程)异步

    setState的过程

    每个组件都有一个renderComponent方法

    执行renderComponent会重新执行实例的render

    patch(oldVnode, newVnode)

    React的基本使用

    事件

    bind this

    this默认是undefined 需要改变this指向

    1
    this.handler = this.handler.bind(this)

    也可以使用箭头函数

    关于event参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    clickHandler2 = (e) => {
    console.log(e);
    e.nativeEvent(); // 原生event 所有的事件挂载到document
    e.preventDefault();
    e.stopPropagation();
    console.log(e.target); // 指向当前元素, 即当前元素触发
    console.log(e.currentTarget); // 指向当前元素,假象! null
    // 注意,event其实是React封装的。可以看__proto__.constructor 是 SyntheticBaseEvent 组合事件
    // 原生event的__proto__.constructor是MouseEvent
    }

    表单

    受控组件

    input textarea select用value

    checkbox radio 用checked

    1
    2
    3
    4
    5
    <div>
    <label htmlFor="inputName">Name</label>
    <input id="inputName" value={this.state.name} onChange={this.changeHandle}></input>
    <p>{this.state.name}</p>
    </div>

    非受控组件

    ref

    defaultValue defaultChecked

    手动操作DOM元素

    使用场景

    必须手动操作DOM元素,setState实现不了

    文件上传

    某些富文本编辑器,需要传入DOM元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    import React, { Component } from "react";

    export class UncontrolledDemo extends Component {
    constructor(props) {
    super(props)
    this.state = {
    name: 'xx'
    }
    this.nameInputRef = React.createRef()
    }
    render() {
    return (
    <div>
    {/* 没有onChange事件
    state 并不会随着变化 */}
    <input defaultValue={this.state.name} ref={this.nameInputRef}></input>
    <p>{this.state.name}</p>
    <button onClick={this.alertName}>click me</button>
    </div>
    )
    }
    alertName = () => {
    console.log(this.nameInputRef);
    const e = this.nameInputRef.current // 通过ref获取DOM节点
    alert(e.value)
    }
    }

    父子组件通讯

    props传递数据

    props传递函数

    setState

    不可变值(函数式编程,纯函数)

    可能是异步更新

    可能会被合并

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    import { Component } from "react";

    export class StateDemo extends Component {
    constructor(props) {
    super(props)
    // state 要定义在构造函数中
    this.state = {
    count: 0
    }
    }
    clickHandle = () => {
    // 不要直接修改state,使用不可变数据
    this.setState({
    count: this.state.count + 1
    }, () => {
    console.log(this.state.count); // 回调函数
    })
    // setTimeout 中setState是同步的
    // setTimeout(() => {
    // this.setState({
    // count: this.state.count + 1
    // })
    // console.log(this.state.count);
    // })
    // 自定义DOM事件,setState同步

    console.log(this.state.count); // 异步,拿不到最新值
    }
    // 操作数组时、对象的常用形式
    // this.setState({
    // list1: [...this.state.list, 100], // 追加
    // list2: this.state.list.slice(0, 3), // 截取
    // list3: this.state.list.filter(item => item > 10),
    // })
    // this.setState({
    // obj1: Object.assign({}, this.state.obj, {a: 100}),
    // obj2: {...this.state.obj, a: 100}
    // })
    render() {
    return (
    <div>
    <p>{this.state.count}</p>
    <button onClick={this.clickHandle}>click me</button>
    </div>
    )
    }
    }

    组件生命周期

    装载阶段

    constructor componentDidMount

    更新阶段

    render shouldComponentUpdate componentDidUpdate

    卸载阶段

    componentWillUnmount

    函数组件

    1
    2
    3
    4
    5
    6
    7
    function List(props) {
    const { list } = this.props
    return ()
    }
    纯函数,输入props,输出JSX
    没有实例,没有生命周期,没有state
    不能扩展其他方法

    Portals

    组件默认会按照既定层次嵌套渲染

    让组件渲染到父组件以外

    1
    2
    3
    4
    5
    6
    render() {
    return ReactDOM.createPortal(
    <div className="modal">{this.props.children}</div>,
    document.body
    )
    } // 使用Portals渲染到body上,fixed元素要放在body上,有更好的浏览器兼容性
    使用场景

    父组件设置了overflow:hidden

    父组件z-index值太小

    fixed元素要放在body第一层级

    context

    公共信息传递给每个组件 非父子组件通讯

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    import React, { Component } from "react";

    const ThemeContext = React.createContext('light')

    export class ContextDemo extends Component {
    constructor(props) {
    super(props)
    this.state = {
    theme: 'light'
    }
    }
    render() {
    return (
    <ThemeContext.Provider value={this.state.theme}>
    <Toolbar />
    <div>{this.state.theme}</div>
    <button onClick={this.changeTheme}>click me</button>
    </ThemeContext.Provider>
    )
    }
    changeTheme = () => {
    this.setState({
    theme: this.state.theme === 'light' ? 'dark' : 'light'
    })
    }
    }

    class Toolbar extends Component {
    static contextType = ThemeContext // es6 写法
    render() {
    const theme = this.context // React会向上找最近的theme provider
    return (
    <div>
    <div>123{theme}</div>
    <ThemeLink />
    </div>
    )
    }
    }
    // Toolbar.contextType = ThemeContext // 指定conteType

    function ThemeLink (props) {
    return (
    <ThemeContext.Consumer>
    {value => value}
    </ThemeContext.Consumer>
    )
    } // 函数组件写法

    异步组件

    import()

    React.lazy

    React.Suspense

    1
    2
    3
    4
    5
    const ContextDemo  = React.lazy(() => import('./components/todo/Demo/ContextDemo'))

    <React.Suspense fallback={<div>loading</div>}>
    <ContextDemo />
    </React.Suspense>

    性能优化

    shouldComponentUpdate(SCU)

    PureComponent 和 React.memo

    不可变值 immutable.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    shouldComponentUpdate(nextProps, nextState) {
    if (nextState.count !== this.state.count) {
    return true // 可以渲染
    }
    return false // 不可渲染
    }
    // 默认父组件更新,子组件无条件更新
    // SCU一定要每次都用吗? 需要的时候才优化
    // SCU一定要配合不可变值
    1
    2
    PureComponent ,SCU中实现了浅比较
    memo函数组件中的PureComponent
    1
    2
    class Pure extends React.PureComponent {}
    export React.memo(MyComponent, ateEqual)
    1
    2
    3
    immutable.js
    彻底拥抱不可变值
    基于共享数据(不是深拷贝),速度快

    高阶组件

    关于组件公共逻辑的抽离

    高阶组件HOC
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 高阶组件并不是一种功能,而是一种模式
    const HOCFactory = (Component) => {
    const HOC extends React.Component {
    //在此处定义多个组件的公共逻辑
    render() {
    return <Component {...this.props} /> // 返回拼装的结果
    }
    return HOC
    }
    }
    const EnhancedComponent1 = HOCFactory(WrappedComponent1)
    const EnhancedComponent2 = HOCFactory(WrappedComponent2)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    const withMouse = (Component) => {
    class withMouseComponent extends React.Component {
    constructor(props) {
    super(props)
    this.state = {
    x: 0,
    y: 0
    }
    }
    handleMouseMove = (e) => {
    console.log(e);
    this.setState({
    x: e.clientX,
    y: e.clientY
    })
    }
    render() {
    return ( <div style = {{height: '500px'}} onMouseMove={this.handleMouseMove} >
    {/* 传递所有props 增加mouse属性 */ }
    <Component {...this.props} mouse={this.state} />
    </div>
    )
    }
    }
    return withMouseComponent
    }
    const App = (props) => {
    const {x, y} = props.mouse
    return (
    <div style={{height: '500px'}}>
    <h1>{x}, {y}</h1>
    </div>
    )
    }
    export default withMouse(App)
    Render Props
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // Render Props 的核心思想
    // 通过一个函数将class组件的state作为props传递给纯函数组件
    class Factory extends React.component {
    constructor(props) {
    super(props)
    this.state = {
    // state 即多个组件的公共逻辑的数据
    }
    }
    // 修改state
    render() {
    return <div>{this.props.render(this.state)}</div>
    }
    }

    const App = () => {
    <Factory render={
    // render 是一个函数组件
    (props)) => <P> {props.a} {props.b} </p>
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    import { Component } from "react";
    class Mouse extends Component {
    constructor(props) {
    super(props)
    this.state = {x: 0, y: 0}
    }
    handleMouseMove = (e) => {
    this.setState({
    x: e.clientX,
    y: e.clientY
    })
    }
    render() {
    return (
    <div style={{height: '500px'}} onMouseMove={this.handleMouseMove}>
    {/* 将当前state作为props,传递给render(render是一个纯函数) */}
    {this.props.render(this.state)}
    </div>
    )
    }
    }
    const App = (props) => (
    <div style={{height: '500px'}}>
    <p>{props.a}</p>
    <Mouse render={({x, y}) => <h1> {x}, {y} </h1>} />
    </div>
    )
    export default App

    Redux的使用

    三大原则

    单一数据源

    整个应用的 state被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个store中。

    State 是只读的

    唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。

    使用纯函数来执行修改

    为了描述 action 如何改变 state tree ,你需要编写 reducer

    单项数据流

    dispatch(action)

    reducer => newState

    subscribe 触发通知

    react-redux

    < Provider>

    mapStateToProps mapDispatchToProps

    异步action

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 同步action
    export const addTodo = text => {
    return {
    type: 'x',
    text
    }
    }

    // 异步action
    import { createStore, applyMiddleware } from 'redux'
    import thunk from 'redux-thunk'
    import rootReducer from './reducers/index'
    const store = createStore(rootReducer, applyMiddleware(thunk))
    export const addTodoAsync = text => {
    return (dispatch) => {
    fetch(url).then(res => dispatch(addTodo(res.text)))
    }
    }
    1
    button(callback) => dispatch(action) => reducer(state) => view

    redux数据流图

    img

    React原理

    函数式编程

    纯函数

    不可变值

    合成事件

    所有事件挂载到document

    event不是原生的,是SyntheticEvent合生事件对象

    更好的兼容性和跨平台

    setState batchUpdate

    有时异步(普通使用),有时同步(setTimeout, DOM事件)

    有时合并(对象模式),有时不合并(函数模式)

    setState看是否命中batchUpdate 判断isBatchingUpdates

    组件之间如何通讯?

    父子组件props

    Redux Context

    JSX本质

    createElement

    执行返回vnode

    shouldComponentUpdate用途

    性能优化

    配合不可变值,一起使用,否则会出错

    纯函数

    返回一个新值,没有副作用

    重点:不可变值

    如arr1 = arr,slice()

    函数组件和class组件的区别

    纯函数,输入props,输出JSX

    没有实例,没有生命周期,没有state

    不能扩展其他方法

    什么是受控组件?

    表单的值,受state控制

    需要自行监听onChange事件,更新state

    对比非受控组件

    多个组件有公共逻辑,如何抽离?

    高阶组件

    Render Props

    PureComponent

    实现了浅比较的shouldComponentUpdate

    优化性能

    但要结合不可变值使用

    React性能优化

    渲染列表加key

    自定义事件、DOM事件及时销毁

    合理使用异步组件

    减少函数bind this的次数

    合理使用SCU PureComponent和memo

    图片懒加载

    上一篇:
    Advanced JavaScript
    下一篇:
    vue-admin 后台系统总结
    本文目录
    本文目录