react16知识点总结

一、基本使用

1、create-react-app

2.JSX语法

(1)变量、表达式

        // 获取变量 插值
        const pElem = <p>{this.state.name}</p>
        return pElem
        // 表达式
        const exprElem = <p>{this.state.flag ? 'yes' : 'no'}</p>
        return exprElem

(2)class style

 // 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

(3)子元素和组件

       // 子元素
        const imgElem = <div>
            <p>我的头像</p>
            <img src="xxxx.png"/>
            <img src={this.state.imgUrl}/>
        </div>
        return imgElem
       // 加载组件
        const componentElem = <div>
            <p>JSX 中加载一个组件</p>
            <hr/>
            <List/>
        </div>
        return componentElem

3.条件

        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>

4.列表渲染

 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>
    }

5.事件

(1)bind this 改变事件指向

 this.clickHandler1 = this.clickHandler1.bind(this)

(2)关于event参数:
React16事件绑定到document上
1. event 是 SyntheticEvent ,模拟出来 DOM 事件所 有能力
2. event.nativeEvent 是原生事件对象
3. 所有的事件,都被挂载到 document 上
4. 和 DOM 事件不一样,和 Vue 事件也不一样
5.
React17及以后的版本事件绑定到root组件上
这样有利于多个React版本共存,例如微前端
(3)传递自定义参数

 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>
   // 传递参数
    clickHandler4(id, title, event) {
        console.log(id, title)
        console.log('event', event) // 最后追加一个参数,即可接收 event
    }

6.组件和props(类型检查)

(1)父组件将参数和方法直接绑定在子组件上,子组件使用props接受父组件传递过来的参数和方法
(2)props类型检查:

import PropTypes from 'prop-types'
Input.propTypes = {
    submitTitle: PropTypes.func.isRequired//submitTitle是父组件传递过来的方法
}

7.state和setState

在类组件中使用state和setState,函数组件中没有。
(1)state:在constructor(构造函数)中定义。不要直接修改state,使用不可变值
(2)setState:
1️⃣不可变值:不能直接对 this.state.list 进行 push 、pop 、splice 等(对于数组我们可以使用cancat、filter、slice、扩展运算符或单独外部定义一个新数组处理后再在setState中赋值等方法进行修改)。不能直接对 this.state.obj 进行属性设置,这样违反不可变值这样违反不可变值,可以使用Object.assign或扩展运算符的形式进行对象的修改。
2️⃣可能是异步更新:

 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 中
    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)
    }

3️⃣可能被合并

 // 传入对象,会被合并(类似 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
            }
        })

8.组件和生命周期

单组件生命周期:
在这里插入图片描述
父子组件生命周期:当父组建 render 时遇到子组件,然后进入子组件的生命周期,当执行完子组件生命周期中的componentDidMount 时会回到父组建继续执行父组建未完成的生命周期。

二、高级特性

1、函数组件

纯函数,输入props 输出JSX,没有实例,没有生命周期,没有state

2、受控和非受控组件

(1)受控组件:表单里的值收到setState的控制(通过绑定onChange等事件)
(2)非受控组件:表单组件的值不受setState控制,不绑定onChange时间,而是同时绑定ref的方式对dom元素进行操作(应用场景:文件上传等)

3、refs

react18:

//函数组件
import {useRef} from 'react'
const inputRef=useRef(null)
<imput ref={inputRef}/>

react16:

//类组件
import React from 'react'
this.inputRef= React.createRef() 
 <input  ref={this.inputRef}/>

4、Protals(传送门)

组件默认会按照既定层次嵌套渲染,如何让组件渲染到父组件以外?
使用场景:overflow:hidden,父组件z-index值太小、fixed需要放在body第一层级

import React from 'react'
import ReactDOM from 'react-dom'
import './style.css'//这里设置元素fixed定位

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

5、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

6、异步组件(懒加载)

import( )、React.lazy、React.Suspence

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

7、性能优化

1️⃣shouldComponantUpdate(SCU)
SCU默认返回true,即React默认重新渲染所有子组件,必须配合“不可变值”一起使用,可先不用SCU,有性能问题时再考虑使用
当组件内部其他子组件更新时,无需更新的组件也会发起更新,如果引起页面卡顿则可以使用SCU钩子,判断nextProps和目前传入的props内容是否一致,如果需要更新则返回true 否则返回false

import React from 'react'
import _ from 'lodash'
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 // 不重复渲染
    //  }
 // 增加 shouldComponentUpdate这里_是引入的lodash
    shouldComponentUpdate(nextProps, nextState) {
        // _.isEqual 做对象或者数组的深度比较(一次性递归到底)
        if (_.isEqual(nextProps.list, this.props.list)) {
            // 相等,则不重复渲染
            return false
        }
        return true // 不相等,则渲染
    }
    // React 默认:父组件有更新,子组件则无条件也更新!!!
    // 性能优化对于 React 更加重要!
    // SCU 一定要每次都用吗?—— 需要的时候才优化
}

2️⃣纯组件PureComponant和memo
(1)PureComponant:
创建类组件的时候使用React.PureComponent,相当于做了一个SCU的浅比较

class List extends React.PureComponent {
    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() {/*浅比较*/}
}
// props 类型检查
List.propTypes = {
    list: PropTypes.arrayOf(PropTypes.object).isRequired
}

(2)memo:

function MyComponent(props){
return <div>{props.text}</div>
}
function areEqual(preProps,nextProps){
//比较preProps和nextProps是否一致,一致则返回true ,否则返回false
}
export default React.memo(MyComponent,areEqual)

