前端笔记 React篇

1. 什么是react,以及react的优点

分析react优势及特点_家里有只猪的博客-CSDN博客_react优点

构建用户界面的javascript库(只能说这句话没啥用)。

优点:

1.声明式
声明式 :很多东西不需要亲自去作,声明一下就行了,不需要亲自去操作DOM,只需要更新数据,界面会自动变化。(其实就是mvvm 和vue一样)。

2.组件化
例如一个很复杂的界面功能,想要简化的话,就得需要拆分,拆分成一个个的小的功能,每一个小的界面功能就是一个组件,就是拆分成了很多组件,最后拼装起来就行了,最后维护起来也很方便。

3.一次学习,随处编写
现在的react不仅自能写web应用也还能写手机端应用使用react native可以打包成安卓和ios。

4.高效
1.虚拟DOM,不直接操作DOM。
2.DOM Diff算法,最小化页面重绘。
例如一个页面有很多地方需要更新,会先都在虚拟DOM完成修改,最后会映射到真实的DOM中,完成一次性修改,重绘就会减少。
 

缺点:

其实就是单页面应用的一些缺点,比如不利于seo。

2. 关于react的简单实例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<style>
    .title {
        color: red;
    }
</style>

<body>
    <div id="test"></div>
    <script type="text/babel">//babel帮忙转译
        const myID = "h1test"
        const myData = "Hello,React"
        //创建虚拟dom 这是jsx react官方出的比较方便 就是为了创建虚拟dom更舒服
        const VDOM = (
            <h1 className="title" id={myID.toLocaleLowerCase()}>
                <span style={
   
   { border: "solid black" }}>{myData}</span>
            </h1>
        )
        /**
        关于jsx
        {}内是表达式
        class要用className
        定义不要夹引号
        内敛样式写成对象模型
        jsx只能有一个根标签
        标签必须闭合
        标签首字母小写 会被转为html同名元素
        大写的开头会变成组件 没有定义就报错
        **/


        //渲染虚拟dom到页面
        ReactDOM.render(VDOM, document.getElementById("test"))
        //dom 容器 这个是替换的不是追加的
        console.log(VDOM);
    </script>
</body>

</html>

3. 数组及数据处理(可以理解为vue的v-for)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<style>
    .title {
        color: red;
    }
</style>

<body>
    <div id="test"></div>
    <script type="text/babel">//babel帮忙转译
        const myID = "h1test"
        const myData = "Hello,React"
        const arr = ["芜湖", "起飞", "666"]
        //创建虚拟dom 这是jsx react官方出的比较方便 就是为了创建虚拟dom更舒服
        const VDOM = (
            <ul>
                {
                    arr.map((val,index) => {
                        return <li key={index}>{`${index}.${val}`}</li>
                    })
                }
            </ul>
        )

        ReactDOM.render(VDOM, document.getElementById("test"))
        //dom 容器 这个是替换的不是追加的
    </script>
</body>

</html>

4. 组件

学过vue的话感觉也不需要过多介绍什么是组件。

react中的组件主要分为两类:函数组件和类组件(其实我们大多数使用用到的是类组件)。

函数组件

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

<body>
    <div id="test"></div>
    <script type="text/babel">
        //函数式组件 适用于简单组件的定义
        function Demo() {
            return <h2>我是函数组件</h2>
        }
        ReactDOM.render(<Demo />, document.getElementById("test"))
    </script>
</body>

</html>

也比较好理解,就是用函数返回虚拟dom,但是只适用于简单组件而不适用于复杂组件(也可以称为无状态组件和有状态组件)。

类组件

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

<body>
    <div id="test"></div>
    <script type="text/babel">
        //类组件 适用复杂组件(是否有状态)
        class Demo extends React.Component {
            render() {
                console.log(this);
                return (
                    <h1>类组件</h1>
                )
            }
        }
        ReactDOM.render(<Demo />, document.getElementById("test"))
    </script>
</body>

</html>

以类的方式构造虚拟dom。

复杂组件和简单组件(或者是状态组件和无状态组件)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

<body>
    <div id="test"></div>
    <script type="text/babel">
        //类组件 适用复杂组件(是否有状态)
        class Demo extends React.Component {
            //state改变构造器只调用1次
            constructor(props) {
                super(props)
                this.state = { isHot: true, wind: "微风" }
                this.change = this.change.bind(this)
            }
            //类中的方法会在局部开启严格模式 改几次状态调用几次render
            render() {
                return (
                    //这个方法是直接调用 不是实例调用的
                    <h1 onClick={this.change}>今天天气很{this.state.isHot ? "炎热" : "凉爽"}</h1>
                )
            }
            change() {
                //状态不可直接更改
                // this.state.isHot = !this.state.isHot
                //要调用api setState
                const isHot = this.state.isHot
                this.setState({ isHot: !isHot }) //合并而不是替换
            }
        }
        ReactDOM.render(<Demo />, document.getElementById("test"))
    </script>
</body>

</html>

可以看到在这个class里我们有一个state属性,其实他就相当于vue里的组件中的data,用来存放该组件的数据,只有类组件可以定义state。但是这里有个坑就是,不能直接修改state中的值(会失去响应式),要使用人家封装好的函数:this.setState({...}). 注意上面的这个构造体中接收props以及super(props)都是必须的,不然报错。

5. React三大件核心属性

(1)state

前面已经介绍了,其实就是组件的状态,用来保存组件的数据。

state除了在构造体中定义,还有简写模式,比较常用

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>

<body>
    <div class="main"></div>
    <script type="text/babel">
        class MyComponent extends React.Component {
            state = { isHot: false, wind: "微风" }
            render() {
                return (
                    <h1 onClick={this.change}>天气{this.state.isHot ? "炎热" : "凉爽"}</h1>
                )
            }
            change = () => {
                const isHot = this.state.isHot
                this.setState({ isHot: !isHot })
            }
        }
        ReactDOM.render(<MyComponent />, document.getElementsByClassName("main")[0])
    </script>
</body>

</html>

注意点就是记得一定适用this.setState修改组件的状态。

(2)props

