Part4-React Hooks

预习视频

【最新】零基础快速入门React 17.x

视频P56-P73

今日学习目标

1、掌握函数式组件

2、掌握Hooks的写法

3、掌握Fragment

4、了解错误边界

5、掌握PureComponent

6、了解HOC高阶组件

7、掌握LazyLoad与Suspense、fallback

一、React Hooks介绍【了解】

官网地址:https://react.docschina.org/docs/hooks-intro.html

二、函数式组件【掌握】

纯函数组件有以下特点:

  • 没有状态

  • 没有生命周期

  • 没有 this

因存在如上特点,使得纯函数组件只能做UI展示的功能, 涉及到状态的管理与切换就不得不用到类组件或者redux。 但因为简单的页面也是用类组件,同时要继承一个React实例,使得代码会显得很重。

以前我们可以使用class来声明一个组件,其实使用function也可以定义一个组件:

创建 App1.js

import React from 'react'

function App1(){
    return (
        <div>
            <h1>function 声明的App</h1>
        </div>
    )
}

export default App1;

备注:

在vscode中,如果安装过 ES7 React/Redux/GraphQL/React-Native snippets 这个插件,即可直接使用 rfc 快捷键敲出以下模板:

import React from 'react';

const App = () => {
    return (
        <div>
            
        </div>
    );
}

export default App;

index.js 中调用:

import ReactDOM from 'react-dom'
import App from './App1'

ReactDOM.render(
    <App />,
    document.getElementById('root')
)

效果如下:

三、常用Hooks【掌握】

按照我们之前的类声明编程,写一个累加器如下:

import React, { Component } from 'react'

export default class App extends Component {
    constructor(p){
        super(p)
        this.state = {num: 0}
    }
    render() {
        return (
            <div>
                <h2>{this.state.num}</h2>
                <button onClick={this.addNum.bind(this)}>累加</button>
            </div>
        )
    }
    addNum(){
        this.setState({
            num: this.state.num+1
        })
    }
}

效果如下:

1、useState

现在我们改成函数式编程:

// useState就是hooks提供的一个api
import React, { useState } from 'react'

function App(){
  	// 这里useState(0)中的0,就是定义num的初始值
    const [num, setNum] = useState(0);

    return (
        <div>
            <h2>{num}</h2>
            <button onClick={()=>{setNum(num+1)}}>累加</button>
        </div>
    )
}

export default App;

累加效果一样可以实现。

2、Hooks开发注意点【注意】

Hooks的定义不能放在条件判断的语句中,如:

if(true){
	const [num, setNum] = useState(0);
}

这种写法会报错:

这里注意:

Hooks的API只能在函数内的最外层声明。

3、useEffect

React函数式编程没有生命周期,因此需要借助useEffect来实现。

* componentDidMount与componentDidUpdate

如果使用class编程,我们检测数据变化就需要这么做:

import React, { Component } from 'react'

export default class App extends Component {
    constructor(p){
        super(p)
        this.state = {num: 0}
    }
    render() {
        return (
            <div>
                <h2>{this.state.num}</h2>
                <button onClick={this.addNum.bind(this)}>累加</button>
            </div>
        )
    }
    componentDidMount(){
        console.log('Mount')
    }
    componentDidUpdate(){
        console.log('Update')
    }
    addNum(){
        this.setState({
            num: this.state.num+1
        })
    }
}

结合componentDidMount与componentDidUpdate两个生命周期函数来检测。

我们来看看function编程与Hooks怎么解决这一问题:

import React, { useState, useEffect } from 'react'

function App1(){
    const [num, setNum] = useState(0);
    
    useEffect(()=>{
        console.log('useEffect')
    })
    
    return (
        <div>
            <h2>{num}</h2>
            <button onClick={()=>{setNum(num+1)}}>累加</button>
        </div>
    )
}

export default App1;

一个useEffect就可以替代两个生命周期函数:

这里注意:

useEffect是异步的,不会阻断试图更新。

如果有两个字段都在页面中有更新,但只想指定其中某个来检测,那么可以给 useEffect 添加第二个参数,第二个参数是个数组,数组为空时,表示不检测,数组项为某个字段名称时,表示只检测指定字段的更新:

import {useState, useEffect} from 'react'

