当前位置:   article > 正文

前端框架面试之 React 的使用、 React 的原理和 React 的面试真题_在前台开发中,下列关丁react的用法及原理,说法正确的是

在前台开发中,下列关丁react的用法及原理,说法正确的是
一、 React 的使用
  1. React 的基本知识点,如下所示:
  • JSX 基本使用,变量、表达式、class style、子元素和组件等等,代码如下所示:
import React from 'react'
import './style.css'
import List from '../List'

class JSXBaseDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '张三',
            imgUrl: 'https://img1.mukewang.com/5a9fc8070001a82402060220-140-140.jpg',
            flag: true
        }
    }
    render() {
        // // 获取变量 插值
        // const pElem = <p>{this.state.name}</p>
        // return pElem

        // // 表达式
        // const exprElem = <p>{this.state.flag ? 'yes' : 'no'}</p>
        // return exprElem

        // // 子元素
        // const imgElem = <div>
        //     <p>我的头像</p>
        //     <img src="xxxx.png"/>
        //     <img src={this.state.imgUrl}/>
        // </div>
        // return imgElem

        // // class
        // const classElem = <p className="title">设置 css class</p>
        // return classElem

        // // style
        // const styleData = { fontSize: '30px',  color: 'blue' }
        // const styleElem = <p style={styleData}>设置 style</p>
        // // 内联写法,注意 {{ 和 }}
        // // const styleElem = <p style={{ fontSize: '30px',  color: 'blue' }}>设置 style</p>
        // return styleElem

        // 原生 html
        const rawHtml = '<span>富文本内容<i>斜体</i><b>加粗</b></span>'
        const rawHtmlData = {
            __html: rawHtml // 注意,必须是这种格式
        }
        const rawHtmlElem = <div>
            <p dangerouslySetInnerHTML={rawHtmlData}></p>
            <p>{rawHtml}</p>
        </div>
        return rawHtmlElem

        // // 加载组件
        // const componentElem = <div>
        //     <p>JSX 中加载一个组件</p>
        //     <hr/>
        //     <List/>
        // </div>
        // return componentElem
    }
}

export default JSXBaseDemo

  • 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
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 条件判断,if else、三元表达式、逻辑运算符 && ||,代码如下所示:
import React from 'react'
import './style.css'

class ConditionDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'black'
        }
    }
    render() {
        const blackBtn = <button className="btn-black">black btn</button>
        const whiteBtn = <button className="btn-white">white btn</button>

        // // if else
        // if (this.state.theme === 'black') {
        //     return blackBtn
        // } else {
        //     return whiteBtn
        // }

        // // 三元运算符
        // return <div>
        //     { this.state.theme === 'black' ? blackBtn : whiteBtn }
        // </div>

        // &&
        return <div>
            { this.state.theme === 'black' && blackBtn }
        </div>
    }
}

export default ConditionDemo

  • 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
  • 渲染列表,mapkey,代码如下所示:
import React from 'react'

class ListDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            list: [
                {
                    id: 'id-1',
                    title: '标题1'
                },
                {
                    id: 'id-2',
                    title: '标题2'
                },
                {
                    id: 'id-3',
                    title: '标题3'
                }
            ]
        }
    }
    render() {
        return <ul>
            { /* vue v-for */
                this.state.list.map(
                    (item, index) => {
                        // 这里的 key 和 Vue 的 key 类似,必填,不能是 index 或 random
                        return <li key={item.id}>
                            index {index}; id {item.id}; title {item.title}
                        </li>
                    }
                )
            }
        </ul>
    }
}

export default ListDemo

  • 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
  • 事件,bind this,关于 event 参数,传递自定义参数,代码如下所示:
import React from 'react'

class EventDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'zhangsan',
            list: [
                {
                    id: 'id-1',
                    title: '标题1'
                },
                {
                    id: 'id-2',
                    title: '标题2'
                },
                {
                    id: 'id-3',
                    title: '标题3'
                }
            ]
        }

        // 修改方法的 this 指向
        this.clickHandler1 = this.clickHandler1.bind(this)
    }
    render() {
        // // this - 使用 bind
        // return <p onClick={this.clickHandler1}>
        //     {this.state.name}
        // </p>

        // // this - 使用静态方法
        // return <p onClick={this.clickHandler2}>
        //     clickHandler2 {this.state.name}
        // </p>

        // // event
        // return <a href="https://imooc.com/" onClick={this.clickHandler3}>
        //     click me
        // </a>

        // 传递参数 - 用 bind(this, a, b)
        return <ul>{this.state.list.map((item, index) => {
            return <li key={item.id} onClick={this.clickHandler4.bind(this, item.id, item.title)}>
                index {index}; title {item.title}
            </li>
        })}</ul>
    }
    clickHandler1() {
        // console.log('this....', this) // this 默认是 undefined
        this.setState({
            name: 'lisi'
        })
    }
    // 静态方法,this 指向当前实例
    clickHandler2 = () => {
        this.setState({
            name: 'lisi'
        })
    }
    // 获取 event
    clickHandler3 = (event) => {
        event.preventDefault() // 阻止默认行为
        event.stopPropagation() // 阻止冒泡
        console.log('target', event.target) // 指向当前元素,即当前元素触发
        console.log('current target', event.currentTarget) // 指向当前元素,假象!!!

        // 注意,event 其实是 React 封装的。可以看 __proto__.constructor 是 SyntheticEvent 组合事件
        console.log('event', event) // 不是原生的 Event ,原生的 MouseEvent
        console.log('event.__proto__.constructor', event.__proto__.constructor)

        // 原生 event 如下。其 __proto__.constructor 是 MouseEvent
        console.log('nativeEvent', event.nativeEvent)
        console.log('nativeEvent target', event.nativeEvent.target)  // 指向当前元素,即当前元素触发
        console.log('nativeEvent current target', event.nativeEvent.currentTarget) // 指向 document !!!

        // 1. event 是 SyntheticEvent ,模拟出来 DOM 事件所有能力
        // 2. event.nativeEvent 是原生事件对象
        // 3. 所有的事件,都被挂载到 document 上
        // 4. 和 DOM 事件不一样,和 Vue 事件也不一样
    }
    // 传递参数
    clickHandler4(id, title, event) {
        console.log(id, title)
        console.log('event', event) // 最后追加一个参数,即可接收 event
    }
}

