React笔记(学习中......)

一、React简介

1.1 React是什么 ?

React用于构建用户界面的JavaScript库,是一个将数据渲染为HTML视图的开源JavaScript库。

1.2 为什么学React?

  1. 原生JavaScript操作DOM繁琐,效率低(DOM-API操作UI)
  2. 使用JavaScript直接操作DOM,浏览器会进行大量的重绘重排
  3. 原生JavaScript没有组件化编码方案,代码复用率低

1.3 React的特点

  1. 采用组件化模式、声明式编码,提高开发效率及组件复用率
  2. 在React Native中可以使用React语法进行移动端开发
  3. 使用虚拟DOM+优秀的Diffing算法,尽量减少与真实DOM的交互

二、React 初体验

2.1 hello_react案例

  • js库引入顺序;
  • 创建虚拟DOM时不加引号;
  • script标签为babel;
<body>
    <!-- 准备好一个“容器” -->
    <div id="test"></div>

    <!--引入react核心库  -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom,用于支持react操作DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <script type="text/babel"> /* 此处写babel,将jsx转为浏览器能够识别的js */
        // 1.创建虚拟DOM
        const VDOM = <h1>Hello,React</h1> /* 此处不写引号,因为不是字符串 */
        // 2.渲染虚拟DOM到页面
        ReactDOM.render(VDOM,document.getElementById('test'))
    </script>
</body>

2.2 关于虚拟DOM

  1. 本质时Object 类型的对象(一般对象)
  2. 虚拟DOM较“轻”,真实DOM较“重”,因为虚拟DOM是在React内部用,无需真实DOM上那么多的属性
  3. 虚拟DOM最终会被React转化为真实DOM,呈现在页面上。

2.3 jsx语法规则

  1. 定义虚拟DOM时,不要写引号
  2. 标签中混入JS表达式时要用{}
  3. 样式的类名指定不要用class,要用className
  4. 内联样式,要用style={{key:value}}的形式去写;外侧花括号代表里面要写js表达式,里侧花括号代表的是一个对象
  5. 只有一个根标签
  6. 标签必须闭合
  7. 标签首字母
    (1)若小写字母开头,则将该标签专区html中同名元素,若html中无该标签对应的同名元素,则报错
    (2)若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错
    <script type="text/babel">
        const myId = 'aYguiGu'
        const myData = 'HelLo,ReAct'
        // 1.创建虚拟DOM
        const VDOM = ( /* 此处不写引号,因为不是字符串 */
            <div>
                <h1 className="title" id={myId.toLocaleLowerCase()}>
                    <span style={{ color: 'white', fontSize: '30px' }}>{myData.toLocaleLowerCase()}</span>
                </h1>
                <h1 className="title" id={myId.toLocaleUpperCase()}>
                    <span style={{ color: 'white', fontSize: '30px' }}>{myData.toLocaleLowerCase()}</span>
                </h1>
                <input type="text" />
            </div>
        )
        // 2.渲染虚拟DOM到页面
        ReactDOM.render(VDOM, document.getElementById('test'))
    </script>

2.4 jsx练习

区分 【js语句代码】与【js表达式】

  1. 表达式:一个表达式会产生一个值,可以放在任何一个需要值得地方
    例如: (1)a (2)a+b (3)demo(1) (4)arr.map() (5)function test() {}
  2. 语句(代码)
    例如:(1)if(){} (2)for(){} (3)switch(){case:xxxx}
  <script type="text/babel">
    const data = ['Angular', 'React', 'Vue']
    // 1.创建虚拟DOM
    const VDOM = (
      <div>
        <h1>前端js框架列表</h1>
        <ul>
          {
            data.map((item, index) => {
              return <li key={index}>{item}</li>
            })
          }
        </ul>
      </div>
    )
    // 2.渲染虚拟DOM到页面
    ReactDOM.render(VDOM, document.getElementById('test'))
  </script>