3️⃣不可变值immutablejs
基于共享数据(不是深拷贝),速度好,有一定的学习和迁移成本,按需使用

const map1=Immutable.Map({a:1,b:2,c:3})
const map2=map1.set('b',50)
map1.get('b') //2
map2.get('b') //50

11、高阶组件HOC

高阶组件不是一种功能,而是一种模式,在高阶组件里定义一些公共逻辑,来包裹有需要的组件,通过属性绑定的方式传到子组件中来。

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) // 返回高阶函数

12、Render Props

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、redux :

1️⃣store2️⃣reducer3️⃣action4️⃣dispatch

import { createStore } from 'redux'

/**
 * 这是一个 reducer 函数:接受当前 state 值和描述“发生了什么”的 action 对象,它返回一个新的 state 值。
 * reducer 函数签名是 : (state, action) => newState
 *
 * Redux state 应该只包含普通的 JS 对象、数组和原语。
 * 根状态值通常是一个对象。 重要的是,不应该改变 state 对象,而是在 state 发生变化时返回一个新对象。
 *
 * 你可以在 reducer 中使用任何条件逻辑。 在这个例子中,我们使用了 switch 语句,但这不是必需的。
 * 
 */
function counterReducer(state = { value: 0 }, action) {
  switch (action.type) {
    case 'counter/incremented':
      return { value: state.value + 1 }
    case 'counter/decremented':
      return { value: state.value - 1 }
    default:
      return state
  }
}

// 创建一个包含应用程序 state 的 Redux store。
// 它的 API 有 { subscribe, dispatch, getState }.
let store = createStore(counterReducer)

// 你可以使用 subscribe() 来更新 UI 以响应 state 的更改。
// 通常你会使用视图绑定库(例如 React Redux)而不是直接使用 subscribe()。
// 可能还有其他用例对 subscribe 也有帮助。

store.subscribe(() => console.log(store.getState()))

// 改变内部状态的唯一方法是 dispatch 一个 action。
// 这些 action 可以被序列化、记录或存储,然后再重放。
store.dispatch({ type: 'counter/incremented' })
// {value: 1}
store.dispatch({ type: 'counter/incremented' })
// {value: 2}
store.dispatch({ type: 'counter/decremented' })
// {value: 1}

5️⃣单向数据流模型
dispatch调用action=>reducer更新newState=>subcribe触发通知更新视图
在这里插入图片描述

6️⃣中间件redux-thunk、redux-saga
用于改造dispatch方法

2、react-redux

1️⃣provider

 <Provider store={store}>
        <App />
    </Provider>

2️⃣connect(mapStateToProps,mapDispatchToProps)
React-Redux提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来。
mapStateToProps:更新props—>作为输入源。返回一个对象,key为UI界面对应的名称,value为state处理的结果
mapDispatchToProps:更新action—>作为输出源。触发action更新reducer,进而更新state,从而驱动参数变化,引起UI数据的变化

import React, {Component} from 'react'
import {connect} from "react-redux";
 
class App extends Component {
    render() {
        const {value, onIncreaseClick, onReduceClick} = this.props
        return (
            <div>
                <button style={{width: 40, height: 40}} onClick={onIncreaseClick}>+</button>
                <text style={{padding: 40}}>{value}</text>
                <button style={{width: 40, height: 40}} onClick={onReduceClick}>-</button>
            </div>
        );
 
    }
}
 
const mapStateToProps = (state) => {
        return {
            value: state.count
        }
}
const mapDispatchToProps = (dispatch, ownProps) => {
    return {
        onIncreaseClick: () => dispatch({type: 'increase'}),
        onReduceClick: () => dispatch({type: 'reduce'})
    }
}
export default connect(mapStateToProps, mapDispatchToProps)(App);

3、react-router

路由模式:hash/history
路由配置:动态路由 懒加载 Suspense lazy

四、原理

1、函数式编程

纯函数 、不可变值

2、vdom和diff算法

vdom:h函数 、vnode数据结构、 patch函数
diff : 只比较同一层级不跨级比较,tag不同则直接删掉重建,不再深度比较。tag和key两者都相同则认为是相同节点,不再深度比较

3、JSX本质

JSX等同于Vue的模板
JSX的本质是JavaScript的语法扩展,它提供了一种类似于HTML的语法来编写React组件,但实际上这些HTML-like的语法最终会被转换为JavaScript的React.createElement函数调用生成vnode后再通过patch函数进行渲染。

4、合成事件

React16:
在这里插入图片描述

更好的兼容性和跨平台、
挂载到document上(react17后挂载到root上),减少内存消耗,避免频繁解绑
方便事件的统一管理

React17及之后:
事件绑定到root组件上,有利于多个React版本并存,例如微前端
在这里插入图片描述

5、setState和batchUpdate

setState无所谓异步还是同步,关键看是否能命中batchUpdate机制,判断isBatchingUpdate是否为true
哪些能命中batchUpdate机制(异步)?
生命周期、react中注册的事件、react可以管理的入口
哪些不能命中batchUpdate机制(同步)?
setTimeOut setInterval、自定义dom时间、react管理不到的入口
在这里插入图片描述

6、组件渲染过程

jsx及createElement函数执行生成vnode,patch(elem,vnode)和patch(vnode,newVnode)
组件渲染:props state=>render()生成vnode=> patch(elem,vnode)
组件更新:setState(newState)=>dirtyComponents=>render()生成newVnode=> patch(vnode,newVnode)

  • 11
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值