react学习笔记

react 环境搭建

  • npm 已经内置了 react 的脚手架(脚手架:用于搭建环境的工具), node >= 8.10 npm >= 5.6

创建 react 项目

  • npx create-react-app 项目名

组件

  • js 文件就是组件
  • 组件的写法
    • 函数式组件,一个函数返回 html 节点,只能返回一个根节点
  1. class 组件
  2. 函数组件 一个函数返回 html 节点, 只能返回一个节点无 state 组件

jsx 语法

  • 在 js 内写 html,如果要在该 html 写 js 的话需要使用 {}
  • 标签的 class 换成 className
  • 标签的 for 换成 htmlFor

props

  • 需要下载 prop-types 包 npm i prop-types
  • 需要引入 import PropTypes from ‘prop-types’;
  • static propTypes 设置 prop 的规则
static propTypes = {
    // 设置 name 必须为字符型,而且必传
    name: PropTypes.string.isRequired,
}
  • static defaultProps 设置 prop 的默认值
static defaultProps = {
    // 设置 sex 默认是 '男'
    sex: '男'
}

state

  • 调用 let { count } = this.state;
  • 修改
        this.setState({
        count: count - 1,
        });
    

条件渲染

  1. 利用样式显示隐藏 box
<div className={show ? 'box' : 'box none'}></div>
    // 在 jsx 内的 style 属性要写成对象类型
<div style={{display:show ? 'block' : 'none'}} className="box"></div>
  1. dom 出现和消失
    {show ? <div className="box"></div>:''}
    // 或 && 写法
    {show&&<div className="box"></div>}

组件的生命周期

生命周期函数不写成箭头函数也可以使用 this

  • 挂载(首次加载)
    • constructor()
      • 必须有 super()
      • 如果需要 state 也需要写在 constructor 里面
      • 禁止使用 setState
      • props 的值不可以复制给 state
    • static getDerivedstateFromProps()
      • 基本不用
    • render() 组件中必须的方法
      • 纯函数,不可以加上异步操作
    • componentDidMount() 一般使用这个
      • 组件 DOM 渲染完毕,可以发送网络请求,setState 更新数据
  • 更新(组件的 state 和 props 发生改变)
    • shouldComponentUpdate(nextProps,nextState) 接收两个参数,新的 props 和新的 state 组件是否应该可以更新,需要返回一个布尔值,用的不多
    • render() 根据 state 和 props 渲染 react 节点,发生改变重新渲染
    • componentDidUpdata() 组件更新 dom 渲染完毕,可以发送网络请求使用 setState 更新数据,获取更新后的真实 dom 节点
  • 卸载(组件 dom 消失)
    • componentWillUnmount() 组件即将被销毁时触发,清除定时器等

refs 的使用

  1. createRef() 容器
  • 类里面定义一个属性, 属性的值是 createRef()
  • 将这个值绑定到任一元素的 ref 属性上,那么当你访问这个属性下的 current 属性是就拿到了真实 dom
    // 上面写
    p = createRef()
    // dom 节点写
    <p ref={this.p}>我是一个段落</p>
  1. 将真实 dom 赋值给 react 的属性
    • ref 属性的属性值写一个函数,该函数默认接收的第一个参数就是真实 dom 将这个真实 dom 赋值给类的属性即可
        // 上面写 也可以省略
        inputRef = null
        // dom 节点写
        <input type="text" ref={(ele)=>this.inputRef=ele} />
    

虚拟 dom 中 key 的使用

  • 经典面试题:
  1. react/vue中的key有什么作用?(key的内部原理是什么? )
    • 简单的说:key是虚拟DOM对象的标识,在更新显示时key起着极其重要的作用。
    • 详细的说:当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
      1. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
        • 若虚拟DOM中内容没变,直接使用之前的真实DOM
        • 若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
      2. 旧虚拟DOM中未找到与新虚拟DOM相同的key
        • 根据数据创建新的真实DOM,随后渲染到到页面
  2. 为什么遍历列表时,key最好不要用index?
    1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
      会产生没有必要的真实DOM更新==〉界面效果没问题,但效率低。
    2. 如果结构中还包含输入类的DOM:
      会产生错误DOM更新==>界面有问题。
    3. 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