2.5 模块、组件、模块化、组件化

  1. 模块:向外提供特定功能的js程序,一般就是一个js文件
    作用:复用js,简化js的编写,提高js运行效率
  2. 组件:用来实现局部功能效果的代码和资源的集合
    作用::复用编码,简化项目编码,提高运行效率
  3. 模块化:当应用的js都以模块来编写的,这个应用就是一个模块化的应用
  4. 组件化:当应用是以多组件的方式实现,这个应用就是一个组件化的应用。

三、React面向组件编程

3.1.安装React开发者工具

3.2 基本理解和使用

3.2.1 react中定义函数式组件

  <script type="text/babel">
    // 1. 创建函数式组件
    function MyComponent() {
      console.log(this); //此处的this时undefined,因为babel编译后开起来严格模式
      return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
    }
    // 2. 渲染组件到页面
    ReactDOM.render(<MyComponent/>,document.getElementById('test'))
  </script>

执行了ReactDOM.render(,document.getElementById(‘test’))之后,发生了什么?

  1. React解析组件标签,找到了MyComponent组件
  2. 发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。

3.2.2 类的基本知识(复习)

  1. 类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写
  2. 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的
  3. 类中所定义的方法,都是放在了类的原型对象上,供实例去使用
    <script type="text/javascript">
        // 创建一个Person类
        class Person {
        	//构造器方法
            constructor(name, age) {
            	//	构造器中的this是谁?————类的实例对象
                this.name = name
                this.age = age
            }
            speak() {
                // speak方法放在了哪里?————类的原型对象上,供实例使用
                // 通过Person实例调用speak时,speak中的this就是Person实例
                console.log(`我叫${this.name},我的年龄是${this.age}`);
            }
        }

        // 创建一个Student类,继承于Person类
        class Student extends Person {
            constructor(name, age, grade) {
                super(name, age)
                this.grade = grade
                this.school = '翻斗幼儿园'
            }
            // 重写从父类继承过来的方法
            speak() {
                console.log(`我叫${this.name},我的年龄是${this.age}岁,我读的是${this.grade}年级`);
            }
            study() {
                //study方法放在了哪里?————类的原型对象上,供实例使用
                // 通过Person实例调用study时,study中的this就是Person实例
                console.log('good good study,day day up!');
            }
        }

        const s1 = new Student('胡图图',3,'小一班')
        console.log(s1);
        s1.speak()
        s1.study()
    </script>

在这里插入图片描述
3.2.3 类式组件

  <script type="text/babel">
    // 1. 创建类式组件
    class MyComponent extends React.Component {
      render() {
        // render是放在哪里的?————MyComponent的原型对象上,供实例使用
        // render中的this是谁?————MyComponent的实例对象 <==> MyComponent组件实例对象
        console.log('render中的this', this);
        return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
      }
    }
    // 2. 渲染组件到页面
    ReactDOM.render(<MyComponent />, document.getElementById('test'))
  </script>

执行了ReactDOM.render(,document.getElementById(‘test’))之后,发生了什么?

  1. React解析组件标签,找到了MyComponent组件
  2. 发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render’方法
  3. 将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
    在这里插入图片描述

3.3 state属性

3.3.1 理解

  1. state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)
  2. 组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)

3.3.2 state基本使用

    <script type="text/babel">
        // 1. 创建类式组件
        class Weather extends React.Component {
            // 构造器调用几次?————1次
            constructor(props) {
                console.log('constructor');
                super(props)
                // 初始化状态
                this.state = { isHot: false,wind:'微风' }
                // 解决changeWeather中this指向问题
                this.changeWeather0 = this.changeWeather.bind(this) 
                //bind之后,this由undefined变为==>Weather类组件的实例对象;然后将左侧新的函数放到了实例自身,即this,并取新名字为changeWeather0 
            }
            // render调用几次?————1+n次,1是初始化的那次,n是状态更新的次数
            render() {
                console.log('render');
                // 读取状态
                const { isHot,wind } = this.state
                return <h1 onClick={ this.changeWeather0 }>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
            }
            // changeWeather调用几次?————点几次,调用几次
            changeWeather(){
                // changeWeather放在哪里?————Weather的原型对象上,供实例使用
                // 由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
                // 类中的方法默认开起来局部的严格模式,所以changeWeather中的this为undefined
               
                console.log('changeWeather');
                // 获取原来的isHot值
                const isHot = this.state.isHot
                // 【注意】:状态必须通过setState进行更新,且更新是一种合并,不是替换
                this.setState({isHot:!isHot})

                // 状态(state)不可直接更改,下面是错误的写法
                // this.state.isHot = !isHot
            }
        }
        // 2. 渲染组件到页面
        ReactDOM.render(<Weather />, document.getElementById('test'))

    </script>