学过vue对这玩意可太熟悉了,在react里其实一模一样,就是传值的,父子传值,完全没有问题。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
    <div class="main"></div>
    <script type="text/babel">
        class MyComponent extends React.Component {
            state = { isHot: false, wind: "微风" }
            render() {
                console.log(this);
                return (
                    <div>
                        <h1 onClick={this.change}>天气{this.state.isHot ? "炎热" : "凉爽"}</h1>
                        <ul>
                            <li>姓名:{this.props.name}</li>
                            <li>性别:{this.props.sex}</li>
                            <li>年龄:{this.props.age}</li>
                        </ul>
                    </div>
                )
            }
            change = () => {
                const isHot = this.state.isHot
                this.setState({ isHot: !isHot })
            }
        }
        //添加prop规则-------------------------------------
        // MyComponent.propTypes = {   //你类的属性规则 ,, static相当于直接给类本身添加了属性
        //                 name: PropTypes.string.isRequired,
        //                 sex: PropTypes.string,
        //                 age: PropTypes.number,
        //                 spack: PropTypes.func  
        // }
        //添加默认值-------------------------------
        MyComponent.defaultProps = {
            sex: '未知',
            age: 28,
        }
        // ------------------------------------------
        const obj = { a: 1, b: 2, c: 3 }
        //从外向内传
        ReactDOM.render(<MyComponent {...obj} name="tom" sex="男" />, document.getElementsByClassName("main")[0])
    </script>
</body>

</html>

传值的方法也和vue一样,往组件上绑定就行。我们可以看到传入的props

 当然除了上面那种定义初始值的方法,还有简写方法

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>

<body>
    <div class="main"></div>
    <script type="text/babel">
        class MyComponent extends React.Component {
            state = { isHot: false, wind: "微风" }
            static defaultProps = {
                sex: '未知',
                age: 28,
            }
            // static propTypes = {   //你类的属性规则 ,, static相当于直接给类本身添加了属性
            //                 name: PropTypes.string.isRequired,
            //                 sex: PropTypes.string,
            //                 age: PropTypes.number,
            //                 spack: PropTypes.func  
            // }
            render() {
                return (
                    <div>
                        <h1 onClick={this.change}>天气{this.state.isHot ? "炎热" : "凉爽"}</h1>
                        <ul>
                            <li>姓名:{this.props.name}</li>
                            <li>性别:{this.props.sex}</li>
                            <li>年龄:{this.props.age}</li>
                        </ul>
                    </div>
                )
            }
            change = () => {
                const isHot = this.state.isHot
                this.setState({ isHot: !isHot })
            }
        }
        // ------------------------------------------
        const obj = { a: 1, b: 2, c: 3 }
        //从外向内传
        ReactDOM.render(<MyComponent {...obj} name="tom" sex="男" />, document.getElementsByClassName("main")[0])
    </script>
</body>

</html>

props与state不同,他同样可以在函数组件里使用

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>

<body>
    <div class="main"></div>
    <script type="text/babel">
        const MyComponent = function (props) {
            const { name } = props
            return (
                <div>
                    <h1>函数组件</h1>
                    <h2>{name}-{props.a}</h2>
                </div>
            )
        }
        // ------------------------------------------
        const obj = { a: 1, b: 2, c: 3 }
        //从外向内传
        ReactDOM.render(<MyComponent {...obj} name="tom" sex="男" />, document.getElementsByClassName("main")[0])
    </script>
</body>

</html>

(3)refs

这个又和vue的差不多(因为我是先学的vue,当然我并不关心谁抄的谁这种争论),ref就是用来标记dom的,这样就可以方便我们快速操作。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>

<body>
    <div id="main"></div>
    <script type="text/babel">
        class Demo extends React.Component {
            render() {
                console.log(this);
                //string类型的ref过时了 存在效率问题 可能未来移除
                return (
                    <div>
                        <h1>我是组件</h1>
                        <input ref="input1" type="text" placeholder="点击显示" />
                        <button onClick={this.showData}>点我提示左侧的数据</button>
                        <input ref="input2" type="text" placeholder="失焦" />
                    </div>
                )
            }
            showData = () => {
                console.log(this.refs.input1.value);
            }
        }
        ReactDOM.render(<Demo />, document.getElementById("main"))
    </script>
</body>

</html>

可以看一下这的ref

但是ref有三种类型的使用方法:

1. 字符串类型:

如上,但是已经不提倡使用了,string类型的ref过时了 存在效率问题 可能未来移除。

2. 回调类型

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>

<body>
    <div id="main"></div>
    <script type="text/babel">
        class Demo extends React.Component {
            state = { age: 1 }
            render() {
                // 回调类型的ref 内敛形式更新时会执行两次 第一次传null 第二次传需要的dom
                return (
                    <div>
                        <h1>我是组件</h1>
                        <input ref={(ele) => { this.input1 = ele, console.log("内敛形式", ele); }} type="text" placeholder="点击显示" />
                        <button onClick={this.showData}>点我提示左侧的数据</button>
                        <input ref={this.saveInput} type="text" placeholder="失焦" />
                        <button onClick={this.add}>{this.state.age}</button>
                    </div>
                )
            }
            showData = () => {
                console.log(this.input1.value);
            }
            //不会重复执行
            saveInput = (e) => {
                this.input2 = e
                console.log("非内联形式", e);
            }
            add = () => {
                const age = this.state.age
                this.setState({ age: age + 1 })
            }
        }
        ReactDOM.render(<Demo />, document.getElementById("main"))
    </script>
</body>

</html>

很好理解,就是直接给他挂载到组件上,给个属性名。

 3. api方法(官方最提倡但是最麻烦)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>

<body>
    <div id="main"></div>
    <script type="text/babel">
        class Demo extends React.Component {
            // myRef=React.createRef 返回一个容器 存储ref所标识的节点
            // 专人专用
            myRef = React.createRef()
            myRef2 = React.createRef()
            render() {
                console.log(this);
                // 回调类型的ref
                return (
                    <div>
                        <h1>我是组件</h1>
                        <input ref={this.myRef} type="text" placeholder="点击显示" />
                        <button onClick={this.showData}>点我提示左侧的数据</button>
                        <input ref={this.myRef2} type="text" placeholder="失焦" />
                    </div>
                )
            }
            showData = () => {
                console.log(this.myRef.current.value);
            }
        }
        ReactDOM.render(<Demo />, document.getElementById("main"))
    </script>
</body>

</html>

 6. react的事件

react事件是再封装的,写法就是on后面首字母大写

例如

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>