react脚手架配置代理

方法一

在package.json中追加如下配置

"proxy":"http://localhost:5000"

说明:

  1. 优点:配置简单,前端请求资源时可以不加任何前缀。
  2. 缺点:不能配置多个代理。
  3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)
方法二
  1. 第一步:创建代理配置文件

    在src下创建配置文件:src/setupProxy.js
    
  2. 编写setupProxy.js配置具体代理规则:

    const proxy = require('http-proxy-middleware')
    
    module.exports = function(app) {
      app.use(
        proxy('/api1', {  //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
          target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址)
          changeOrigin: true, //控制服务器接收到的请求头中host字段的值
          /*
          	changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
          	changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
          	changeOrigin默认值为false,但我们一般将changeOrigin值设为true
          */
          pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
        }),
        proxy('/api2', { 
          target: 'http://localhost:5001',
          changeOrigin: true,
          pathRewrite: {'^/api2': ''}
        })
      )
    }
    

说明:

  1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
  2. 缺点:配置繁琐,前端请求资源时必须加前缀。

发布订阅模式

PubSub 下载 npm i pubsub-js

  • 最好在 componentDidMount 设置订阅 并且在 componentWillUnmount 取消订阅 PubSub.unsubscribe(this.token)
  • 接收数据端的使用 PubSub.subscribe(‘订阅名’, (msg, stateObj) => {函数体})
  • 发送数据端的使用 PubSub.publish(‘订阅名’,{ 发送的数据 })

Fragment 标签

  • 一个语法糖
  • 空标签不会渲染 也可以简写成 <></>
  • 可循环,可以加 key

strictMode 严格模式

  • 使用 <React.StrictMode></React.StrictMode>包裹可以使用严格模式
  • 有助于
    • 识别不安全的生命周期
    • 关于使用过时字符串ref API的警告
    • 关于使用废弃的findDOMNode方法的警告
    • 检测意外的副作用
    • 检测过时的context API

createPortal

  • 需要 import { createPortal } from ‘react-dom’
  • 可以将组件渲染到 app 以外的地方
render() {
    return createPortal(
        <div>
            <h2>我是对话框</h2>
        </div>,
        document.querySelector('body')
    )
}

Context 共享数据

  • 父组件写
        import { Component, createContext } from 'react';
        // 这里可以导入子组件
        // 导入子组件之后先导出 provide 和 consumer
        // 创建了一个 themeContext 默认值是 red
        export const { Provider, Consumer } = createContext('red')  
        // 父组件传递的数据需要使用 provider 包裹 value 的值就是传递的值,必须传,会覆盖上面的 red ,上面可以不传
        <Provider value='red'>
            <Context1 />
        </Provider>
    
  • 子组件写
    //先从父组件导入 Consumer
        import { Consumer } from './ContextDemo'
        // 使用 父组件的数据的时候 需要使用 consumer 包裹 里面需要是个函数
        <Consumer>
            {
                (x) => (
                    <div>
                        <h4>我是子组件{x}</h4>
                        <Context11 />
                    </div>
                )
            }
        </Consumer>
    
  • 有的时候很深层次的后代组件想要使用祖先组件的 state 但是中间的组件不需要使用,我们可以在祖先组件定义一个变量,这样就可以使用了,就不用层层向下传递了
const getUser=(<User user={user} />)

render props类似作用域插槽

  • 父组件写 {this.props.render(x,y)} 意思为传递 x和y 给子组件
  • <MouseTracker render={(x,y)=>}/> 父组件采用这种方法调用子组件,子组件接收了 x 和 y

react router 路由

  • 安装 npm i react-router-dom

路由的使用