3.3.3 state简写形式

    <script type="text/babel">
        // 1. 创建类式组件
        class Weather extends React.Component {
            state = { isHot: false, wind: '微风' }
            render() {
                // 读取状态
                const { isHot, wind } = this.state
                return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
            }

            // changeWeather现在是Weather实例对象上的一个属性
            // 自定义方法————赋值语句+箭头函数
            changeWeather = () => {
                const isHot = this.state.isHot
                this.setState({ isHot: !isHot })
            }
        }
        // 2. 渲染组件到页面
        ReactDOM.render(<Weather />, document.getElementById('test'))
    </script>

【注意】:
1)组件中render方法中的this为组件实例对象
2)组件自定义的方法中this为undefined,如何解决? a. 强制绑定this:通过函数对象的bind() b. 箭头函数
3)状态数据,不能直接修改或更新

3.4 props属性

3.4.1 理解

  1. 每个组件对象都会有props(properties的简写)属性
  2. 组件标签的所有属性都保存在props中

3.4.2 作用

  1. 通过标签属性从组件外向组件内传递变化的数据
  2. 注意:组件内部不要修改props数据

3.4.3 组件实例中使用props

    <script type="text/babel">
        // 创建组件
        class Person extends React.Component {
            constructor(props){
            // 构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
                super(props)
                console.log('constructor',this.props);
            }
            
            /*static 静态方法;该形式将propTypes添加到Person身上*/
            // 对标签属性进行类型、必要性的限制
            static propTypes = {
                name: PropTypes.string.isRequired, //限制name必传,且为字符串
                sex: PropTypes.string, //限制sex为字符串
                age: PropTypes.number, //限制age为数值
            }
            // 指定默认标签属性值
            static defaultProps = {
                sex: '男', //sex默认值为男
                age: 18, //age默认值为18
            }

            render() {
                console.log(this);
                const { name, age, sex } = this.props
                return (
                    <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age + 1}</li>
                    </ul>
                )
            }
        }
        // 渲染组件到页面
        ReactDOM.render(<Person name="jerry" sex="女"  />, document.getElementById('test1'))        
    </script>

3.4.4 函数组件使用props

    <script type="text/babel">
        // 创建组件
        function Person(props) {
            const { name, age, sex } = props
            return (
                <ul>
                    <li>姓名:{name}</li>
                    <li>性别:{sex}</li>
                    <li>年龄:{age}</li>
                </ul>
            )
        }
        Person.propTypes = {
            name: PropTypes.string.isRequired,
            sex: PropTypes.string, 
            age: PropTypes.number, 
        }
        Person.defaultProps = {
            sex: '男', 
            age: 18, 
        }
        // 渲染组件到页面
        ReactDOM.render(<Person name="jerry" sex="女" />, document.getElementById('test1'))
    </script>

3.5 ref属性

3.5.1 字符串形式的ref

    <script type="text/babel">
        // 1. 创建类式组件
        class Demo extends React.Component {
            // 展示左侧输入框的数据
            showData = () => {
                const {input1} = this.refs
                alert(input1.value)
            }
             // 展示右侧输入框的数据
             showData2 = () => {
                const {input2} = this.refs
                alert(input2.value)
            }
            render() {
                return (
                    <div>
                        <input ref="input1" type="text" placeholder="点击按钮提示数据" />&nbsp;
                        <button onClick={this.showData}>点我提示左侧数据</button>&nbsp;
                        <input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />&nbsp;
                    </div>
                )
            }
        }
        // 2. 渲染组件到页面
        ReactDOM.render(<Demo/>, document.getElementById('test'))
    </script>