function App1(){
  const [num, setNum] = useState(0)
  const [num1, setNum1] = useState(1)

  useEffect(()=>{
    console.log('视图更新了')
  }, [num])		// 这里只检测num更新,num1的更新不会检测到

  return (
    <div>
      <h1>{num}</h1>
      <button onClick={()=>setNum(num+1)}>累加</button>
      <h1>{num1}</h1>
      <button onClick={()=>setNum1(num1+1)}>累加</button>
    </div>
  )
}

export default App1;

* componentWillUnmount

当我们需要在组件销毁时做一些清除工作,就需要使用componentWillUnmount。但函数式编程没有这个生命周期,因此也同样需要借助useEffect实现。

由于这里需要做页面的销毁,在 index.js 中最后添加:

setTimeout(()=>{
    ReactDOM.render(
        <p>你好</p>,
        document.getElementById('root')
    )
}, 3000)

创建 App2.js :

import {useState, useEffect} from 'react'

export default function App2() {
    let [num, setNum] = useState(0);
    let [num1, setNum1] = useState(0);

  	// 这个hook检测num更新
    useEffect(()=>{
        console.log('渲染了num');
    }, [num])	// [num] 代表这个useEffect只检测num这个字段的更新

  	// 这个hook检测离开组件
    useEffect(()=>{
        return ()=>{
            console.log('离开了App1组件')
        }
    },[])		// [] 代表这个useEffect不检测任何一个字段更新

    return (
        <div>
            <h2>num:{num}</h2>
            <button onClick={() => setNum(num+1)}>累加</button>
            <hr />
            <h2>num1:{num1}</h2>
            <button onClick={() => setNum1(num1+1)}>累加</button>
        </div>
    )
}

⚠️ 这里注意:

同个useEffect下,在检测销毁和检测字段更新之间,只能二选一。留下空数组,可以检测到return中的销毁。数组非空时,视图更新会带动return返回值,因此如果要检测字段更新,就无法检测销毁。

4、useContext

函数式的组件,如果需要父传子,那么就需要使用useContext。

* 用法一:

useContext有两种用法,第一种是使用useContext来调用上下文内容。代码如下:

/* 
    createContext用于创建上下文
    useContext用于调用上下文
*/
import {useState, createContext, useContext} from 'react'

// 1、创建上下文
const NumContext = createContext();

// 子组件
function Count(){
    // 3、调用上下文内容
    const num = useContext(NumContext)
    return (
        <h3>{num}</h3>
    )
}

function App3(){
    const [num, setNum] = useState(0)
    return (
        <>
            {/* 2、设置提供器,并传入value,包含子组件 */}
            <NumContext.Provider value={num}>
                <Count />
            </NumContext.Provider>
            <button onClick={()=>{setNum(num+1)}}>累加</button>
        </>
    )
}

export default App3;

* 用法二:

使用 Consumer 来接收数据,这种方法还可以使用 Chrome 扩展 react-context-devtool3.2.crx 来观测数据。

首先,将 react-context-devtool3.2.crx 直接拽入 Chrome 的扩展中。

扩展下载链接: https://pan.baidu.com/s/1ejGlj6_Sierd6w6GQ74mYw 提取码: 1wv6

其次,安装 react-context-devtool

npm install react-context-devtool

# or 
yarn add react-context-devtool

然后在项目入口文件 src>index.js 中:

// 将原本代码改写为:
import ReactDOM from 'react-dom'
import App from './App5'
// 引入react-context-devtool
import { debugContextDevtool } from 'react-context-devtool';

let container = document.getElementById('root')

ReactDOM.render(
    <App />,
    container
)

debugContextDevtool(container);

这里注意:

debugContextDevtool方法有第二个参数,是用来传入配置项的。主要配置项有:

NameTypeDefaultDescription

debugReducer

boolean

true

enable/disable useReducer debug

debugContext

boolean

true

enable/disable context debug

disable

boolean

false

disable react-context-devtool including manual mode

disableAutoMode

boolean

false

disable auto mode only

但我们直接忽略不填就行了。具体参考网址:

Debug ReactJS Context and useReducer hook with React Context Devtool

引入组件并在Provider使用:

import { ContextDevTool } from 'react-context-devtool';

<NumContext.Provider value={num}>
  <ContextDevTool context={NumContext} />
  <Sub />
</NumContext.Provider>

然后在子组件中修改:

const Sub = () => {
    return (
        <NumContext.Consumer>
            {
                num => {
                    if (window._REACT_CONTEXT_DEVTOOL) {
                      window._REACT_CONTEXT_DEVTOOL({ num });
                    }
                    return <h1>{num}</h1>;
                  }
            }
        </NumContext.Consumer>
    )
}