需要先导入 import {BrowserRouter, Route } from ‘react-router-dom’

  1. BrowserRouter 是一个组件,作用是只有在用这个组件包裹的地方才能使用路由相关的技术

    • 一般使用 BrowserRouter 包裹全部的 render
    • 也可以导入 HashRouter,HashRouter 和 BrowserRouter 一样,只是前者是锚点模式,后者是 历史记录模式
  2. Route 是一个组件,该组件的作用是根据传递的地址展示传递的组件,就是一个页面
    1.默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
    2.开启严格匹配:
    3.严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

  3. 嵌套路由
    1.注册子路由时要写上父路由的path值
    2.路由的匹配是按照注册路由的顺序进行的

  4. 添加 Switch 使用 Switch 包裹的组件只展示第一个匹配成功的组件

  5. 添加 Link 实现路由跳转,写法 home

    • 也可以添加 NavLink 和 Link 基本没有区别, NavLink可以实现路由链接的高亮,通过activeClassName指定样式名
  6. 添加 withRouter 让非路由组件可以获取路由数据

    • 使用方法,在导出组件时用 withRouter 包裹一下 withRouter(App);
    • 但是这个组件需要 BrowserRouter 包裹
  7. Redirect的使用

    1. 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由
    2. 添加 Redirect 路由重定向

路由组件与一般组件

1.写法不同:
            一般组件:<Demo/>
            路由组件:<Route path="/demo" component={Demo}/>
2.存放位置不同:
            一般组件:components
            路由组件:pages
3.接收到的props不同:
            一般组件:写组件标签时传递了什么,就能收到什么
            路由组件:接收到三个固定的属性
                                history:
                                            go: ƒ go(n)
                                            goBack: ƒ goBack()
                                            goForward: ƒ goForward()
                                            push: ƒ push(path, state)
                                            replace: ƒ replace(path, state)
                                location:
                                            pathname: "/about"
                                            search: ""
                                            state: undefined
                                match:
                                            params: {}
                                            path: "/about"
                                            url: "/about"

解决多级路径刷新页面样式丢失的问题

1.public/index.html 中 引入样式时不写 ./ 写 / (常用)
2.public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用)
3.使用HashRouter

向路由组件传递参数

    1.params参数
                路由链接(携带参数):<Link to='/demo/test/tom/18'}>详情</Link>
                注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/>
                接收参数:this.props.match.params
    2.search参数
                路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'}>详情</Link>
                注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>
                接收参数:this.props.location.search
                备注:获取到的search是urlencoded编码字符串,需要借助querystring解析
    3.state参数
                路由链接(携带参数):<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>
                注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>
                接收参数:this.props.location.state
                备注:刷新也可以保留住参数

无痕模式

- 路由模式有 push 模式和 replace 模式,默认是 push 模式,也就是路由跳转会有记录
- 如果在跳转也就是 Link 标签添加 replace 就会变成无痕模式,也就不会产生记录

编程式路由导航

  • 借助this.prosp.history对象上的API对操作路由跳转、前进、后退
    • this.prosp.history.push()
    • this.prosp.history.replace()
      • 如果传递参照,可以传递 params search state 参数,接收和获取对应写就行
    • this.prosp.history.goBack()
    • this.prosp.history.goForward()
    • this.prosp.history.go(n)
      • n 为几就前进或者后退几步,正为前进负为后退

BrowserRouter与HashRouter的区别

		1.底层原理不一样:
					BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
					HashRouter使用的是URL的哈希值。
		2.path表现形式不一样
					BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
					HashRouter的路径包含#,例如:localhost:3000/#/demo/test
		3.刷新后对路由state参数的影响
					(1).BrowserRouter没有任何影响,因为state保存在history对象中。
					(2).HashRouter刷新后会导致路由state参数的丢失!!!
		4.备注:HashRouter可以用于解决一些路径错误相关的问题。

Ant Design

npm i antd --save

使用

  • 使用时需要引入 import { Button } from 'antd'import 'antd/dist/antd.css'
  • 如果使用图标需要 import {想要使用的图标} from '@ant-design/icons'