3.5.2 回调形式的ref

    <script type="text/babel">
        // 1. 创建类式组件
        class Demo extends React.Component {
            // 展示左侧输入框的数据
            showData = () => {
                const { input1 } = this
                alert(input1.value)
            }
            // 展示右侧输入框的数据
            showData2 = () => {
                const { input2 } = this
                alert(input2.value)
            }
            render() {
                return (
                    <div>
                        <input ref={(currentNode) => { this.input1 = currentNode }} type="text" placeholder="点击按钮提示数据" />&nbsp;
                        <button onClick={this.showData}>点我提示左侧数据</button>&nbsp;
                        <input ref={c => this.input2 = c} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />&nbsp;
                    </div>
                )
            }
        }
        // 2. 渲染组件到页面
        ReactDOM.render(<Demo />, document.getElementById('test'))
    </script>

3.5.3 createRef
React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器只能存储一个节点

    <script type="text/babel">
        // 1. 创建类式组件
        class Demo extends React.Component {
            myRef = React.createRef()
            myRef2 = React.createRef()

            // 展示左侧输入框的数据
            showData = () => {
               alert(this.myRef.current.value)
            }
            // 展示右侧输入框的数据
            showData2 = () => {
                alert(this.myRef2.current.value)
            }
            render() {
                return (
                    <div>
                        <input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />&nbsp;
                        <button onClick={this.showData}>点我提示左侧数据</button>&nbsp;
                        <input ref={this.myRef2} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />&nbsp;
                    </div>
                )
            }
        }
        // 2. 渲染组件到页面
        ReactDOM.render(<Demo />, document.getElementById('test'))
    </script>

3.6 高阶函数柯里化

  1. 高阶函数:如果一个函数符合下面2个规范中的任何一个,那么该函数就是高阶函数
    (1)若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数
    (2)若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数
    常见的高阶函数:Promise、setTimeout、arr.map()等等
  2. 函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
    <script type="text/babel">
        // 1. 创建类式组件
        class Login extends React.Component {
            state = {
                username: '',
                password: ''
            }
            // 保存数据到表单
            saveFormData = (dataType) => {
                return (event) => {
                    this.setState({ [dataType]: event.target.value })
                }
            }
            handleSubmit = (event) => {
                event.preventDefault();
                const { username, password } = this.state
                alert(`用户名:${username},密码:${password}`)
            }
            render() {
                return (
                    <form onSubmit={this.handleSubmit}>
                        用户名:<input onChange={this.saveFormData('username')} type="text" name="username" />
                        密码:<input onChange={this.saveFormData('password')} type="text" name="password" />
                        <button>登录</button>
                    </form>
                )
            }
        }
        // 2. 渲染组件到页面
        ReactDOM.render(<Login />, document.getElementById('test'))
    </script>

3.7 生命周期函数

3.7.1 旧版生命周期过程
在这里插入图片描述
1. 挂载组件时执行顺序:

构造器(constructor)–> 组件将要挂载(componentWillMount)–> 组件渲染(render)—> 组件挂载完毕(componentDidMount)

页面加载完毕,控制打印如下结果:

在这里插入图片描述
2. 组件状态更新执行顺序

组件是否应该更新(shouldComponentUpdate)–> 组件将要更新(componentwillUpdate)–> 组件渲染(render) --> 组件更新完毕(componentDidUpadte)

shouldComponentUpdate默认返回true

页面加载完毕后,点击“点我+1”按钮,控制台打印结果如下:
在这里插入图片描述
3. 强制更新执行顺序

组件将要更新(componentwillUpdate)–> 组件渲染(render) --> 组件更新完毕(componentDidUpadte)

forceUpdate()和setState是react的api,不询问是否应该更新(shouldComponentUpdate)

点击红色框内按钮,强制更新组件:
在这里插入图片描述