* 最终代码:

重新修改 App3.jsx

import React, {useState, useContext, createContext} from 'react';
import { ContextDevTool } from 'react-context-devtool';

// 创建上下文
const NumContext = createContext()

const Sub = () => {
    // const num = useContext(NumContext)
    return (
        <NumContext.Consumer>
            {
                num => {
                    if (window._REACT_CONTEXT_DEVTOOL) {
                      window._REACT_CONTEXT_DEVTOOL({ id: 'uniqContextId', displayName: 'Context Display Name', num });
                    }
                    return <h1>{num}</h1>;
                  }
            }
        </NumContext.Consumer>
    )
}

const App5 = () => {
    const [num, setNum] = useState(0);
    return (
        <div>
            <NumContext.Provider value={num}>
                <ContextDevTool context={NumContext} id="uniqContextId" displayName="Context Display Name" />
                <Sub />
            </NumContext.Provider>
            <button onClick={()=>setNum(num+1)}>累加</button>
        </div>
    );
}

export default App5;

5、useReducer

useReducer与useContext不太一样,有点类似于Redux。

import {useReducer} from 'react'

// 1、定义一个reducer
function numReducer(state, action){
    // 3、进行一步深拷贝,因为state是不允许直接修改的
    let newState = JSON.parse(JSON.stringify(state));
    switch(action.type){
        case 'add':
            newState.num++;
            return newState;
        case 'cut':
            newState.num--;
            return newState;
        default:
            return newState;
    }
}

function App4(){
    // 2、使用reducer并解构出state与dispatch, {num:0}表示state的默认值
    const [state, dispatch] = useReducer(numReducer, {num: 0});

    return (
        <>
            <h2>{state.num}</h2>
            {/* 使用dispatch调用 */}
            <button onClick={()=>{dispatch({type: 'add'})}}>累加</button>
            <button onClick={()=>{dispatch({type: 'cut'})}}>减少</button>
        </>
    )
}

export default App4;

6、useContext结合useReducer实现redux

* 第一步

将以上 1、定义一个reducer 这一步的函数抽离到 store>reducer.js 中:

// 1、定义一个reducer
export const numReducer = function(state, action){
    // 3、进行一步深拷贝,因为state是不允许直接修改的
    let newState = JSON.parse(JSON.stringify(state));
    switch(action.type){
        case 'add':
            newState.num++;
            return newState;
        case 'cut':
            newState.num--;
            return newState;
        default:
            return newState;
    }
}

* 第二步

引入reducer,并引入useContext与createContext

import {useReducer, useContext, createContext} from 'react'
import {numReducer} from './store/reducer'

// 创建上下文
const NumContext = createContext()

* 第三步

定义Sub1和Sub2子组件,Sub1用于累加,Sub2用于减少。

// 定义Sub1子组件
function Sub1(){
    const dispatch = useContext(NumContext)		// 从提供器中传过来
    return <button onClick={()=>{dispatch({type: 'add'})}}>累加</button>
}

// 定义Sub2子组件
function Sub2(){
    const dispatch = useContext(NumContext)
    return <button onClick={()=>{dispatch({type: 'cut'})}}>减少</button>
}

* 第四步

在App4组件中调用Sub1和Sub2,并使用Provider:

function App4(){
    // 2、使用reducer并解构出state与dispatch, {num:0}表示state的默认值
    const [state, dispatch] = useReducer(numReducer, {num: 0});

    return (
        <NumContext.Provider value={dispatch}>
            <h2>{state.num}</h2>
            {/* 使用dispatch调用 */}
            <Sub1 />
            <Sub2 />
        </NumContext.Provider>
    )
}

* 完整代码

import {useReducer, useContext, createContext} from 'react'
import {numReducer} from './store/reducer'

// 创建上下文
const NumContext = createContext()

// 定义Sub1子组件
function Sub1(){
    const dispatch = useContext(NumContext)
    return <button onClick={()=>{dispatch({type: 'add'})}}>累加</button>
}

// 定义Sub2子组件
function Sub2(){
    const dispatch = useContext(NumContext)
    return <button onClick={()=>{dispatch({type: 'cut'})}}>减少</button>
}