antd的按需引入+自定主题

  1. 安装依赖:npm i react-app-rewired customize-cra babel-plugin-import less less-loader@7.1.0
  2. 修改package.json

    “scripts”: {
    “start”: “react-app-rewired start”,
    “build”: “react-app-rewired build”,
    “test”: “react-app-rewired test”,
    “eject”: “react-scripts eject”
    },
  3. 根目录下创建config-overrides.js
    // 配置具体的修改规则
    const { override, fixBabelImports,addLessLoader} = require(‘customize-cra’);
    module.exports = override(
    fixBabelImports(‘import’, {
    libraryName: ‘antd’,
    libraryDirectory: ‘es’,
    style: true,
    }),
    addLessLoader({
    lessOptions:{
    javascriptEnabled: true,
    modifyVars: { ‘@primary-color’: ‘green’ },
    }
    }),
    );
  4. 备注:不用在组件里亲自引入样式了,即:import 'antd/dist/antd.css’应该删掉

自定义 icon

  • 利用阿里图标制作一个自定义的 icon
  • 先引入 import Icon from ‘@ant-design/icons’
  • 定义一个函数 const iosIcon = () => (svg图片代码)
  • 在定义一个函数 const IsoIcon = (props) => <Icon component={iosIcon} {…props} />
  • 直接在后面引就可以了

压缩 avg

  • 全局安装 npm i -g svgo
  • svgo 图片名

Redux

安装 npm i redux

一些 API

  1. store.getState() 获取 store 的状态
  2. store.dispatch({ type: ‘decrement’, data: value * 1 }) 执行 redux 的函数
  3. store.subscribe(()=>{函数体}) 当 store 数据改变时执行回调

使用

  1. .src下建立:
    -redux
    -store.js
    -count_reducer.js 加工数据
    -count_action.js 专门用于创建action对象
    -constant.js 放置容易写错的type值

  2. store.js:

    • 引入redux中的createStore函数,创建一个store
    • createStore调用时要传入一个为其服务的reducer
    • 记得暴露store对象
        import { createStore } from "redux";
    
        import countReducer from './count_reducer'
    
        export default createStore(countReducer)
    
  3. count_reducer.js:

    • reducer的本质是一个函数,接收:preState(上次的 state 默认是 undefined 一般需要给一个默认值), action,返回加工后的状态
    • reducer有两个作用:初始化状态,加工状态
    import {INCREMENT,DECREMENT} from './constant'
    
    const initState = 0 //初始化状态
    export default function countReducer(preState=initState,action){
        //从action对象中获取:type、data
        const {type,data} = action
        //根据type决定如何加工数据
        switch (type) {
            case INCREMENT: //如果是加
                return preState + data
            case DECREMENT: //若果是减
                return preState - data
            default:
                return preState
        }
    }
    
  4. count_action.js

    • 该文件专门为Count组件生成action对象
    import {INCREMENT,DECREMENT} from './constant'
    
    export const createIncrementAction = data => ({type:INCREMENT,data})
    export const createDecrementAction = data => ({type:DECREMENT,data})
    
  5. constant.js

    • 该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
    • 这样引入之后就会有提示,就不容易写错了
    export const INCREMENT = 'increment'
    export const DECREMENT = 'decrement'
    
  6. 在index.js中监测store中状态的改变,一旦发生改变重新渲染,原来渲染 App 的 render 不要删除

        ReactDOM.render(<App />,document.getElementById('root'));
    
        store.subscribe(()=>{
            ReactDOM.render(<App />,document.getElementById('root'));
        })
    
     备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。
    
  • 真正在项目中调用 redux 使用 store.dispatch(createIncrementAction(value)) 就可以了
异步 action
  1. 明确:延迟的动作不想交给组件自身,想交给action
  2. 何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。
  3. 具体编码:
    1. npm i redux-thunk,并配置在store中
          import { createStore, applyMiddleware } from "redux";
      
          import countReducer from './count_reducer'
      
          import thunk from "redux-thunk";
      
          export default createStore(countReducer,applyMiddleware(thunk))
      
    2. 创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
    //异步action中一般都会调用同步action,异步action不是必须要用的。
        export const increamentAsyncAction = (data,time) =>{
            // dispatch 是从 store 中得到的,因为是 store 调用的所以可以直接获取
            return (dispatch)=>{
                setTimeout(() => {
                    dispatch(increamentAction(data))
                }, time);
            }
        }
    
    1. 异步任务有结果后,分发一个同步的action去真正操作数据。
  4. 备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。