export default EventDemo

  • 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
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 表单,受控组件,input、textarea、 selectvaluecheckbox、 radiochecked,代码如下所示:
import React from 'react'

class FormDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '张三',
            info: '个人信息',
            city: 'shanghai',
            flag: true,
            gender: 'male'
        }
    }
    render() {

        // // 受控组件(非受控组件,后面再讲)
        // return <div>
        //     <p>{this.state.name}</p>
        //     <label htmlFor="inputName">姓名:</label> {/* 用 htmlFor 代替 for */}
        //     <input id="inputName" value={this.state.name} onChange={this.onInputChange}/>
        // </div>

        // textarea - 使用 value
        return <div>
            <textarea value={this.state.info} onChange={this.onTextareaChange}/>
            <p>{this.state.info}</p>
        </div>

        // // select - 使用 value
        // return <div>
        //     <select value={this.state.city} onChange={this.onSelectChange}>
        //         <option value="beijing">北京</option>
        //         <option value="shanghai">上海</option>
        //         <option value="shenzhen">深圳</option>
        //     </select>
        //     <p>{this.state.city}</p>
        // </div>

        // // checkbox
        // return <div>
        //     <input type="checkbox" checked={this.state.flag} onChange={this.onCheckboxChange}/>
        //     <p>{this.state.flag.toString()}</p>
        // </div>

        // // radio
        // return <div>
        //     male <input type="radio" name="gender" value="male" checked={this.state.gender === 'male'} onChange={this.onRadioChange}/>
        //     female <input type="radio" name="gender" value="female" checked={this.state.gender === 'female'} onChange={this.onRadioChange}/>
        //     <p>{this.state.gender}</p>
        // </div>

        // 非受控组件 
    }
    onInputChange = (e) => {
        this.setState({
            name: e.target.value
        })
    }
    onTextareaChange = (e) => {
        this.setState({
            info: e.target.value
        })
    }
    onSelectChange = (e) => {
        this.setState({
            city: e.target.value
        })
    }
    onCheckboxChange = () => {
        this.setState({
            flag: !this.state.flag
        })
    }
    onRadioChange = (e) => {
        this.setState({
            gender: e.target.value
        })
    }
}

export default FormDemo

  • 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
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  1. 对于组件的使用,父子组件通讯,props 传递数据,props 传递函数,props 类型检查,代码如下所示:

import React from 'react'
import PropTypes from 'prop-types'

class Input extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            title: ''
        }
    }
    render() {
        return <div>
            <input value={this.state.title} onChange={this.onTitleChange}/>
            <button onClick={this.onSubmit}>提交</button>
        </div>
    }
    onTitleChange = (e) => {
        this.setState({
            title: e.target.value
        })
    }
    onSubmit = () => {
        const { submitTitle } = this.props
        submitTitle(this.state.title) // 'abc'

        this.setState({
            title: ''
        })
    }
}
// props 类型检查
Input.propTypes = {
    submitTitle: PropTypes.func.isRequired
}

class List extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        const { list } = this.props

        return <ul>{list.map((item, index) => {
            return <li key={item.id}>
                <span>{item.title}</span>
            </li>
        })}</ul>
    }
}
// props 类型检查
List.propTypes = {
    list: PropTypes.arrayOf(PropTypes.object).isRequired
}

class Footer extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        return <p>
            {this.props.text}
            {this.props.length}
        </p>
    }
    componentDidUpdate() {
        console.log('footer did update')
    }
    shouldComponentUpdate(nextProps, nextState) {
        if (nextProps.text !== this.props.text
            || nextProps.length !== this.props.length) {
            return true // 可以渲染
        }
        return false // 不重复渲染
    }

    // React 默认:父组件有更新,子组件则无条件也更新!!!
    // 性能优化对于 React 更加重要!
    // SCU 一定要每次都用吗?—— 需要的时候才优化
}