<body>
    <div id="main"></div>
    <script type="text/babel">
        //1.通过onClick这一类的绑定(封装的为了高效)
        //2.通过event.tatget
        //自己操作自己的dom 可以不用ref
        class Demo extends React.Component {
            render() {
                return (
                    <div>
                        <div>我是组件</div>
                        <input onBlur={this.showData} />
                    </div>
                )
            }
            showData = (e) => {
                console.log(e.target.value);
            }
        }
        ReactDOM.render(<Demo />, document.getElementById("main"))
    </script>
</body>

</html>

7. 受控组件和非受控组件

感觉不重要俩例子带过。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>

<body>
    <div id="main"></div>
    <script type="text/babel">
        //非受控就是dom现用现取
        class Demo extends React.Component {
            render() {
                return (
                    <form onSubmit={this.handle}>
                        <input ref="username" placeholder="用户名" type="text" name="username" />
                        <input ref="password" placeholder="密码" type="password" name="password" />
                        <button>登录</button>
                    </form>
                )
            }
            handle = (e) => {
                const { username, password } = this.refs
                e.preventDefault()
                console.log(`用户名:${username.value},密码:${password.value}`);
            }
        }
        ReactDOM.render(<Demo />, document.getElementById("main"))
    </script>
</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>

<body>
    <div id="main"></div>
    <script type="text/babel">
        //受控就是数据维护在state里(更推荐)
        class Demo extends React.Component {
            state = { username: "", password: "" }
            render() {
                return (
                    <form onSubmit={this.handle}>
                        <input onChange={this.setUserName} placeholder="用户名" type="text" name="username" />
                        <input onChange={this.setPassWord} placeholder="密码" type="password" name="password" />
                        <button>登录</button>
                    </form>
                )
            }
            setUserName = (e) => {
                this.setState({ username: e.target.value })
            }
            setPassWord = (e) => {
                this.setState({ password: e.target.value })
            }
            handle = (e) => {
                const { username, password } = this.state
                e.preventDefault()
                console.log(`用户名:${username},密码:${password}`);
            }
        }
        ReactDOM.render(<Demo />, document.getElementById("main"))
    </script>
</body>

</html>

8. react函数柯里化操作,高阶函数应用

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>

<body>
    <div id="main"></div>
    <script type="text/babel">
        //受控就是数据维护在state里(更推荐)
        class Demo extends React.Component {
            state = { username: "", password: "" }
            render() {
                return (
                    <form onSubmit={this.handle}>
                        <input onChange={this.save("username")} placeholder="用户名" type="text" name="username" />
                        <input onChange={(e) => { this.save2("password", e.target.value) }} placeholder="密码" type="password" name="password" />
                        <button>登录</button>
                    </form>
                )
            }
            //高阶函数 符合任何一个
            //A接收一个函数
            //A返回一个函数

            //   //柯里化
            // 指通过函数调用继续返回函数多次接收参数 最后统一处理
            save = (name) => {
                return (e) => {
                    this.setState({ [name]: e.target.value })
                }
            }

            //不用柯里化
            save2 = (name, value) => {
                this.setState({ [name]: value })
            }
            handle = (e) => {
                const { username, password } = this.state
                e.preventDefault()
                console.log(`用户名:${username},密码:${password}`);
            }
        }
        ReactDOM.render(<Demo />, document.getElementById("main"))
    </script>
</body>

</html>

9. 生命周期

老生常谈的东西。指整个编译过程的各个事件节点。

有新旧两种生命周期,旧的依然使用,因为向下兼容。

生命周期(旧)

 大致讲一下就是

首先挂载这条路

constructor类似于vue的created

主要负责一些初始化工作,类似于state初始化等等。

component will mount(故意分开写了)就是说组件即将挂载,挂载可以理解为将这些虚拟dom信息挂载到页面上生成真实dom。

render就是渲染,所以可以发现,其实不管第一次挂载还是以后更新都是需要再次调用render的。

component did mount 就是挂载完了。

最后unmount很好理解就是卸载,就是组件生命尽头,不用了所以丢弃掉了。

第二条线

首先是compoenet will receive props 其实就是字面意思,组件即将接收props,第一次的默认不算。

should compoent update就是组件应该更新吗,其实这玩意更像一个开关阀门。它必须返回true或者false,true就是更新,false就是不更新。当然可以调用this.forceUpdate()跳过闸门。

will update 就是即将更新,did update就是更新完了。中间依旧是渲染。最后是卸载。

直接给例子

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<style>
    .title {
        color: red;
    }
</style>

<body>
    <div id="test"></div>
    <script type="text/babel">//babel帮忙转译
        class Count extends React.Component {
            constructor(props) {
                super(props)
                this.state = { count: 0 }
                console.log("Count1", "constructor");
            }
            //组件将要挂载
            componentWillMount() {
                console.log("Count2", "componentWillMount");
            }
            //挂载完毕
            componentDidMount() {
                console.log("Count4", "componentDidMount");
            }
            //即将卸载
            componentWillUnmount() {
                console.log("Count5", "componentWillUnmount");
            }
            //是否更新的阀门 setState之后
            shouldComponentUpdate() {
                console.log("shouldComponentUpdate");
                return true
            }
            //即将更新
            componentWillUpdate() {
                console.log("componentWillUpdate");
            }
            //更新完毕 在render之后
            componentDidUpdate() {
                console.log("componentDidUpdate");
            }
            render() {
                console.log("Count3", "render");
                return (
                    <div>
                        <h1>求和值为:{this.state.count}</h1>
                        <button onClick={this.add}>点击+1</button>
                        <button onClick={this.force}>forceUpdate()</button>
                        <button onClick={this.death}>卸载</button>
                    </div>
                )
            }
            add = () => {
                let { count } = this.state
                count++
                this.setState({ count })
            }
            death = () => {
                ReactDOM.unmountComponentAtNode(document.getElementById("test"))
            }
            force = () => {
                //强制更新跳过阀门
                this.forceUpdate()
            }
        }
        class A extends React.Component {
            state = { fatherName: "A" }
            render() {
                return (
                    <div>
                        <h1>我是父组件</h1>
                        <button onClick={this.addA}>名字多个A</button>
                        <B father={this.state.fatherName} />
                    </div>
                )
            }
            addA = () => {
                let { fatherName } = this.state
                fatherName = fatherName + "A"
                this.setState({ fatherName })
            }
        }
        class B extends React.Component {
            //子组件即将收到props 第一次的默认不算
            componentWillReceiveProps(props) {
                console.log("componentWillReceiveProps", props);
            }
            shouldComponentUpdate() {
                console.log('shouldComponentUpdate');
                return true
            }
            componentWillUpdate() {
                console.log("componentWillUpdate");
            }
            componentDidUpdate() {
                console.log("componentDidUpdate");
            }
            render() {
                console.log("render");
                return (
                    <div>
                        <h1>我是子组件</h1>
                        <h1>父亲是{this.props.father}</h1>
                    </div>
                )
            }
        }
        ReactDOM.render(<A />, document.getElementById("test"))
    </script>