react-redux基本使用

安装 npm i react-redux

  • 使用 react-redux 之后就可以将 index.js 中 subscribe 监测语句删除了,因为 react-redux 自己就可以监测了
  1. 明确两个概念:
    1. UI组件:不能使用任何redux的api,只负责页面的呈现、交互等。
    2. 容器组件:负责和redux通信,将结果交给UI组件。
  2. 如何创建一个容器组件————靠 react-redux 的 connect 函数
    connect(mapStateToProps,mapDispatchToProps)(UI组件)
    -mapStateToProps:映射状态,返回值是一个对象
    -mapDispatchToProps:映射操作状态的方法,返回值是一个对象
  3. 备注1:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
  4. 备注2:mapDispatchToProps,也可以是一个对象
    import { connect } from 'react-redux'

    import countUi from '../../components/Count'

    import {increamentAction,decreamentAction,increamentAsyncAction} from '../../redux/count_action'

    function mapStateToProps(state) {
        return { count: state }
    }
    function mapDispatchToProps(dispatch) {
        return {
            increment: (data) => {
                dispatch(increamentAction(data))
            },
            decreament: (data) => {
                dispatch(decreamentAction(data))
            },
            increamentAsync: (data,time) => {
                dispatch(increamentAsyncAction(data,time))
            },
        }
    }
    export default connect(mapStateToProps, mapDispatchToProps)(countUi)

简化代码

import { connect } from 'react-redux'

import countUi from '../../components/Count'

import { increamentAction, decreamentAction, increamentAsyncAction } from '../../redux/count_action'

export default connect(
    state => ({ count: state }),
    dispatch => ({
        increment: (data) => {
            dispatch(increamentAction(data))
        },
        decreament: (data) => {
            dispatch(decreamentAction(data))
        },
        increamentAsync: (data, time) => {
            dispatch(increamentAsyncAction(data, time))
        },
    })
)(countUi)
//其中的 第二个参数可以在次简写为
    {
        increment: increamentAction,
        decreament: decreamentAction,
        increamentAsync: increamentAsyncAction
    }

react-redux优化

  1. 容器组件和UI组件整合一个文件
  2. 无需自己给容器组件传递 store,给包裹一个即可。
    
    import store from './redux/store';
    import { Provider } from 'react-redux'
    
    ReactDOM.render(
    <Provider store={store}>
    <App />
    </Provider>
    ,document.getElementById('root'));
    
    
  3. 使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。
  4. mapDispatchToProps也可以简单的写成一个对象
  5. 一个组件要和redux“打交道”要经过哪几步?
    1. 定义好UI组件—不暴露
    2. 引入connect生成一个容器组件,并暴露,写法如下:
      connect(
      state => ({key:value}), //映射状态
      {key:xxxxxAction} //映射操作状态的方法
      )(UI组件)
    3. 在UI组件中通过this.props.xxxxxxx读取和操作状态
组件中使用
  • this.props.count 获取 count 的值
  • this.props.increment 使用 increment 函数

react-redux 共享多个数据

  1. 定义两个组件通过redux共享数据。
  2. 重点:两个组件的 reducer 要使用combineReducers进行合并,
    合并后的总状态是一个对象!!!
  3. 交给 store 的是总 reducer ,最后注意在组件中取出状态的时候,记得“取到位”。

react-redux开发者工具的使用

  1. npm i redux-devtools-extension
  2. store中进行配置
    import {composeWithDevTools} from ‘redux-devtools-extension’
    const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

扩展

1.setState更新状态的2种写法