function App4(){
    // 2、使用reducer并解构出state与dispatch, {num:0}表示state的默认值
    const [state, dispatch] = useReducer(numReducer, {num: 0});

    return (
        <NumContext.Provider value={dispatch}>
            <h2>{state.num}</h2>
            {/* 使用dispatch调用 */}
            <Sub1 />
            <Sub2 />
        </NumContext.Provider>
    )
}

export default App4;

* 提升:

如果还想把h2标签抽离成组件,那么代码要改为:

import {useReducer, useContext, createContext} from 'react'
import {numReducer} from './store/reducer'

// 创建上下文
const NumContext = createContext()

// 定义Sub1子组件
function Sub1(){
    const {dispatch} = useContext(NumContext)
    return <button onClick={()=>{dispatch({type: 'add'})}}>累加</button>
}

// 定义Sub2子组件
function Sub2(){
    const {dispatch} = useContext(NumContext)
    return <button onClick={()=>{dispatch({type: 'cut'})}}>减少</button>
}

// 定义Sub3子组件——h2标签组件
function Sub2(){
    const {state} = useContext(NumContext)
    return <h2>{state.num}</h2>
}


function App4(){
    // 2、使用reducer并解构出state与dispatch, {num:0}表示state的默认值
    const [state, dispatch] = useReducer(numReducer, {num: 0});

    return (
      	{/* 这里注意:value中要传对象过去context,因此useContext获得的内容要解构 */}
        <NumContext.Provider value={{state, dispatch}}>
            <Sub3 />
            {/* 使用dispatch调用 */}
            <Sub1 />
            <Sub2 />
        </NumContext.Provider>
    )
}

export default App4;

这里再提供Consumer写法:

可以把子组件的useContext改为:

function Sub1() {
    // const {dispatch} = useContext(NumContext)
    return (
        <NumberContext.Consumer>
            {
                ({dispatch}) => {
                    return <button onClick={() => dispatch({ type: 'addNum', value: 1 })}>累加</button>
                }
            }
        </NumberContext.Consumer>
    )
}

function Sub2() {
    // const {state} = useContext(NumContext)
    return (
        <NumberContext.Consumer>
            {
                ({state}) => {
                    return <h2>{state.num}</h2>
                }
            }
        </NumberContext.Consumer>
    )
}

7、useRef

ref就是类似于id的属性,用于获取dom元素,比较简单:

import {useRef} from 'react'

function App5(){
    const element = useRef(null)

    const handleClick = () => {
        console.log(element.current)    		// 获取input
        console.log(element.current.value)  // 获取到input中的值
        
    }

    return (
        <>
            <input type="text" ref={element} />
            <button onClick={handleClick}>获取input标签</button>
        </>
    )
}

export default App5;

除此之外还有useMemo等多个Hooks,但我们没必要全部掌握,了解常用的即可。

四、Fragment与空标签【掌握】

组件中每个return都必须包含一个根标签,通常我们会使用 <div></div> ,但这也比较麻烦,毕竟有时真的不想套一个div,那我们可以这样:

// 使用空标签
function Sub2(){
    const dispatch = useContext(NumContext)
    return (
        <>
            <button>减少</button>
        </>
    )
}

或者是:

import {Fragment} from 'react'

// 使用Fragment,Fragment不会渲染成为标签节点
function Sub2(){
    const dispatch = useContext(NumContext)
    return (
        <Fragment>
            <button>减少</button>
        </Fragment>
    )
}

* 注意:

<></> 语法不能接受键值或属性,而 <Fragment> 可以。keychildren 是唯一可以传递给 Fragment 的属性,主要用在做for循环时可以拿key来做唯一标识。未来可能会添加对其他属性的支持,例如事件。

五、错误边界【了解】

官网地址:https://react.docschina.org/docs/error-boundaries.html

错误边界是一种 React 组件,可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,并且渲染出备用 UI。错误边界在渲染期间、生命周期和整个组件树的构造函数中捕获错误。

它无法在以下场景捕获错误:

  • 事件处理(了解更多)

  • 异步代码(例如 setTimeout 或 requestAnimationFrame 回调函数)

  • 服务端渲染

  • 它自身抛出来的错误(并非它的子组件)

错误边界有两种,一种是componentDidCatch(),另一种是 static getDerivedStateFromError 。这是两种生命周期方法,只要class组件用了任意一种,那它就变成一个错误边界组件。

1、componentDidCatch()

声明一个错误边界组件 ErrorBoundary.jsx,这个组件可以直接应用到全局