代码如下:

  <script type="text/babel">
    // 1. 创建类式组件
    class Count extends React.Component {

      constructor(props) {
        console.log('Count---constructor');
        super(props)
        // 初始化状态
        this.state = { count: 0 }
      }

      // 加1按钮的回调
      add = () => {
        const { count } = this.state
        this.setState({ count: count + 1 })
      }
      // 卸载组件按钮的回调
      death = () => {
        ReactDOM.unmountComponentAtNode(document.getElementById('test'))
      }
      // 强制更新按钮的回调
      force = () => {
        this.forceUpdate()
      }
      // 组件将要挂载的钩子
      componentWillMount() {
        console.log('Count---componentWillMount');
      }
      // 组件挂载完毕的钩子
      componentDidMount() {
        console.log('Count---componentDidMount');
      }
      // 组件将要卸载的钩子
      componentWillUnmount() {
        console.log('Count---componentWillUnmount');
      }

      // 控制组件更新的“阀门”
      shouldComponentUpdate() {
        console.log('Count---shouldComponentUpdate');
        return true
      }
      // 组件将要更新的钩子
      componentWillUpdate() {
        console.log('Count---componentWillUpdate');
      }
      // 组件更新完毕的钩子
      componentDidUpdate() {
        console.log('Count---componentDidUpdate');
      }


      render() {
        console.log('Count---render');
        const { count } = this.state
        return (
          <div>
            <h2>当前求和为:{count}</h2>
            <button onClick={this.add}>点我+1</button>
            <button onClick={this.death}>卸载组件</button>
            <button onClick={this.force}>不更改任何状态中的参数,强制更新</button>
          </div>
        )
      }
    }
    // 2. 渲染组件到页面
    ReactDOM.render(<Count />, document.getElementById('test'))
  </script>

4. 父组件render

子组件将要接收新的props(componentWillReceiveProps)–> 组件是否应该更新(shouldComponentUpdate)–> 组件将要更新(componentwillUpdate)–> 组件渲染(render) --> 组件更新完毕(componentDidUpadte)

componentWillReceiveProps: 只有父组件状态发生改变重新调用render, 才会调用子组件的componentWillReceiveProps钩子,父组件第一次渲染时,子组件的这个钩子不会调用
第一次渲染:
在这里插入图片描述

重新render :
在这里插入图片描述

  <script type="text/babel">
    class A extends React.Component {
      state = { carName: '奔驰' }
      changeCar = () => {
        this.setState({ carName: '宝马' })
      }

      render() {
        return (
          <div>
            <div>我是A组件</div>
            <button onClick={this.changeCar}>换车</button>
            <B carName={this.state.carName} /> {/*给B组件传数据*/}
          </div>
        )
      }
    }

    class B extends React.Component {
      // 组件将要接收新的props的钩子
      componentWillReceiveProps(props) {
        console.log('B---componentWillReceiveProps', props);
      }
      // 控制组件更新的“阀门”
      shouldComponentUpdate() {
        console.log('B---shouldComponentUpdate');
        return true
      }
      // 组件将要更新的钩子
      componentWillUpdate() {
        console.log('B---componentWillUpdate');
      }
      // 组件更新完毕的钩子
      componentDidUpdate() {
        console.log('B---componentDidUpdate');
      }
      render() {
        console.log('B---render');
        return (
          <div>
            {/*B组件可以通过props拿到数据*/}
            <div>我是B组件,接收到的车是:{this.props.carName}</div>
          </div>
        )
      }
    }

    // 2. 渲染组件到页面
    ReactDOM.render(<A />, document.getElementById('test'))
  </script>

总结

  1. 初始化阶段:由ReactDOM.render()触发—初次渲染
    1. constructor()
    2. componentWillMount()
    3. render()
    4. componentDidMount() ===> 常用
  2. 更新阶段:由组件内部this.setState()或父组件render触发
    1. shouldComponentUpdate()
    2. componentWillUpdate()
    3. render() ===> 必须使用
    4. componentDidUpdate()
  3. 卸载组件:由ReactDOM.unmountComponentAtNode()触发
    1. componentWillUnmount() ===> 常用 一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息等
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值