class TodoListDemo extends React.Component {
    constructor(props) {
        super(props)
        // 状态(数据)提升
        this.state = {
            list: [
                {
                    id: 'id-1',
                    title: '标题1'
                },
                {
                    id: 'id-2',
                    title: '标题2'
                },
                {
                    id: 'id-3',
                    title: '标题3'
                }
            ],
            footerInfo: '底部文字'
        }
    }
    render() {
        return <div>
            <Input submitTitle={this.onSubmitTitle}/>
            <List list={this.state.list}/>
            <Footer text={this.state.footerInfo} length={this.state.list.length}/>
        </div>
    }
    onSubmitTitle = (title) => {
        this.setState({
            list: this.state.list.concat({
                id: `id-${Date.now()}`,
                title
            })
        })
    }
}

export default TodoListDemo

  • 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
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  1. setState 是不可变值,可能是异步更新,可能会被合并,代码如下所示:
import React from 'react'

// 函数组件(后面会讲),默认没有 state
class StateDemo extends React.Component {
    constructor(props) {
        super(props)

        // 第一,state 要在构造函数中定义
        this.state = {
            count: 0
        }
    }
    render() {
        return <div>
            <p>{this.state.count}</p>
            <button onClick={this.increase}>累加</button>
        </div>
    }
    increase = () => {
        // // 第二,不要直接修改 state ,使用不可变值 ----------------------------
        // // this.state.count++ // 错误
        // this.setState({
        //     count: this.state.count + 1 // SCU
        // })
        // 操作数组、对象的的常用形式

        // 第三,setState 可能是异步更新(有可能是同步更新) ----------------------------
        
        // this.setState({
        //     count: this.state.count + 1
        // }, () => {
        //     // 联想 Vue $nextTick - DOM
        //     console.log('count by callback', this.state.count) // 回调函数中可以拿到最新的 state
        // })
        // console.log('count', this.state.count) // 异步的,拿不到最新值

        // // setTimeout 中 setState 是同步的
        // setTimeout(() => {
        //     this.setState({
        //         count: this.state.count + 1
        //     })
        //     console.log('count in setTimeout', this.state.count)
        // }, 0)

        // 自己定义的 DOM 事件,setState 是同步的。再 componentDidMount 中

        // 第四,state 异步更新的话,更新前会被合并 ----------------------------
        
        // // 传入对象,会被合并(类似 Object.assign )。执行结果只一次 +1
        // this.setState({
        //     count: this.state.count + 1
        // })
        // this.setState({
        //     count: this.state.count + 1
        // })
        // this.setState({
        //     count: this.state.count + 1
        // })
        
        // 传入函数,不会被合并。执行结果是 +3
        this.setState((prevState, props) => {
            return {
                count: prevState.count + 1
            }
        })
        this.setState((prevState, props) => {
            return {
                count: prevState.count + 1
            }
        })
        this.setState((prevState, props) => {
            return {
                count: prevState.count + 1
            }
        })
    }
    // bodyClickHandler = () => {
    //     this.setState({
    //         count: this.state.count + 1
    //     })
    //     console.log('count in body event', this.state.count)
    // }
    // componentDidMount() {
    //     // 自己定义的 DOM 事件,setState 是同步的
    //     document.body.addEventListener('click', this.bodyClickHandler)
    // }
    // componentWillUnmount() {
    //     // 及时销毁自定义 DOM 事件
    //     document.body.removeEventListener('click', this.bodyClickHandler)
    //     // clearTimeout
    // }
}

export default StateDemo


// // 不可变值(函数式编程,纯函数) - 数组
// const list5Copy = this.state.list5.slice()
// list5Copy.splice(2, 0, 'a') // 中间插入/删除
// this.setState({
//     list1: this.state.list1.concat(100), // 追加
//     list2: [...this.state.list2, 100], // 追加
//     list3: this.state.list3.slice(0, 3), // 截取
//     list4: this.state.list4.filter(item => item > 100), // 筛选
//     list5: list5Copy // 其他操作
// })
// // 注意,不能直接对 this.state.list 进行 push pop splice 等,这样违反不可变值

// // 不可变值 - 对象
// this.setState({
//     obj1: Object.assign({}, this.state.obj1, {a: 100}),
//     obj2: {...this.state.obj2, a: 100}
// })
// // 注意,不能直接对 this.state.obj 进行属性设置,这样违反不可变值

  • 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
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  1. setState 有的是同步的,有的是异步的,代码如下所示:
import React from 'react'

class ListDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
    }
    render() {
        return <p>{this.state.count}</p>
    }
    componentDidMount() {
        // count 初始值为 0
        this.setState({ count: this.state.count + 1 })
        console.log('1', this.state.count) // 0
        this.setState({ count: this.state.count + 1 })
        console.log('2', this.state.count) // 0
        setTimeout(() => {
            this.setState({ count: this.state.count + 1 })
            console.log('3', this.state.count) // 2
        })
        setTimeout(() => {
            this.setState({ count: this.state.count + 1 })
            console.log('4', this.state.count) // 3
        })
    }
}

export default ListDemo

  • 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
  1. React 的组件生命周期,单组件生命周期,父子组件生命周期,和 Vue 的一样。

  2. 对于 React 的高级特性,分为函数组件、非受控组件、Portalscontext 、异步组件、性能优化、高阶组件 HOCRender Props,如下所示:

  • 函数组件,纯函数,输入 props,输出 JSX。没有实例,没有生命周期,没有 state,不能扩展其它办法
  • 非受控组件,refdefaultValuedefaultChecked,手动操作 DOM 元素。对于非受控组件的使用场景,必须手动操作 DOM 元素,setState 实现不了。文件上传 <input type=file>, 某些富文本编辑器,需要传入 DOM 元素。对于受控组件和非受控组件的比较,优先使用受控组件,符合 React 的设计原则。必须操作 DOM 时,再使用非受控组件,代码如下所示:
import React from 'react'

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '张三',
            flag: true,
        }
        this.nameInputRef = React.createRef() // 创建 ref
        this.fileInputRef = React.createRef()
    }
    render() {
        // // input defaultValue
        // return <div>
        //     {/* 使用 defaultValue 而不是 value ,使用 ref */}
        //     <input defaultValue={this.state.name} ref={this.nameInputRef}/>
        //     {/* state 并不会随着改变 */}
        //     <span>state.name: {this.state.name}</span>
        //     <br/>
        //     <button onClick={this.alertName}>alert name</button>
        // </div>

        // // checkbox defaultChecked
        // return <div>
        //     <input
        //         type="checkbox"
        //         defaultChecked={this.state.flag}
        //     />
        // </div>

        // file
        return <div>
            <input type="file" ref={this.fileInputRef}/>
            <button onClick={this.alertFile}>alert file</button>
        </div>

    }
    alertName = () => {
        const elem = this.nameInputRef.current // 通过 ref 获取 DOM 节点
        alert(elem.value) // 不是 this.state.name
    }
    alertFile = () => {
        const elem = this.fileInputRef.current // 通过 ref 获取 DOM 节点
        alert(elem.files[0].name)
    }
}

export default App

  • 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
  • 49
  • 50
  • React Portals,组件默认会按照既定层次嵌套渲染,让组件渲染到父组件之外。对于 Portals 的使用场景,overflow: hidden,父组件 z-index 值太小,flexd 需要放在 body 第一层级,代码如下所示:
import React from 'react'
import ReactDOM from 'react-dom'
import './style.css'

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
        }
    }
    render() {
        // // 正常渲染
        // return <div className="modal">
        //     {this.props.children} {/* vue slot */}
        // </div>

        // 使用 Portals 渲染到 body 上。
        // fixed 元素要放在 body 上,有更好的浏览器兼容性。
        return ReactDOM.createPortal(
            <div className="modal">{this.props.children}</div>,
            document.body // DOM 节点
        )
    }
}

export default App

  • 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
  • context,上下文环境,公共信息传递给每个组件,用 props 太繁琐,用 redux 小题大做,代码如下所示:
import React from 'react'

// 创建 Context 填入默认值(任何一个 js 变量)
const ThemeContext = React.createContext('light')

// 底层组件 - 函数是组件
function ThemeLink (props) {
    // const theme = this.context // 会报错。函数式组件没有实例,即没有 this

    // 函数式组件可以使用 Consumer
    return <ThemeContext.Consumer>
        { value => <p>link's theme is {value}</p> }
    </ThemeContext.Consumer>
}

// 底层组件 - class 组件
class ThemedButton extends React.Component {
    // 指定 contextType 读取当前的 theme context。
    // static contextType = ThemeContext // 也可以用 ThemedButton.contextType = ThemeContext
    render() {
        const theme = this.context // React 会往上找到最近的 theme Provider,然后使用它的值。
        return <div>
            <p>button's theme is {theme}</p>
        </div>
    }
}
ThemedButton.contextType = ThemeContext // 指定 contextType 读取当前的 theme context。

// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar(props) {
    return (
        <div>
            <ThemedButton />
            <ThemeLink />
        </div>
    )
}

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'light'
        }
    }
    render() {
        return <ThemeContext.Provider value={this.state.theme}>
            <Toolbar />
            <hr/>
            <button onClick={this.changeTheme}>change theme</button>
        </ThemeContext.Provider>
    }
    changeTheme = () => {
        this.setState({
            theme: this.state.theme === 'light' ? 'dark' : 'light'
        })
    }
}

export default App

  • 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
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 异步组件,import()、React.lazy、React.Suspense,代码如下所示:
import React from 'react'

const ContextDemo = React.lazy(() => import('./ContextDemo'))

class App extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        return <div>
            <p>引入一个动态组件</p>
            <hr />
            <React.Suspense fallback={<div>Loading...</div>}>
                <ContextDemo/>
            </React.Suspense>
        </div>

        // 1. 强制刷新,可看到 loading (看不到就限制一下 chrome 网速)
        // 2. 看 network 的 js 加载
    }
}

export default App

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  1. 对于 React 的性能优化,shouldComponentUpdate,简称 SCUPureComponentReact.memo,不可变值 immutable.js,如下所示:
  • SCUSCU 默认返回 true,即 React 默认重新渲染所有子组件,必须配合 不可变值 一起使用。可先不用 SCU,有性能问题时再考虑使用,代码如下所示:
import React from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash'

class Input extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            title: ''
        }
    }
    render() {
        return <div>
            <input value={this.state.title} onChange={this.onTitleChange}/>
            <button onClick={this.onSubmit}>提交</button>
        </div>
    }
    onTitleChange = (e) => {
        this.setState({
            title: e.target.value
        })
    }
    onSubmit = () => {
        const { submitTitle } = this.props
        submitTitle(this.state.title)

        this.setState({
            title: ''
        })
    }
}
// props 类型检查
Input.propTypes = {
    submitTitle: PropTypes.func.isRequired
}