import React, { Component } from 'react'

export default class ErrorBoundary extends Component {
    state = {
        hasError: false
    }
    // 捕获错误
    componentDidCatch(error, errorInfo){
        this.setState({
            error,
            errorInfo,
          	hasError: true
        })
    }
    render() {
        if(this.state.hasError){
            return (
                <div>
                		<h2>出错了!!</h2>
                </div>
            )
        }else{
            return this.props.children
        }
    }
}

声明一个测试组件 Test.jsx,只要输入的是非字母就会报错:

import React, { Component } from 'react'

export default class Test extends Component {
    state = {
        value: ""
    }
    /*
        * JS中的||符号:
        1、运算方法:
            只要“||”前面为false,不管“||”后面是true还是false,都返回“||”后面的值。
            只要“||”前面为true,不管“||”后面是true还是false,都返回“||”前面的值。
        2、总结:真前假后

        * throw语句会抛出一个错误。当错误发生时, JavaScript 会停止执行并抛出错误信息。
    */
    render() {
        const value = this.state.value;
        if(/^[a-zA-Z]+$/.test(value) || value===""){
            return <input type="text" placeholder="只能填写字母" value={value} onChange={this.changeFn.bind(this)} />
        }
    }
    changeFn(e){
        this.setState({
            value: e.currentTarget.value
        })
    }
}

在整个App中的组件最外层,套上这个错误边界组件:

import React, { Component } from 'react'
import ErrorBoundary from './ErrorBoundary'
import Test from './Test'

export default class App6 extends Component {
    render(){
        return(
            <ErrorBoundary>
                <Test />
            </ErrorBoundary>
        )
    }
}

如上,可以测试到,只要输入的是非字母,就会报错:

2、static getDerivedStateFromError()

将错误边界组件中的 componentDidCatch() 改为:

import React from 'react'
export default class ErrorBoundary extends React.Component {
    constructor(props) {
        super(props);
        this.state = { hasError: false };
    }
  	// 使用static getDerivedStateFromError()
    // 意思为:获得从错误边界中产生的状态
    static getDerivedStateFromError(error) {
        return { hasError: true };
      	// 这里最好写为: return {hasError: error}
      	// 本质上是根据error是否存在来判断是否设置 hasError 为 true,但其实能够进入这个函数的话,也就代表一定会有错误,因此直接设置 hasError 为 true 也未尝不可
    }
    render() {
        if (this.state.hasError) {
            return (
                <div>
                	<h2>出错了!!</h2>
                </div>
            )
        }
        //正常则返回子元素,即该组件包裹的元素
        return this.props.children;
    }
}

六、PureComponent【熟悉】

我们在学习生命周期的时候,了解过 shouldComponentUpdate ,它是用来判断字段修改前后值是否相等的,而且必须返回true或false,我们来看:

import React, { Component } from 'react'

// 子组件
class Sub extends Component {
  	// 通过shouldComponentUpdate返回的boolean,决定是否触发UI修改
    shouldComponentUpdate(nextProps, nextState){
        return nextProps.num !== this.props.num;
    }
    render(){
        return <h1>{this.props.num}</h1>
    }
}

export default class App7 extends Component {
    state = {
        num: 1
    }
    render() {
        return (
            <div>
                {/* 传值 */}
                <Sub num={this.state.num} />
                <button onClick={this.handleClick.bind(this)}>修改num</button>
            </div>
        )
    }
    handleClick(){
        this.setState({
            num: 2		// 想改成相同的值,可以num: 1
        })
    }
}

我们有个替代的方案,那就是 PureComponent 。它其实就是在帮我们做这样一件事:自动的帮我们编写shouldComponentUpdate方法, 避免我们为每个组件都编写一次的麻烦。我们只需要这样, 就可以一步到位:

import React, { Component, PureComponent } from 'react'

class Sub extends PureComponent {
    render(){
        return <h1>{this.props.num}</h1>
    }
}

export default class App7 extends Component {
    state = {
        num: 1
    }
    render() {
        return (
            <div>
                {/* 传值 */}
                <Sub num={this.state.num} />
                <button onClick={this.handleClick.bind(this)}>修改num</button>
            </div>
        )
    }
    handleClick(){
        this.setState({
            num: 1			// 当为1时,你会发现改不动
        })
    }
}

七、高阶组件HOC【掌握】

官网地址:https://react.docschina.org/docs/higher-order-components.html