</body>

</html>

生命周期(新)忽略弹幕,我直接截图的。

 可以看到那些will的都没了,除了will unmount。

这些新钩子都,基本用不上,我就直接给例子了

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<style>
    .title {
        color: red;
    }
</style>

<body>
    <div id="test"></div>
    <script type="text/babel">//babel帮忙转译
        class A extends React.Component {
            state = { fatherName: "A" }
            //必须return state obj   基本不用 如果state取决于props
            static getDerivedStateFromProps(props, state) {
                console.log("getDeivedStateFromProps", props, state);
                return null
                // return props
            }
            //马上出效果之前 它的返回值会返回给componentDidUpdate
            getSnapshotBeforeUpdate() {
                console.log("getSnapshotBeforeUpdate");
                return { snap: "snap" }
            }
            componentDidUpdate(preProps, preState, sanp) {
                console.log("A-componentDidUpdate", preProps, preState, sanp);
            }
            render() {
                return (
                    <div>
                        <h1>我是父组件</h1>
                        <button onClick={this.addA}>名字多个A</button>
                        <B father={this.state.fatherName} />
                    </div>
                )
            }
            addA = () => {
                let { fatherName } = this.state
                fatherName = fatherName + "A"
                this.setState({ fatherName })
            }
        }
        class B extends React.Component {
            // //子组件即将收到props 第一次的默认不算
            // UNSAFE_componentWillReceiveProps(props) {
            //     console.log("componentWillReceiveProps", props);
            // }
            // UNSAFE_componentWillMount() {
            //     console.log("UNSAFE_componentWillMount");
            // }
            shouldComponentUpdate() {
                console.log('shouldComponentUpdate');
                return true
            }
            //新版可以加UNSAFE_去掉警告
            // UNSAFE_componentWillUpdate() {
            //     console.log("componentWillUpdate");
            // }
            componentDidUpdate() {
                console.log("componentDidUpdate");
            }
            render() {
                console.log("render");
                return (
                    <div>
                        <h1>我是子组件</h1>
                        <h1>父亲是{this.props.father}</h1>
                    </div>
                )
            }
        }
        ReactDOM.render(<A test={"test"} />, document.getElementById("test"))
    </script>
</body>

</html>

10. react脚手架

## 全局安装

npm i -g create-react-app

## 创建项目的目录

create-react-app hello-react

## 启动项目

npm start

11. 脚手架文件介绍

两大部分,src和public

public

 第一个就是图标

就是这个玩意:

 第二个,其实就是单页面文件的html,毕竟不管vue还是react都得需要一个页面(index.html)作为载体(里面具体的作用如下)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <!-- 代表public的路径 -->
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <!-- 开启理想视口 用于做适配 -->
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <!-- 用于配置浏览器页签 地址的背景颜色 兼容性差 只支持安卓手机浏览器 -->
    <meta name="theme-color" content="#000000" />
    <!-- 描述信息 -->
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <!-- 添加到主屏上的图标 -->
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <!-- 应用加壳(把web页面变成类似的手机应用 例如生成apk文件) 在以下的文件里配置权限啥的 -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
  </head>
  <body>
    <!-- 浏览器不支持js就会出现 -->
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

还有什么json的爬虫规则的就不说了没啥用

src

 components自己创得用来放组件的

app.js就是最大的父组件,和vue的app.vue一样,都是一个最大的组件然后带了一群后代。

index.js就是类似于vue的main.js 负责的内容如下

//引入react核心件
import React from 'react';
import ReactDOM from 'react-dom/client';
//引入基本样式
import './index.css';
import App from './App';
// 记录页面性能
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';
/* <React.StrictMode>用来检查写的是否合理 比如字符串的ref */
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <BrowserRouter><App /></BrowserRouter>
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

reportWebVitals用来记录页面性能

setupProxy设置服务器代理

//用来配置脚手架
const { createProxyMiddleware } = require("http-proxy-middleware")
module.exports = function (app) {
    app.use('/api1',    // 指定需要转发的请求
        createProxyMiddleware({
            target: "http://127.0.0.1:8000",   //指定转发的目标地址   
            changeOrigin: true,
            pathRewrite: {
                "^/api1": ""   // 字面意思是路径重写,其实就是把url地址中指定片段替换掉
            }
        }))
}

其他的test的都是测试文件,用不上。

12. 组件间传值

父 传 子


        return (
            <div className={Css.TodoListWrap}>
                <Header getValue={this.getValue} />
                <List setDone={this.setDone} deleteItem={this.deleteItem} todos={this.state.todos} />
                <Footer todos={this.state.todos} />
            </div>
        )

就是在子组件上面邦就完事了。然后子组件的props接收。

子 传 父

经典emit

父在子的身上绑定回调函数

  <List setDone={this.setDone} deleteItem={this.deleteItem} todos={this.state.todos} />