setState 函数是同步的,但是后期引起 react 状态更新是异步的

	(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函数中读取

2. lazyLoad

路由组件的lazyLoad

	import { lazy,Suspense } from 'react';
	//1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
	const Login = lazy(()=>import('@/pages/Login'))
	
	//2.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面
	<Suspense fallback={<h1>loading.....</h1>}>
        <Switch>
            <Route path="/xxx" component={Xxxx}/>
            <Redirect to="/login"/>
        </Switch>
    </Suspense>

3. Hooks

1. React Hook/Hooks是什么?
(1). Hook是React 16.8.0版本增加的新特性/新语法
(2). 可以让你在函数组件中使用 state 以及其他的 React 特性
2. 三个常用的Hook
(1). State Hook: React.useState()
(2). Effect Hook: React.useEffect()
(3). Ref Hook: React.useRef()
3. State Hook
(1). State Hook让函数组件也可以有state 状态, 并进行状态数据的读写操作
(2). 语法: const [xxx, setXxx] = React.useState(initValue)  
(3). useState()说明:
        参数: 第一次初始化指定的值在内部作缓存
        返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
(4). setXxx()2种写法:
        setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
        setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
4. Effect Hook
(1). Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
(2). React中的副作用操作:
        发ajax请求数据获取
        设置订阅 / 启动定时器
        手动更改真实DOM
(3). 语法和说明: 
        useEffect(() => { 
          // 在此可以执行任何带副作用操作
          return () => { // 在组件卸载前执行
            // 在此做一些收尾工作, 比如清除定时器/取消订阅等
          }
        }, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
    
(4). 可以把 useEffect Hook 看做如下三个函数的组合
        componentDidMount()
        componentDidUpdate()
    	componentWillUnmount() 
5. Ref Hook
(1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
(2). 语法: const refContainer = useRef()
(3). 作用:保存标签对象,功能与React.createRef()一样

4. Fragment

可以不用必须有一个真实的DOM根标签了

使用

<Fragment><Fragment>
<></>

区别

  • Fragment 可以写属性,空标签不可以

5. Context

理解

一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信

使用

1) 创建Context容器对象:
	const XxxContext = React.createContext()  
	
2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
	<xxxContext.Provider value={数据}>
		子组件
    </xxxContext.Provider>
    
3) 后代组件读取数据:

	//第一种方式:仅适用于类组件 
	  static contextType = xxxContext  // 声明接收context
	  this.context // 读取context中的value数据
	  
	//第二种方式: 函数组件与类组件都可以
	  <xxxContext.Consumer>
	    {
	      value => ( // value就是context中的value数据
	        要显示的内容
	      )
	    }
	  </xxxContext.Consumer>

注意

在应用开发中一般不用context, 一般都用它的封装react插件

6. 组件优化

Component的2个问题

  1. 只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低

  2. 只当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低

效率高的做法

只有当组件的state或props数据发生改变时才重新render()

原因

Component中的shouldComponentUpdate()总是返回true

解决

办法1: 
	重写shouldComponentUpdate()方法
	比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false
办法2:  
	使用PureComponent
	PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
	注意: 
		只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false  
		不要直接修改state数据, 而是要产生新数据
项目中一般使用PureComponent来优化

7. 打包上线

  1. npm run build 进行打包
  2. npm i serve -g 安装工具
  3. serve build 运行

8. 错误边界

理解:

错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面

特点:

只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误

使用方式:

getDerivedStateFromError配合componentDidCatch

state = {hasError: ''}
// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
    console.log(error);
    // 在render之前触发
    // 返回新的state
    return {
        hasError: true,
    };
}

componentDidCatch(error, info) {
    // 统计页面的错误。发送请求发送到后台去
    console.log(error, info);
}

9. 组件通信方式总结

组件间的关系:
  • 父子组件
  • 兄弟组件(非嵌套组件)
  • 祖孙组件(跨级组件)
几种通信方式:
	1.props:
		(1).children props
		(2).render props
	2.消息订阅-发布:
		pubs-sub
	3.集中式管理:
		redux
	4.conText:
		生产者-消费者模式

新电脑 新系统 开始 vue 项目

  1. 第一种情况 给你一个 git 仓库地址
    • 安装 git 下载 gitbash 安装即可
    • 安装 node 官网安装即可
    • 安装 vscode 下载插件
  2. 第二个情况 给一个 demo 从 0 开始
    • 安装 node
    • 安装脚手架工具
    • 命令行工具 gitbash
    • 安装 vscode
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值