**高阶组件(HOC)**是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。

可以简单将其理解成 “类工厂”,一般在class组件时使用。我们先来写个简单的案例:

import React, { Component } from 'react'

class Sub1 extends Component {
    state = {
        num: 1
    }
    addNumFn(){
      	// 一秒后将num修改为2
        setTimeout(()=>{
            this.setState({num: 2})
        }, 1000)
    }
    render(){
        return <button onClick={this.addNumFn.bind(this)}>Sub1数字为:{this.state.num}</button>
    }
}

class Sub2 extends Component {
    state = {
        num: 1
    }
    addNumFn(){
      	// 两秒后将num修改为2
        setTimeout(()=>{
            this.setState({num: 2})
        }, 2000)
    }
    render(){
        return <button onClick={this.addNumFn.bind(this)}>Sub2数字为:{this.state.num}</button>
    }
}

export default class App8 extends Component {
    render() {
        return (
            <div>
                <Sub1></Sub1>
                <hr/>
                <Sub2></Sub2>
            </div>
        )
    }
}

上面的Sub1和Sub2都是在定时器结束后修改num,大家可以看到代码几乎完全一样。那么这时候你肯定会想到代码抽离,所以我们定义一个函数来抽离:

import React, { Component } from 'react'

// 定义一个高阶组件HOC(本质就是高阶函数HOF)
// 参数传入一个Component,也就是调用这个函数时需要传入的组件
const hocFunc = (Component, timer) => {
    // 返回一个组件,这里class后面可以不跟组件名
    return class extends Component {
        state = {
            num: 1
        }
        addNumFn(){
          	// 两秒后将num修改为2
            setTimeout(()=>{
                this.setState({num: 2})
            }, 2000)
        }
        render(){
            // 将state的值传递给组件,并返回这个组件
            return <Component num={this.state.num} addNumFn={this.addNumFn.bind(this)} />
        }
    }
}

class Sub1 extends Component {
    render(){
        // 要用props接收
        return <button onClick={this.props.addNumFn}>Sub1数字为:{this.props.num}</button>
    }
}

class Sub2 extends Component {
    render(){
        return <button onClick={this.props.addNumFn}>Sub2数字为:{this.props.num}</button>
    }
}

const Sub1Com = hocFunc(Sub1, 1000);	// 使用高阶组件返回出来的是组件,可以在App8中直接调用
const Sub2Com = hocFunc(Sub2, 2000);

export default class App8 extends Component {
    render() {
        return (
            <div>
            		{/* 渲染的是返回出来的值 */}
                <Sub1Com></Sub1Com>
                <hr/>
                <Sub2Com></Sub2Com>
            </div>
        )
    }
}

如此,咱们就实现了高阶组件。

八、LazyLoad【熟悉】

React有组件懒加载的功能。

我们先定义一个 Sub.js 子组件:

import React, { Component } from 'react'

export default class Sub extends Component {
    render() {
        return (
            <h2>Sub</h2>
        )
    }
}

再在父组件引用:

import React, { Component } from 'react'
// 使用 React.lazy引入
const Sub = React.lazy(()=>import('./Sub'))

export default class App9 extends Component {
    render() {
        return (
           <Sub />
        )
    }
}

但以上代码是无效的,因为:懒加载必须结合Suspense一起使用。

九、Suspense【熟悉】

Suspense主要解决的就是网络IO问题,是用来包裹异步组件,添加loading效果等。

我们添加Suspense代码:

import React, { Component, Suspense } from 'react'
// 懒加载
const Sub = React.lazy(() => import('./Sub'));

export default class App9 extends Component {
    render() {
        return (
            // Suspense包裹异步组件
            <Suspense fallback={<div>loading...</div>}>
                <Sub />
            </Suspense>
        )
    }
}

至此,我们将network的 No throtting 调为 slow 3G ,然后刷新页面,就可以看到loading的效果。

十、作业

作业一(初级)

使用 Function Component结合Hooks完成Tab栏切换。

作业二(中级)

使用Function Component结合Hooks完成微博发布。

项目效果:http://codesohigh.com/vue-weibo/

作业三(高级)

开始搭建 《IT猿题库》项目,并完成首页布局。(打开项目时记得先清空localStorage)

项目psd设计稿:链接: https://pan.baidu.com/s/1h5Zz45ZpX6fspbYWqsBtdA 提取码: 8w1g

最后更新于