deleteItem = (id) => {
        let newToDos = this.state.todos.filter((item) => {
            return item.id !== id
        })
        this.setState({ todos: newToDos })
    }
    getValue = (val) => {
        let { todos } = this.state
        this.setState({ todos: [...todos, { id: v4(), name: val, done: false }] })
    }
    setDone = (done, id) => {
        let todos = this.state.todos
        for (let item of todos) {
            if (item.id === id) {
                item.done = done
                break
            }
        }
        this.setState({ todos })

子组件负责触发并且传值

    deleteItem = (id) => {
        this.props.deleteItem(id)
    }
    setDone = (done, id) => {
        this.props.setDone(done, id)
    }

兄弟传值

最常用的就是发布订阅模式

## 兄弟通信组件库

PubSubJS

npm i pubsub-js --save

先把它装了

然后

在接收组件中这么使用

import React, { Component } from 'react'
import PubSub from 'pubsub-js'
export default class B2 extends Component {
    state = { B1: "" }
    componentDidMount() {
        PubSub.subscribe("ValueOfB1", (msg, data) => {
            this.setState({ B1: data })
        })
    }
    render() {
        return (
            <div>B2接收到了B1的值:
                <span>{this.state.B1}</span>
                <hr></hr>
                fetch和xhr都是内置的
                <button onClick={this.test}>fetch test</button>
            </div>
        )
    }
}

引入pubsub并且订阅消息,设置回调函数(参数就是要接收的参数,msg是消息名:这个就是ValueOfB1,data是传来的数据)。

在发送数据的组件中这么使用

import React, { Component } from 'react'
import PubSub from 'pubsub-js';
export default class B1 extends Component {
    sendValue = (e) => {
        if (e.keyCode === 13 && e.target.value.trim() !== "") {
            PubSub.publish("ValueOfB1", e.target.value)
            e.target.value = ""
        }
    }
    render() {
        return (
            <div>B1传值:
                <input onKeyUp={(e) => (this.sendValue(e))} type="text" />
            </div>
        )
    }
}

发布信息,携带参数。此时接收组件的回调函数便会触发。

祖先与后代传参

使用上下文

const MyContext = React.createContext()
//标签
 <MyContext.Provider value={this.state.myname}>                   
     <son>
</MyContext.Provider>

然后在后代组件中接收就行

  static contextType = MyCountext

13. 路由

大部分用的路由是5版本的,现在都更新到了6.这俩有一些区别,但是鉴于5现在用的还特别多就都记录一下。

5版本

(1)基本使用

首先路由是分为两种版本的(同vue)

分别为hash 和 history两种模式

例子

hash:https:/127.0.0.1:7000/#/test

注意#后面的东西是不会发送给服务器的,在这里知识我们router的标识。

history:https:/127.0.0.1:7000/test

这个就是和我们平时见到的一样。

在react中,这两种router分别对应:HashRouter 和 BrowserRouter。

react中的跳转使用的是link标签(来自router库的),但是该标签必须包含在上面的两个router大哥标签内。但是开发的时候为了省事,我们直接在app组件外层包上就行。(在index.js中操作)

//引入react核心件
import React from 'react';
import ReactDOM from 'react-dom/client';
//引入基本样式
import './index.css';
import App from './App';
// 记录页面性能
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';
/* <React.StrictMode>用来检查写的是否合理 比如字符串的ref */
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <BrowserRouter><App /></BrowserRouter>
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

然后我们还需要注册路由(就是说明不同的url具体对应哪个组件)

一共三步:(1)外面包HashRouter 或 BrowserRouter。(2)使用link设置跳转。(3)注册路由。

例子

import React, { Component } from 'react'
import Test1 from '../Test1'
import Test2 from '../Test2'
import Css from "./index.module.css"
import { Link, Route } from "react-router-dom"
export default class Main extends Component {
    render() {
        return (
            <div>
                <div>Main</div>
                <div>
                    <Link className={Css.turn} to="/test1">Test1</Link>
                    <Link className={Css.turn} to="/test2">Test2</Link>
                </div>
                <div>
                    <Route path="/test1" component={Test1} />
                    <Route path="/test2" component={Test2} />
                </div>
            </div>
        )
    }
}

(2)路由组件和一般组件

Main就是一般的,test1,test2就是路由组件(通过路由匹配而展示的页面,没有直接使用<test1/>)。主要是不放在component养成习惯,要放在pages下。

另外一点就是,路由组件会收到路由器传递的一些参数如图(都在props中)

 前三个都有用后面分别都会用到。

(3)NavLink 

需要引入,然后用法和link一模一样,只不过能设置跳转后样式。

 <NavLink className={Css.turn} activeClassName={Css.go} to="/test1">Test1</NavLink>
 <NavLink className={Css.turn} activeClassName={Css.go} to="/test2">Test2</NavLink>

 当然如果有好多导航项的话都得这么写很麻烦,所以可以自己封装

MyNavLink

import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
import Css from "./index.module.css"
export default class MyNavLink extends Component {
    render() {
        // const { to,children } = this.props
        return (
            // <NavLink activeClassName={Css.actived} className={Css.deactived} to={to}>{children}</NavLink>
            <NavLink activeClassName={Css.actived} className={Css.deactived}  {...this.props}/>
        )
    }
}

使用

      <MyNavLink to="/test1">test1</MyNavLink>
      <MyNavLink to="/test2">test2</MyNavLink>

(4)switch

先说route注册的坏处,使用route注册的话匹配模式是全遍历,比如两个或者多个组件都匹配该地址,就会全部展示。

这个效率比较低,因为正常开发中都是一个地址匹配一个组件。

使用非常简单,引入,包在外面,里面就变成了匹配到直接停。

        <Switch>
                        <Route path="/test1" component={Test1} />
                        <Route path="/test2" component={Test2} />
        </Switch>

(5)路由的匹配模式

路由的匹配模式分为两种

一是模糊匹配,例如/home/a/b,如果没有对应匹配的,那么就会匹配/home/a,如果还没有就去找/home。但是给少了不行。顺序不能错,/a/b/home就匹配不上。

二是精准匹配(严格匹配),必须一模一样。开启方式:

<Route exact={true} path="/test1" component={Test1} />

没事干别开,会导致无法匹配二级路由。

(6)Redirect 重定向

默认跳转一个组件。

  <Switch>
      <Route exact={true} path="/test1" component={Test1} />
      <Route path="/test2" component={Test2} />
      <Redirect to="/test1" />
  </Switch>

都没匹配上的话请去Test1

(7)路由传参

第一种方式,占位符,传到params里

import React, { Component } from 'react'
import MyNavLink from '../../components/MyNavLink';
import { Switch, Route } from 'react-router-dom';
import Kid1 from './Kid1';
import Kid2 from './Kid2';
export default class Test1 extends Component {
  render() {
    return (
      <div>
        <MyNavLink to="/test1/kid1/Test1">kid1</MyNavLink>
        <MyNavLink to="/test1/kid2">kid2</MyNavLink>
        <Switch>
          <Route path="/test1/kid1/:father" component={Kid1}></Route>
          <Route path="/test1/kid2" component={Kid2}></Route>
        </Switch>
      </div>
    )
  }
}

看kid1的传参

 所以kid1里可以直接用

第二种 serch参数

       <MyNavLink to="/test1/kid2?father=Test1&id=3">kid2</MyNavLink>

接收(都不帮忙整理的,反正我不喜欢用)

第三种 传递state参数

<MyNavLink to={
   
   { pathname: '/test1/kid2', state: { id: 11, father: "Test1" } }}>kid2</MyNavLink>

 接收

 非常好用。

(8)路由跳转模式

默认为push模式,还有replace。

 <MyNavLink replace={true} to="/test2">test2</MyNavLink>

区别在于 push模式就是栈的模式,会留下痕迹,我们可以后退到刚刚点击的页面。

但是replace就是替换,会把刚刚上一个记录给替换掉。

(9)编程式路由导航

就是不用link标签路由跳转。

 turnKid1 = () => {
    //push模式
    this.props.history.replace('/test1/kid1/Test1')
  }
  turnKid2 = () => {
    //replace模式
    this.props.history.push({ pathname: '/test1/kid2', state: { id: 555, father: "Test1" } })
  }

(10)withRouter

这个是react路由所特有的,对于vue的路由来讲,其实们没有一般组件和路由组件之分,在哪里我们都可以进行路由操作,但是react的一般组件并不具备那三件套(props默认为空)

路由组件

一般组件

所以不可以在一般组件中操作这些东西,类似于history中的goback这些api是够不着的,就需要使用withRouter去包装一下。

import React, { Component } from 'react'
import Test1 from '../../../pages/Test1'
import Test2 from '../../../pages/Test2'
// import Css from "./index.module.css"
import { withRouter, Redirect, Switch, Route } from "react-router-dom"
import MyNavLink from '../../MyNavLink'
class Main extends Component {
    render() {
        console.log(this);
        return (
            <div>
                <div>Main</div>
                <button onClick={this.back}>back</button>
                <div>
                    {/* <Link className={Css.turn} to="/test1">Test1</Link>
                    <Link className={Css.turn} to="/test2">Test2</Link> */}
                    {/* <NavLink className={Css.turn} activeClassName={Css.go} to="/test1">Test1</NavLink>
                    <NavLink className={Css.turn} activeClassName={Css.go} to="/test2">Test2</NavLink> */}
                    <MyNavLink to="/test1">test1</MyNavLink>
                    <MyNavLink replace={true} to="/test2">test2</MyNavLink>
                </div>
                <div>
                    <Switch>
                        <Route path="/test1" component={Test1} />
                        <Route path="/test2" component={Test2} />
                        <Redirect to="/test1" />
                    </Switch>
                </div>
            </div>
        )
    }
    back = () => {
        this.props.history.goBack()
    }
}
export default withRouter(Main)

 最后一个小tip

就是hashRouter刷新会丢失传过来的state值,因为不会维护history。

14. 第三方库使用

以ant design react为例

npm i antd --save 安装

在根css文件引入antdcss

文件如下

@import '~antd/dist/antd.css';
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}

