飞道的博客

React 学习笔记总结(七)

424人阅读  评论(0)

针对React拓展相关的学习。

一. React 项目打包

安装serve,该库的作用可以将一个静态页面作为一台服务器启动。方便测试打包后的html页面。

# 安装serve
npm i serve
# 执行serve,在对应打包后的目录下,执行serve命令
serve 

二. React扩展 之 setState两种写法

第一种写法:就是传对象。

setState方法的两个参数:

import React, {
   Component} from 'react';

export default class Demo extends Component {
   

    state = {
   count:0}

    add = () => {
   
        const {
   count} = this.state
        // 更新状态 , 第二个参数callback是回调函数
        this.setState({
   count:count+1},() => {
   
            // fixme state状态改完,render页面刷新完,才调用该回调函数
        })
        // 因为,setState所调用的形式是异步的。
        console.log('此时state中的count值并没有变化:',this.state.count)
    }

    render() {
   
        return (
            <div>
                <h1>当前求和为:{
   this.state.count}</h1>
                <button onClick={
   this.add}>1</button>
            </div>
        );
    }
}

 

第二种写法:传递函数。

  • 函数式setState: 好处拿到了state和props。也方便维护。
import React, {
   Component} from 'react';

export default class Demo extends Component {
   

    state = {
   count:0}

    add = () => {
   
        const {
   count} = this.state
        // 函数式setState: 好处拿到了state和props
        this.setState((state,props) => {
   
            console.log('state',state)
            console.log('props',props)
            return {
   
                count:count+1
            }
        })
        console.log('此时state中的count值并没有变化:',this.state.count)
    }

    render() {
   
        return (
            <div>
                <h1>当前求和为:{
   this.state.count}</h1>
                <button onClick={
   this.add}>1</button>
            </div>
        );
    }
}

 

使用原则:

  • 如果新状态不依赖于原来状态。 推荐使用对象方式。
  • 如果新状态依赖与原来状态。 推荐使用函数方式。(例如:上面自动+1效果)。

注意:如果需要在setState()执行后获取最新的状态数据,要在第二个callback函数中进行读取操作取。

三. React扩展 之 lazyLoad(懒加载)

一般项目特别大,组件特别多,都会用到懒加载这个东西。

例如:一个页面涉及到了20多个路由页面,但是用户仅仅用了3个,然而,加载的时候缺加载了20多个路由页面,这样就不太好。

所以,就要用懒加载。

案例如下:

  • 通过使用lazy和Suspense来进行操作。
// fixme 1. 引入lazy,Suspense
import React, {
   Component,lazy,Suspense} from 'react';
import {
    NavLink, Route} from 'react-router-dom'

// fixme 2. 路由组件不要用引入的方式
// import About from '../2_lazyLoad/About'
// import Home from '../2_lazyLoad/Home'

// fixme 注意: fallback中的内容组件必须是就位的,不能又使用import()函数,所以最好是提前引入
import Load from '../components/Load/Load'

// fixme 3. 定义成函数并且使用import()函数操作
const Home = lazy(() => {
   
    // 使用import()函数来引入
    return import('../2_lazyLoad/Home')
})
const About = lazy(() => {
   
    // 使用import()函数来引入
    return import('../2_lazyLoad/About')
})

class Demo extends Component {
   
    render() {
   
        return (
            <div style={
   {
   textAlign:"center"}}>
                <div style={
   {
   textAlign:"center"}}>
                    <h1>React Router Demo</h1>
                </div>
                <div style={
   {
   textAlign:"center"}}>
                    {
   /*{React靠路由跳转}*/}
                    <NavLink to="/about">
                        About
                    </NavLink>
                    <br/>
                    <NavLink to="/home">
                        Home
                    </NavLink>
                </div>
                <div style={
   {
   textAlign:"center"}}>
                    {
   /* fixme 4. 使用Suspense来包裹Route,fallback显示的是加载完成前的操作。注意:fallback中的内容组件必须是就位的,不能又使用import()函数 */}
                    <Suspense fallback={
   <Load/>}>
                        {
   /* 注册路由: 注意route和Router区分 */}
                        <Route path="/about" component={
   About}/>
                        <Route path="/home" component={
   Home}/>
                    </Suspense>
                </div>
            </div>
        );
    }
}

export default Demo;

 

四、React的 stateHook

Hook是React 16.8.0版本新增加的一个特性。

可以让函数组件中使用state以及其他的React属性(例如:生命周期钩子函数之类的。)。

三个常用的Hook:

  • State Hook: React.useState()
  • Effect Hook:React.useEffect()
  • Ref Hook:React.useRef()

State Hook的使用:

import React from 'react';

/**
 * 函数式组件
 */
function Index(){
   

    console.log('组件调用次数是 n + 1 次,第一次进入页面1次,之后render渲染n次。')

    /**
     * React.useState(0): 第一个参数代表初始值
     * 返回值是个数组:第一个是状态state值,第二个是函数(负责更新状态state值)。
     */
    const [count,setCount] = React.useState(0)
    const [name,setName] = React.useState("Tom")

    function add(){
   
        console.log('+++')
        // setCount第一种写法:(值写法)
        setCount(count + 1)
        // setCount第二种写法:(函数写法) 可以更好维护
        setCount(preCount => (preCount + 1))
    }

    function update(){
   
        setName('Jerry')
    }

    return (
        <div>
            <h2>当前求和为:{
   count}</h2>
            <h2>我的名字是:{
   name}</h2>
            <button onClick={
   add}>点我 + 1</button>
            <button onClick={
   update}>修改名字</button>
        </div>
    )
}

export default Index;

 

五、React 的 Effect Hook

Effect Hook的使用:

import React from 'react';
import ReactDOM from 'react-dom'