class List extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        const { list } = this.props

        return <ul>{list.map((item, index) => {
            return <li key={item.id}>
                <span>{item.title}</span>
            </li>
        })}</ul>
    }

    // 增加 shouldComponentUpdate
    shouldComponentUpdate(nextProps, nextState) {
        // _.isEqual 做对象或者数组的深度比较(一次性递归到底)
        if (_.isEqual(nextProps.list, this.props.list)) {
            // 相等,则不重复渲染
            return false
        }
        return true // 不相等,则渲染
    }
}
// props 类型检查
List.propTypes = {
    list: PropTypes.arrayOf(PropTypes.object).isRequired
}

class TodoListDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            list: [
                {
                    id: 'id-1',
                    title: '标题1'
                },
                {
                    id: 'id-2',
                    title: '标题2'
                },
                {
                    id: 'id-3',
                    title: '标题3'
                }
            ]
        }
    }
    render() {
        return <div>
            <Input submitTitle={this.onSubmitTitle}/>
            <List list={this.state.list}/>
        </div>
    }
    onSubmitTitle = (title) => {
        // 正确的用法
        this.setState({
            list: this.state.list.concat({
                id: `id-${Date.now()}`,
                title
            })
        })

        // // 为了演示 SCU ,故意写的错误用法
        // this.state.list.push({
        //     id: `id-${Date.now()}`,
        //     title
        // })
        // this.setState({
        //     list: this.state.list
        // })
    }
}