引入使用

import React, { Component } from 'react'
import { Button } from 'antd'
export default class AntdTest extends Component {
    render() {
        return (
            <div>
                <div>AntdTest</div>
                <Button type="link">Link Button</Button>
            </div>
        )
    }
}

样式其实也可以按需引入,还有什么自定义主题,看文档就行

15. redux

类似vue的vuex,状态管理

其实redux也能在vue中使用,不过大家基本用的还是vuex

记住一个原则,和vuex一样,能不用就不用,实在是需要共享的数据组件多了就用。

 流程图

首先组件告诉action要干嘛

action就是行为,包含两个部分,一个是事件类型,理解为你要干嘛,一个是事件的数据。

然后用过dispatch(分发)函数来执行该行为。其实就是为了把这个行为发给store

Store本身就是个调度者,它只负责指挥,不负责处理行为,所以它直接指挥Reducers干活。Store交给reduce之前的状态和行为,比如动作是+3,那我们必须知道原来是多少才能加,当reducer完成之后就会把新的状态返回给store。

然后我们的组件就可以和Store获取现在的数据。

(1)Redux

这个如果想直接学最终版可以直接看第二部分 (2)react-redux

但是呢这个其实有个缺点,就是我们的redux状态发生改变的时候,react不会自动更新组件,毕竟redux也不是facebook研发的,如果使用纯redux完成实例的话,我直接给个例子。

首先需要创建redux文件夹在src下,一共包含这几个文件

 constant.js就是定义常量的,避免我们自己写错,也方便维护。

count_action.就是服务count 的 Actrion对象创建

下面的同理,服务count组件的redux

以及我们的store

安装redux    npm i redux --save

constant.js

//定义常量 type类型
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
export const RESET = 'reset'

count_action.js

//为count生成action对象
// action有同步和异步两种
import { INCREMENT, DECREMENT, RESET } from './constant'
//同步action
export function createIncrement(data) {
    return { type: INCREMENT, data }
}
export function createDecrement(data) {
    return { type: DECREMENT, data }
}
export function createReset(data) {
    return { type: RESET, data }
}
//异步action action的值为函数
export function createAsycIncrement(data, delay) {
    return (dispatch) => {
        setTimeout(() => {
            dispatch(createIncrement(data))
        }, delay)
    }
}

注意这个例子里使用了异步action需要安装一个中间件

npm i redux-thunk --save

这个要在store中使用(看下面的store里)

count_reducer.js

import { INCREMENT, DECREMENT, RESET } from './constant'
//初始化状态
const initState = 0
export default function countRedux(preState = initState, action) {
    const { type, data } = action
    switch (type) {
        case INCREMENT:
            return preState + data
        case DECREMENT:
            return preState - data
        case RESET:
            return initState
        default:
            return preState
    }
}

store.js

import { legacy_createStore,applyMiddleware } from "redux"
import countRedux from './count_redux'
//引入thunk是为了支持异步action
import thunk from "redux-thunk"
//applyMiddleware是store使用中间件,然后要一块传入creatstore中
export default legacy_createStore(countRedux,applyMiddleware(thunk))

然后就是我们的count组件(就是一个利用redux简单运算的功能)