/**
 * 函数式组件
 */
function Index(){
   

    const [count,setCount] = React.useState(0)

    /**
     * React.useEffect函数作用:就是实现了类函数中的componentDidMount之类的钩子函数了。
     *  但是用法很不同: 第一个参数(函数):是要执行的函数。 第二个参数(数组):是要监听哪个状态值。
     */
    React.useEffect(()=>{
   
        let timer = setInterval(()=>{
   
            setCount(count => count + 1)
        },1000)
        // fixme 此处的返回值就是等于componentWillUnmount
        return () => {
   
            console.log('@@@')
            clearInterval(timer)
        }
    },[count])

    /**
     * 还有一种情况,像定时器之类的,在离开页面前需要关闭定时器。
     *  一般对象组件就直接在使用componentWillUnmount组件就可以了。
     *
     */
    function  unmount(){
   
        // 通过使用ReactDOM.unmountComponentAtNode来卸载组件
        ReactDOM.unmountComponentAtNode(document.getElementById('root'))
    }

    function add(){
   
        setCount(preCount => (preCount + 1))
    }

    return (
        <div>
            <h2>当前求和为:{
   count}</h2>
            <button onClick={
   add}>点我 + 1</button>
            <button onClick={
   unmount}>卸载Root</button>
        </div>
    )

}

export default Index;

 

可以把 useEffect Hook 看做对象组件如下三个钩子函数的组合:

  • componentDidMount()
  • componentDidUpdate()
  • componentWillUnmount()
语法和说明: 
useEffect(() => {
    
  // 在此可以执行任何带副作用操作
  return () => {
    // 在组件卸载前执行
    // 在此做一些收尾工作, 比如清除定时器/取消订阅等
  }
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行

六、React 的 RefHook

类式组件使用ref:

/**
 * 类式组件:
 */
class Index extends React.Component {
   

    state = {
   count:0}

    // fixme 创建一个ref
    myRef = React.createRef()

    alertRef = () => {
   
        alert(this.myRef.current.value)
    }

    add = ()=> {
   
        this.setState(state => ({
   count:state.count + 1}))
    }

    render() {
   
        return (
            <div>
                <input type="text" ref={
   this.myRef}/>
                <h2>当前求和为:{
   this.state.count}</h2>
                <button onClick={
   this.add}>点我 + 1</button>
                <button onClick={
   this.alertRef}>提示</button>
            </div>
        );
    }
}

 

RefHook的使用:

(1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
(2). 语法: const refContainer = useRef()
(3). 作用:保存标签对象,功能与React.createRef()一样

七、React 的 Fragment

Frgament用法:

import React, {
   Component,Fragment} from 'react';

class Index extends Component {
   
    render() {
   
        return (
            // 这样就不用写一个div了。
            <Fragment>
                <input type="text"/>
            </Fragment>
        );
    }
}

export default Index;

空标签的用法:

import React, {
   Component,Fragment} from 'react';

class Index extends Component {
   
    render() {
   
        return (
            // fixme 也可以使用一个空标签
            <>
                <input type="text"/>
            </>
        );
    }
}

export default Index;

Fragment 与 空标签 区别:

  • 作用相同,但是Fragment可以有一个key属性,作为唯一标识使用(只有key属性)。

八、React 的 Context

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

考虑两种情况:

  • 对象式组件 和 函数式组件,不过一般项目中使用了redux就不会使用该方式了。
1) 创建Context容器对象:
	const XxxContext = React.createContext()  
	
2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
	<xxxContext.Provider value={
   数据}>
		子组件
    </xxxContext.Provider>
    
3) 后代组件读取数据:

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

 

九、React 的 PureComponent

1. Component组件的两个问题

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

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

上面就会导致效率降低的情况。

导致上面情况的发生,因为,Component中的shouldComponentUpdate()钩子函数总是返回true。

2. PureComponent 纯组件

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

注意:不要直接修改state数据,直接修改state虽然属性变了,但自身对象地址未发生变化(也就是个浅赋值),而是要产生新数据,不然PureComponent无法检测到。

还是要注意纯组件失效不更新的情况。

十、React 的 render props

1. 组成父子组件的 两种方式

第一种方式:

第二种方式:

注意:第二种方式有瑕疵,那么\<B name={this.state}/\>的this指向的是谁?此处便是缺点。

2. render props 的 使用

// 比较Vue 和 React:
Vue中: 
	使用slot技术, 也就是通过组件标签体传入结构  <A><B/></A>
React中:
	使用children props: 通过组件标签体传入结构
	使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性

// 其实render prop就是为了解决如下情况:
<A>
  <B>xxxx</B>
</A>
{
   this.props.children}
问题: 如果B组件需要A组件内的数据, ==> 做不到 

十一、React 的 ErrorBoundary

错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面,实际上就是有一个出错后能显示的页面。

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

通过getDerivedStateFromError配合componentDidCatch来实现:

class Index extends Component {
   

    state = {
   
        hashError:'' // 用于标识子组件是否产生错误
    }

	// 生命周期函数,一旦后台组件报错,就会触发
	static getDerivedStateFromError(error) {
   
	    console.log(error);
	    // 在render之前触发
	    // 返回新的state
	    return {
   
	        hasError: true,
	    };
	}
	// 将错误信息返回后台(需要的话。)
	componentDidCatch(error, info) {
   
	    // 统计页面的错误。发送请求发送到后台去
	    console.log(error, info);
	}

    render() {
   
        return (
            <div>
                <h3>组件</h3>
                {
   this.state.hashError ? <h2>当前不稳定,请稍后重试!</h2> : <Child/>}
            </div>
        );
    }
}

 

十二、React 组件通信方式总结


转载:https://blog.csdn.net/IT_Holmes/article/details/128596452
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场