export default TodoListDemo

  • 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
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • PureComponentmemoPureComponentSCU 中实现了浅比较。memo,函数组件中的 PureComponent,浅比较已使用大部分情况,尽量不要做深度比较

  • immutable.js,彻底拥抱不可变质,基于共享数据,不是深拷贝,速度好,按需使用

  1. 关于组件公共逻辑的抽离,mixin,已被 React 弃用,高阶组件 HOC,Render PropsHOC 是模式简单,但会增加组件层级,Render Props 是代码简洁,学习成本较高,按需使用,代码如下所示:
  • HOC.js
    import React from 'react'
    
    // 高阶组件
    const withMouse = (Component) => {
        class withMouseComponent extends React.Component {
            constructor(props) {
                super(props)
                this.state = { x: 0, y: 0 }
            }
      
            handleMouseMove = (event) => {
                this.setState({
                    x: event.clientX,
                    y: event.clientY
                })
            }
      
            render() {
                return (
                    <div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>
                        {/* 1. 透传所有 props 2. 增加 mouse 属性 */}
                        <Component {...this.props} mouse={this.state}/>
                    </div>
                )
            }
        }
        return withMouseComponent
    }
    
    const App = (props) => {
        const a = props.a
        const { x, y } = props.mouse // 接收 mouse 属性
        return (
            <div style={{ height: '500px' }}>
                <h1>The mouse position is ({x}, {y})</h1>
                <p>{a}</p>
            </div>
        )
    }
    
    export default withMouse(App) // 返回高阶函数
    
    
    • 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
  • RenderProp.js
    import React from 'react'
    import PropTypes from 'prop-types'
    
    class Mouse extends React.Component {
        constructor(props) {
            super(props)
            this.state = { x: 0, y: 0 }
        }
      
        handleMouseMove = (event) => {
          this.setState({
            x: event.clientX,
            y: event.clientY
          })
        }
      
        render() {
          return (
            <div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>
                {/* 将当前 state 作为 props ,传递给 render (render 是一个函数组件) */}
                {this.props.render(this.state)}
            </div>
          )
        }
    }
    Mouse.propTypes = {
        render: PropTypes.func.isRequired // 必须接收一个 render 属性,而且是函数
    }
    
    const App = (props) => (
        <div style={{ height: '500px' }}>
            <p>{props.a}</p>
            <Mouse render={
                /* render 是一个函数组件 */
                ({ x, y }) => <h1>The mouse position is ({x}, {y})</h1>
            }/>
            
        </div>
    )
    
    /**
     * 即,定义了 Mouse 组件,只有获取 x y 的能力。
     * 至于 Mouse 组件如何渲染,App 说了算,通过 render prop 的方式告诉 Mouse 。
     */
    
    export default App
    
    
    • 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
  1. Redux 的使用,分为基本概念、单项数据流、react-redux、异步 action 和中间件,如下所示:
  • redux 的单向数据流,store、state、action 和 reducer。应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中。 惟一改变 state 的办法是触发 action,一个描述发生什么的对象。 为了描述 action 如何改变 state 树,你需要编写 reducers。这是一个 reducer,形式为(state, action) => state的纯函数,描述了 action 如何把 state 转变成下一个 statestate 的形式取决于你,可以是基本类型、数组、对象、甚至是 Immutable.js 生成的数据结构。惟一的要点是当 state 变化时需要返回全新的对象,而不是修改传入的参数。改变内部 state 惟一方法是 dispatch 一个 actionaction 可以被序列化,用日记记录和储存下来,后期还可以以回放的方式执行。对于单向数据流,dispatch(action)、reducer -> newState、subscribe 触发通知
  • 对于react-redux,<Provider> content、connect、mapStateToProps 和 mapDispatchToProps
  • 对于异步 action,有 redux-thunk、redux-promise 和 redux-saga
  • 对于 redux 中间件,在单向数据流的时候,button 进行 callback 指向 dispatchdispatch 通过 action 指向 reducerreducer 通过 state 指向 view。在有中间件以后,button 通过 callback 指向 new dispatch。在 new dispatch 中,mid1 -> mid2 -> action -> dispatch。最后 new dispatch 通过 action 指向 reducer
  1. 对于 redux 的使用,代码如下所示:
  • action 文件夹

    • index.js
      let nextTodoId = 0
      
      // 创建一个 todo
      export const addTodo = text => {
        return {
          type: 'ADD_TODO',
          id: nextTodoId++,
          text
        }
      }
      
      // 设置完成状态
      export const setVisibilityFilter = filter => {
        return {
          type: 'SET_VISIBILITY_FILTER',
          filter
        }
      }
      
      // 切换 todo 完成状态
      export const toggleTodo = id => {
        return {
          type: 'TOGGLE_TODO',
          id
        }
      }
      
      • 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
  • components 文件夹

    • App.js

      import React from 'react'
      import Footer from './Footer'
      import AddTodo from '../containers/AddTodo'
      import VisibleTodoList from '../containers/VisibleTodoList'
      
      const App = () => (
        <div>
          <AddTodo />
          <VisibleTodoList />
          <Footer />
        </div>
      )
      
      export default App
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    • Footer.js

          import React from 'react'
          import FilterLink from '../containers/FilterLink'
          
          const Footer = () => (
            <p>
              Show:
              {' '}
              <FilterLink filter="SHOW_ALL">
                All
              </FilterLink>
              {', '}
              <FilterLink filter="SHOW_ACTIVE">
                Active
              </FilterLink>
              {', '}
              <FilterLink filter="SHOW_COMPLETED">
                Completed
              </FilterLink>
            </p>
          )
          
          export default Footer
          ```
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
    • Link.js

      import React from 'react'
      import PropTypes from 'prop-types'
      
      const Link = ({ active, children, onClick }) => {
        if (active) {
          return <span>{children}</span>
        }
      
        return (
          <a
            href=""
            onClick={e => {
              e.preventDefault()
              onClick() // 设置过滤状态
            }}
          >
            {children}
          </a>
        )
      }
      
      Link.propTypes = {
        active: PropTypes.bool.isRequired,
        children: PropTypes.node.isRequired,
        onClick: PropTypes.func.isRequired
      }
      
      export default Link
      
      • 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
    • Todo.js

      import React from 'react'
      import PropTypes from 'prop-types'
      
      // 函数组件 { onClick, completed, text } = props
      const Todo = ({ onClick, completed, text }) => (
        <li
          onClick={onClick}
          style={ {
            textDecoration: completed ? 'line-through' : 'none'
          }}
        >
          {text}
        </li>
      )
      
      Todo.propTypes = {
        onClick: PropTypes.func.isRequired,
        completed: PropTypes.bool.isRequired,
        text: PropTypes.string.isRequired
      }
      
      export default Todo
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
    • TodoList.js

          import React from 'react'
          import PropTypes from 'prop-types'
          import Todo from './Todo'
          
          // 函数组件 { todos, onTodoClick } = props
          const TodoList = ({ todos, onTodoClick }) => (
            <ul>
              {todos.map(todo => (
                <Todo key={todo.id} {...todo} onClick={() => onTodoClick(todo.id)} /> // 点击切换完成状态
              ))}
            </ul>
          )
          
          TodoList.propTypes = {
            todos: PropTypes.arrayOf(
              PropTypes.shape({
                id: PropTypes.number.isRequired,
                completed: PropTypes.bool.isRequired,
                text: PropTypes.string.isRequired
              }).isRequired
            ).isRequired,
            onTodoClick: PropTypes.func.isRequired
          }
          
          export default TodoList
      
      • 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
  • containers 文件夹

    • AddTodo.js
          import React from 'react'
          import { connect } from 'react-redux'
          import { addTodo } from '../actions'
          
          // 函数组件,接收 props 参数
          let AddTodo = ({ dispatch }) => {
            // dispatch 即 props.dispatch
          
            let input
          
            return (
              <div>
                <form
                  onSubmit={e => {
                    e.preventDefault()
                    if (!input.value.trim()) {
                      return
                    }
                    // 创建一个 todo
                    dispatch(addTodo(input.value))
                    input.value = ''
                  }}
                >
                  <input
                    ref={node => {
                      input = node
                    }}
                  />
                  <button type="submit">
                    Add Todo
                  </button>
                </form>
              </div>
            )
          }
          
          // connect 高阶组件 ,将 dispatch 作为 props 注入到 AddTodo 组件中
          AddTodo = connect()(AddTodo)
          
          export default AddTodo
      
      • 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
    • FilterLink.js
      import { connect } from 'react-redux'
      import { setVisibilityFilter } from '../actions'
      import Link from '../components/Link'
      
      const mapStateToProps = (state, ownProps) => {
        // ownProps 即父组件传来的 `filter="SHOW_ALL"`
        return {
          active: ownProps.filter === state.visibilityFilter
        }
      }
      
      const mapDispatchToProps = (dispatch, ownProps) => {
        // ownProps 即父组件传来的 `filter="SHOW_ALL"`
        return {
          onClick: () => {
            // 设置过滤状态
            dispatch(setVisibilityFilter(ownProps.filter))
          }
        }
      }
      
      const FilterLink = connect(
        mapStateToProps,
        mapDispatchToProps
      )(Link)
      
      export default FilterLink
      
      • 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
    • VisibleTodoList.js
      import { connect } from 'react-redux'
      import { toggleTodo } from '../actions'
      import TodoList from '../components/TodoList'
      
      // 不同类型的 todo 列表
      const getVisibleTodos = (todos, filter) => {
        switch (filter) {
          case 'SHOW_ALL':
            return todos
          case 'SHOW_COMPLETED':
            return todos.filter(t => t.completed)
          case 'SHOW_ACTIVE':
            return todos.filter(t => !t.completed)
        }
      }
      
      const mapStateToProps = state => {
        // state 即 vuex 的总状态,在 reducer/index.js 中定义
        return {
          // 根据完成状态,筛选数据
          todos: getVisibleTodos(state.todos, state.visibilityFilter)
        }
      }
      
      const mapDispatchToProps = dispatch => {
        return {
          // 切换完成状态
          onTodoClick: id => {
            dispatch(toggleTodo(id))
          }
        }
      }
      
      // connect 高阶组件,将 state 和 dispatch 注入到组件 props 中
      const VisibleTodoList = connect(
        mapStateToProps,
        mapDispatchToProps
      )(TodoList)
      
      export default VisibleTodoList
      
      • 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
  • reducers 文件夹

    • index.js
      import { combineReducers } from 'redux'
      import todos from './todos'
      import visibilityFilter from './visibilityFilter'
      
      const todoApp = combineReducers({
        todos,
        visibilityFilter
      })
      
      export default todoApp
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    • todos.js
          const todos = (state = [], action) => {
          switch (action.type) {
            // 创建一个 todo
            case 'ADD_TODO':
              // 注意,返回不可变数据
              return [
                ...state,
                {
                  id: action.id,
                  text: action.text, // title
                  completed: false
                }
              ]
            
            // 切换一个 todo 的完成状态
            case 'TOGGLE_TODO':
              // 注意,返回不可变数据
              return state.map(todo =>
                (todo.id === action.id) 
                  ? {...todo, completed: !todo.completed} // 切换完成状态
                  : todo
              )
            default:
              return state
          }
        }
        
        export default todos
      
      • 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
    • visibilityFilter.js
          const visibilityFilter = (state = 'SHOW_ALL', action) => {
          switch (action.type) {
            // 设置显示类型(所有、完成、未完成)
            case 'SET_VISIBILITY_FILTER':
              return action.filter
      
            // 默认是 SHOW_ALL
            default:
              return state
          }
        }
        
        export default visibilityFilter
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
  • index.js

    import React from 'react'
    import { Provider } from 'react-redux'
    import { createStore } from 'redux'
    import todoApp from './reducers'
    import App from './components/App'
    
    let store = createStore(todoApp)
    
    export default function () {
        return <Provider store={store}>
            <App />
        </Provider>
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  1. React-router,路由模式 hashH5 history,路由配置 动态路由和懒加载。hash 模式是默认的,如 http://abc.com/#/user/10。H5 history 模式,如 http://abc.com/user/20,后者需要 server 端支持,因此无特殊需求可选择前者。
二、React 的原理
  1. 对于 React 的原理,分为函数式编程、vdomdiffJSX 本质、合成事件、setStatebatchUpdate、组件渲染过程。

  2. 对于函数式编程,一种编程范式,纯函数,不可变值。不可变值数组,不能直接对 this.state.list 进行 push pop splice 等,这样违反不可变值。不可变值对象,不能直接对 this.state.obj 进行属性设置,这样违反不可变值。state 要在构造函数中定义
    ;不要直接修改 state,使用不可变值;setState 可能是异步更新(有可能是同步更新);state 异步更新的话,更新前会被合并。

  3. 对于 vdomdiffh 函数、vdom 数据结构、patch 函数。只比较同一层级,不跨级比较。tag 不相同,则直接删掉重建,不再深度比较。tagkey,两者都相同,则认为是相同节点,不再深度比较。

  4. 对于 JSX 的本质,JSX 等同于 Vue 模版,Vue 模版不是 htmlJSX 也不是 JS。如第一个参数是 List 组件,找到 List 组件 jsx 结构,继续拆分。

  5. 对于合成事件,所有事件挂载到 document 上,event 不是原生的,是 SyntheticEvent 合成事件对象。它和 Vue 事件不同,和 DOM 事件也不同。需要合成事件机制的原因,如下所示:

  • 更好的兼容性和跨平台
  • 挂载到 document,减少内存消耗,避免频繁解绑
  • 方便事件的统一管理,如事务机制
  1. setStatebatchUpdate,如下所示:
  • 有时异步,普通使用的时候;有时同步,setTimeoutDOM 事件的时候
  • 有时合并,对象形式;有时不合并,函数形式;
  • 后者相对于好理解,像 Object.assign,前者相对于更重要
  • 对于核心要点,setState 主流程、batchUpdate机制、transition 事务机制
  • 对于 setState 主流程,this.setState(newState)newState 存入 pending 队列,然后判断是否处于 batch update。如果是的话,保存组件于 dirtyComponents 中。如果不是的话,遍历所有的 dirtyComponents,调用 updateComponent,更新 pending state or props
  • 对于 setState 是同步的还是异步的,其实 setState 无所谓异步还是同步,看是否能够命中 batchUpdate 机制,判断 isBatchingUpdates
  • 对于哪些能够命中 batchUpdate 机制,如下所示:
    • 生命周期和它调用的函数
    • React 中注册的事件和它调用的函数
    • React 可以管理的入口
  • 对应哪些不能够命中 batchUpdate 机制,如下所示:
    • setTimeout、setInterval 等和它调用的函数
    • 自定义的 DOM 事件和它调用的函数
    • React 管不到的入口
  • 对于 transition 事务机制,开始处于 batchUpdateisBatchUpdates = true,其它任何操作,结束,isBatchUpdates = false
  1. 对于组件渲染和更新过程,JSX 如何渲染为页面,setState 之后如何更新页面,以及对于全流程的理解,如下所示:
  • JSXcreateElement 函数,执行生成 vnodepatch(elem, vnode)patch(vnode, newVnode)
  • 对于组件的渲染过程,props state,render() 生成 vnodepatch(elem, vnode)
  • 对于组件的更新过程,setState(newState) -> dirtyComponents,可能有子组件。render() 生成 newVnodepatch(vnode, newVnode)
  • 对于 patch 更新的两个阶段,reconciliation 阶段执行 diff 算法,纯 JS 计算。在 commit 阶段,将 diff 结果渲染 DOM
  • 对于可能出现的性能问题,JS 是单线程的,且和 DOM 渲染共用一个线程。当组件足够复杂的时候,组件更新时计算和渲染都压力大,同时再有 DOM 操作需求,如动画、鼠标拖拽等,将卡顿
  • 解决方案就是 fiber,将 reconciliation 阶段进行任务拆分,但是 commit 无法拆分。DOM 需要渲染时暂停,空闲时修复,window.requestIdleCallback
三、React 的面试真题
  1. 组件之间如何通讯,答案如下所示:
  • 父子组件 props
  • 自定义事件
  • ReduxContext
  1. JSX 的本质是什么,答案如下所示:
  • createElement
  • 执行返回 vnode
  1. Context 是什么,如何应用,答案如下所示:
  • 父组件,向其下所有子孙组件传递信息
  • 一些简单的公共信息,主题色、颜色等等
  • 复杂的公共信息,用 redux
  1. shouldComponentUpdate 的用途,答案如下所示:
  • 性能优化
  • 配合不可变值一起使用,否则会出错
  1. 什么是纯函数,答案如下所示:
  • 返回一个新值,没有副作用,不会修改其它值
  • 重点就是不可变值
  • arr1 = arr.slice()
  1. 对于 React 的组件生命周期,答案如下所示:
  • 单组件生命周期
  • 父子组件生命周期
  • 注意 SCU
  1. React 发起 ajax 应该在哪个生命周期,答案如下所示:
  • componentDidMount 这个生命周期函数
  1. 渲染列表,为何使用 key,答案如下所示:
  • 必须用 key,且不能是 indexrandom
  • diff 算法中通过 tagkey 来判断,是否是 sameNode
  • 减少渲染次数,提升渲染性能
  1. 函数组件和 class 组件的区别,答案如下所示:
  • 纯函数,输入 props,输出 JSX
  • 没有实例,没有生命周期,没有 state
  • 不能扩展其它办法
  1. 什么是受控组件,答案如下所示:
  • 表单的值,受 state 控制
  • 需要自行监听 onChange,更新 state
  • 对比非受控组件
  1. 何时使用异步组件,答案如下所示:
  • 加载大组件
  • 路由懒加载
  1. 多个组件有公共逻辑,如何抽离,答案如下所示:
  • 高阶组件
  • Render Props
  • mixin 已被 React 废弃
  1. redux 如何进行异步请求,答案如下所示:
  • 使用异步 action
  • redux-thunk
  1. PureComponent 有何区别,答案如下所示:
  • 实现了浅比较的 shouldComponentUpdate
  • 优化性能
  • 需要结合不可变值使用
  1. React 事件和 DOM 事件的区别,答案如下所示:
  • 所有事件挂载到 document
  • event 不是原生的,是 SyntheticEvent 合成事件对象
  • dispatchEvent
  1. 对于 React 的性能优化,答案如下所示:
  • 渲染列表时添加 key
  • 自定义事件、DOM 事件及时销毁
  • 合理使用异步组件
  • 减少函数 bind this 的次数
  • 合理使用 SCU、PureComponent 和 memo
  • 合理使用 Immutable.js
  • webpack 层面的优化
  • 前端通用的性能优化,如图片懒加载
  • 使用 SSR
  1. ReactVue 的区别,答案如下所示:
  • 都支持组件化
  • 都是数据驱动视图
  • 都使用 vdom 操作 DOM
  • React 使用 JSX 拥抱 JSVue 使用模版拥抱 html
  • React 函数式编程,Vue 声明式编程
  • React 更多的需要是自力更生,Vue 是把想要的都给你
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/650577
推荐阅读
相关标签
  

闽ICP备14008679号