import React, { Component } from 'react'
import store from '../../../redux/store'
import { createDecrement, createIncrement, createReset, createAsycIncrement } from "../../../redux/count_action"
export default class CountRedux extends Component {
    // componentDidMount() {
    //     //检测store状态改变 改变就走回调
    //     store.subscribe(() => {
    //         this.setState({})
    //     })
    // }
    render() {
        return (
            <div>
                <div>CountRedux</div>
                <input ref={(ele) => { this.input = ele }} type="number"></input>
                <button onClick={this.add}>+</button>
                <button onClick={this.decrease}>-</button>
                <button onClick={this.judgeOdd}>increment if odd</button>
                <button onClick={this.judgeEven}>increment if even</button>
                <button onClick={() => { this.incrementAsyc(2000) }}>increment asycn</button>
                <button onClick={this.reset}>Reset</button>
                <div>Result={store.getState()}</div>
            </div>
        )
    }
    add = () => {
        store.dispatch(createIncrement(Number(this.input.value)))
        this.input.value = ""
    }
    decrease = () => {
        store.dispatch(createDecrement(Number(this.input.value)))
        this.input.value = ""
    }
    judgeOdd = () => {
        if (store.getState() % 2 === 1) {
            this.add()
        }
    }
    judgeEven = () => {
        if (store.getState() % 2 === 0) {
            this.add()
        }
    }
    incrementAsyc = (delay) => {
        store.dispatch(createAsycIncrement(Number(this.input.value), delay))
        this.input.value = ""
    }
    reset = () => {
        store.dispatch(createReset(null))
        this.input.value = ""
    }
}

在挂载里可以看到我注释掉了东西,其实是把这个添加到index.js里了,这样一旦redux状态改变都能刷新。

红色区域就是新加的。但是使用react-redux就不需要了。

(2)React-redux

那么为什么会突然跑出来一个react-redux,其实就是因为

其实就是因为 

一.redux是一个通用的状态管理,vue angular react都能用。

二.不会帮我们自动更新组件。

三.操作状态的组件都需要去dispatch,不利于解耦

react-redux的核心思维就是分层解耦

它对应两种组件

一种是容器组件(其实和普通组件完全不同),二是UI组件,就是我们交互的组件。

所有的状态管理以及action方法等等都在容器组件中进行,可以理解为只有容器组件可以直接和redux打交道。然后我们的容器组件会把状态以及操作redux的方法以props的形式传递给ui组件。

第一安装redux

如果上面已经安了就不需要了

npm i redux --save

安装

npm i react-redux --save

安装异步action插件

npm i redux-thunk --save

创建容器组件和容器组件,这个是ui组件和容器组件结合在一起的写法,也可以分开都行(里面api的作用写法啥的都有注释)

//用于连接ui组件
import { connect } from 'react-redux'
import { createIncrement, createDecrement, createReset, createAsycIncrement } from '../../redux/count_action'
import React, { Component } from 'react'
import store from '../../redux/store'
import Css from './index.module.css'
class CountUI extends Component {
    // componentDidMount() {
    //     //检测store状态改变 改变就走回调
    //     store.subscribe(() => {
    //         this.setState({})
    //     })
    // }
    render() {
        return (
            <div className={Css.main}>
                <div>CountRedux</div>
                <input ref={(ele) => { this.input = ele }} type="number"></input>
                <button onClick={this.add}>+</button>
                <button onClick={this.decrease}>-</button>
                <button onClick={this.judgeOdd}>increment if odd</button>
                <button onClick={this.judgeEven}>increment if even</button>
                <button onClick={() => { this.incrementAsyc(2000) }}>increment asycn</button>
                <button onClick={this.reset}>Reset</button>
                <div>Result={store.getState()}</div>
            </div>
        )
    }
    add = () => {
        this.props.increment(Number(this.input.value))
        this.input.value = ""
    }
    decrease = () => {
        this.props.decrement(Number(this.input.value))
        this.input.value = ""
    }
    judgeOdd = () => {
        if (store.getState() % 2 === 1) {
            this.add()
        }
    }
    judgeEven = () => {
        if (store.getState() % 2 === 0) {
            this.add()
        }
    }
    incrementAsyc = (delay) => {
        this.props.incrementAsyc(Number(this.input.value), delay)
        this.input.value = ""
    }
    reset = () => {
        this.props.reset(Number(this.input.value))
        this.input.value = ""
    }
}

//创建用于UI链接的容器
//需要传给UI组件的数据要用函数返回(要使用对象,因为props里需要对应的key标记),再conncet中传入
//state的这个参数其实就是store里的状态
// function mapState(state) {
//     //返回状态
//     // return {test:"test"}
//     return { count: state }
// }
// function mapDispatch(dispatch) {
//     //返回操作状态的方法
//     return {
//         increment: (data) => { dispatch(createIncrement(data)) },
//         decrement: (data) => { dispatch(createDecrement(data)) },
//         reset: (data) => { dispatch(createReset(data)) },
//         incrementAsyc: (data, delay) => { dispatch(createAsycIncrement(data, delay)) },
//     }
// }
//完整方式
// export default connect(mapState, mapDispatch)(CountUI)
//简写方式
export default connect(state => ({ count: state }),
    {
        increment: createIncrement,
        decrement: createDecrement,
        reset: createReset,
        incrementAsyc: createAsycIncrement,
    }
)(CountUI)

在index.js使用provider给容器组件提供store

//引入react核心件
import React from 'react';
import ReactDOM from 'react-dom/client';
//引入基本样式
import './index.css';
import App from './App';
// 记录页面性能
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';
import store from './redux/store';
import { Provider } from 'react-redux';
/* <React.StrictMode>用来检查写的是否合理 比如字符串的ref */
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <Provider store={store}>
        <App />
      </Provider>
    </BrowserRouter>
  </React.StrictMode>
);
//为了让组件redux修改后自动更新,但是使用react-redux的话就不需要了
// store.subscribe(() => {
//   root.render(
//     <React.StrictMode>
//       <BrowserRouter>
//         <Provider store={store}>
//           <App />
//         </Provider></BrowserRouter>
//     </React.StrictMode>
//   );
// })

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

这里解释一下为什么要传入store,因为容器组件必须给它的props里传入我们的store.js才能去操作redux,但是传入又不能直接使用import

有两种方式,一种是针对传递

import React, { Component } from 'react'
import CountContainer from '../../containers/Count'
// import store from '../../redux/store'
export default class countReactRedux extends Component {
    render() {
        return (
            <div>
                {/* <CountContainer store={store}></CountContainer> */}
                <CountContainer></CountContainer>
            </div>
        )
    }
}

就是我注释掉的那句。我们页面要展示的时候引入的是容器组件而不是ui组件,所以可以直接在容器组件标签内传值。

第二中就是使用provider包裹app组件,这样app组件中所有的容器组件都会收到store,在props中,就不需要为每个都传值了。

react-redux的一些坑

第一个就是我们看reducer文件

reducer文件会更具action来修改我们的prestate,但是如果prestate是引用数据类型(数组或对象),我们修改prestate中的属性,例如

return preState.push(1)

 然后return

我们的页面是不会更新的,这个和vue2的数据代理那个缺点挺像的,它对引用数据类型是浅比较,只比较地址,如果返回的地址没变化,就不会更新。但是vue对数组常用方法都再封装了,比如push,shift等是可以响应式的,react没有,所以我们需要修改写法。

所以这里需要写成这样

return [a,...preState]

16. Reat扩展知识

除去这些基础的功能外,react还有一些扩展的知识点。

(1) setState

注意setState为异步更新。

setState有两种写法

第一:直接修改状态

this.setState({target:newValue})

第二:修改状态加回调函数

       let { result } = this.state
        result = result + Number(this.input.value)
        this.setState({ result },()=>{
            //setState的异步回调
            console.log(this.state);
        })

(2)Lazyload 懒加载

用什么组件加载什么组件

一般用在路由组件中:

我们一开始引入组件的方式是

import HomePage from "./components/HomePage";
import Count from "./pages/Count";
import countReactRedux from "./pages/countReactRedux";

使用懒加载

import React, { lazy, Suspense } from "react";
import { Switch, Route } from "react-router-dom"
import HomePage from "./components/HomePage";
// import AntdTest from "./pages/AntdTest";
// import Count from "./pages/Count";
// import countReactRedux from "./pages/countReactRedux";
const AntdTest = lazy(() => import("./pages/AntdTest"))
const Count = lazy(() => import("./pages/Count"))
const countReactRedux = lazy(() => import("./pages/countReactRedux"))
class App extends React.Component {
  render() {
    return (
      <div>
        <Suspense fallback={<h1>404 Error</h1>}>
          <Switch>
            <Route exact={true} path="/" component={HomePage}></Route>
            <Route path="/antD" component={AntdTest}></Route>
            <Route path="/countRedux" component={Count}></Route>
            <Route path="/countReactRedux" component={countReactRedux}></Route>
          </Switch>
        </Suspense>
      </div>
    )
  }
}
export default App;

第一是引入使用lazy()

第二是在路由组件前加 <Suspense fallback=出不来显示的组件>

(3)hooks

1.statehook 让函数组件也有state

import React from 'react'

export default function StateHook() {
    const [count, setCount] = React.useState(0)
    function add() {
        let val = count + 1
        setCount(val)
    }
    return (
        <div>
            <h3>count的值为:{count}</h3>
            <button onClick={add}>+</button>
        </div>
    )
}

2.Effecthooks

作用就是检测变化,如果第二个参数不为空,就检测第二个参数里的变量,否则就是全部检测

import React from 'react'
import ReactDOM from "react-dom"
export default function EffectHook() {
    const [count, setCount] = React.useState(0)
    //检测所有人
    // React.useEffect(() => {
    //     console.log("@");
    // })
    //检测count
    // React.useEffect(() => {
    //     console.log("@");
    // }, [count])
    //didMount willunmount
    React.useEffect(() => {
        let timer = setInterval(() => {
            setCount(count=>count+1)
        }, 1000)
        return () => {
            clearInterval(timer)
        }
    }, [])

    function add() {
        setCount(count + 1)
    }
    function unmount() {
        ReactDOM.unmountComponentAtNode(document.getElementById("main"))
    }
    return (
        <div>
            <h1>count:{count}</h1>
            <button onClick={add}>+</button>
            <button onClick={unmount}>gg</button>
        </div>
    )
}

当然如果第二个参数传入空数组,那么就相当于didMount的钩子。

如果第一个函数返回一个函数,那么这个函数就相当于willUnmount的钩子

3. Refhook

就是让函数组件也有ref

const input =React.useRef()
    function show(){
        console.log(input.current.value);
    }
 return (
        <div>
            <input ref={input} type="number" />
            <button onClick={show}>show</butto>
        </div>
    )

(4) Fregment

类似于vue的template标签,这样就不会每个组件都多一层div了

import React, { Component, lazy,Fragment} from 'react'
import MyNavLink from '../../components/MyNavLink'
import { Switch, Route } from 'react-router-dom'
const StateHook = lazy(() => import("./StateHook/index"))
const EffectHook = lazy(() => import("./EffectHook/index"))
export default class Hooks extends Component {
    render() {
        return (
            <Fragment>
                <MyNavLink to="/hooks/EffectHook">EffectHook</MyNavLink>
                <MyNavLink to="/hooks/statehook">statehook</MyNavLink>
                <Switch>
                    <Route path="/hooks/statehook" component={StateHook}></Route>
                    <Route path="/hooks/EffectHook" component={EffectHook}></Route>
                </Switch>
            </Fragment>
        )
    }
}

如果参与遍历可以加key属性

如果不参与都可以用空标签

(5)错误边界

有错误就返回别的东西,没错误就返回子组件

import React, { Component } from 'react'
import Helllo from "../Hello/Hello";
import TodoList from "../TodoList";
import B0 from "../Brother/B0";
import Http from "../Http";
import Main from "../RouterTest/Main";
import MyNavLink from "../MyNavLink";
export default class HomePage extends Component {
  state = { hasError: "" }
  static getDerivedStateFromError(error) {
    return { hasError: error }
  }
  componentDidCatch(){
    console.log("出错");
  }
  render() {
    return (
      <div>
        {this.state.hasError ? <h1>出错了</h1> : <Helllo />}
        <hr></hr>
        <TodoList />
        <hr></hr>
        <Http />
        <hr />
        <B0 />
        <hr />
        <Main />
        <MyNavLink to="/antD">antD</MyNavLink>
        <MyNavLink to="/countRedux">countRedux</MyNavLink>
        <MyNavLink to="/countReactRedux">countReactRedux</MyNavLink>
        <MyNavLink to="/hooks">hooks</MyNavLink>
      </div>
    )
  }
}

猜你喜欢

转载自blog.csdn.net/weixin_47964837/article/details/124851651