React

简介

React是Facebook开发并开源的前端框架
用来假设Instagram
React解决的是前端MVC框架中View视图层的问题

Virtual DOM

DOM(文档对象模型Document Object Model)
在这里插入图片描述
在这里插入图片描述

  • 将网页内所有内容映射到一颗数型结构的层级对象模型上,游览器提供对DOM的支持,用户可以是用脚本调用DOMAPL来动态的修改DOM结点,从而达到修改网页的目的,这种修改是游览器中完成,游览器会根据DOM的改变重绘改变DOM结点部分
  • 修改DOM重新渲染代价太高,前端框架为了提高效率,尽量减少DOM的重绘,提出了Virtual DOM,Virtual DOM是一个JavaScript对象,性能更高,所有的修改都是先生成一个新的Virtual DOM,通过比较算法比较新旧Virtual DOM,得到差异化Virtual DOM,将这部分差异更新到游览器DOM,游览器只需要渲染这部分变化就行了。React实现了DOM DIff算法可以高效比对Virtual DOM
支持JSX语法

后续代码需要以测试文件为基础
文件获取:https://pan.baidu.com/s/15pG9w2awFVsNZTGljSiU-A
提取码:d1fj
JSX是一种JavaScript和XML混写的语法,是JavaScript的扩展
测试程序
替换/src/index.js为下面的代码

import React from 'react';
import ReactDom from 'react-dom';
 
class Root extends React.Component{
	render(){
		return <div> Hello python</div>;
	}
}
ReactDom.render(<Root/>,document.getElementById('root'))

保存文件后,会自动编译,并重新装载刷新游览器端页面
在这里插入图片描述

  • 程序解释
    import React from ‘react’; 导入react模块
    import ReactDom from ‘react-dom’;导入react的DOM模块。
    class Root extends React.Component组件类定义,从React.Component类上继承,这个类生成JSXElement对象即React元素
    render()渲染函数,返回组件中渲染的内容,注意,只能返回唯一一个顶级元素。
    ReactDom.render(,document.getElementById(‘root’));第一个参数是JSXElement对象,第二个是DOM的Element元素,将React元素添加到DOM的Element元素中并渲染。
    还可以使用React.createElement创建react元素,第一参数是React组件或者一个HTML的标签名称

修改后代码

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

class Root extends React.Component{
    render(){
        // return <div> hello python </div>;
        return React.createElement('div',null,'hello')
    }
}
// ReactDom.render(<Root />,document.getElementById('root'));
ReactDom.render(React.createElement(Root),document.getElementById('root'))

增加一个元素

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

class Child extends React.Component{
    render(){
        return  <div>Child</div>
    }
}

class Parent extends React.Component{
    render(){
        return(
            <div>
                <h2>this is Parent</h2>
            <br />
            <Child />
            </div>
        )
    }
}
ReactDom.render(<Parent />,document.getElementById('root'))

在这里插入图片描述
注意:
1、React组件的render函数return,只能是一个顶级元素。
2、JSX语法是XML,要求所有元素必须闭合,注意
不能写成

JSX规范
  • 约定标签中首字母小写就是写html标记,首字母大写就是组件
  • 要求严格的HTML标记,要求所有标签都必须闭合,br也应该写成
    ,/前留空格
  • 单行省略小括号,多行需要使用小括号
  • 元素有嵌套,建议多行,注意缩进
  • JSX表达式:表达式使用{}括起来,如果大括号内使用了引号,会当做字符串处理。
组件状态state

每一个React组件都有一个状态属性state,它是一个JavaScript对象,可以为它定义属性来保存值。
如果状态变化了,会触发UI的重新渲染,使用setState()方法可以修改state值
注意:state是每个组件自己内部使用的,是组件自己的属性
修改 /src/index.js

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

class Root extends React.Component{
    // 定义一个对象
    state={p1:'string',p2:'1111'}
    render(){
        // this.state.p1='python' //可以修改属性值
        // this.setState({p1:'python'})//不可以对还在更新中的state使用setState
        setTimeout(()=>this.setState({p1:'python123'}),5000)
        return (
            <div>
                <div>this is {this.state.p1}{this.state.p2}</div>
            <br />
            </div>
        )
    }
}
ReactDom.render(<Root />,document.getElementById('root'))

如果将this.state.p1='python’改成this.setState({p1:'python})就会发出警告,可以是使用延迟函数setTimeout(()=>this.setState({p1:‘python123’}),5000)即可。

HTML DOM的JavaScipt事件

在这里插入图片描述

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

class Toggle extends React.Component{
    state={flag:true}
    handleClick(event){
        console.log(event.target.id)
        console.log(event.target===this)
        console.log(this)
        console.log(this.state)
        this.setState({flag:!this.state.flag})
    }
    render(){
        return <div id='t1' onClick={this.handleClick.bind(this)}>
            点击触发一个事件{this.state.flag.toString()}
        </div>
    }
}

class Root extends React.Component{
    render(){
        return(
            <Toggle />
        )
     
    }
}
ReactDom.render(<Root />,document.getElementById('root'))

在这里插入图片描述
分析
Toggle类
它有自己的state属性。当render完成后,网页上有一个div标签,div标签对象捆绑了一个click事件的处理函数,div标签内有文本内容,如果通过点击左键,就触发一个click方法关联的handleClick函数,在这个函数里将状态值改变
状态值state的改变将引发render重绘
如果组件自己的state变了,只会触发自己的render方法重绘
注意:
{this.handleClick.bind(this)},不能外加引号
this.handleClick.bind(this)一定要绑定this,否则当触发捆绑的函数时,this是函数执行的上下文决定的,this已经不是触发事件的对象了
console.log(event.target.id),取消的产生事件的对象的id,但是这不是封装的对象console.log(event.target===this)是false。所以这里一定要用this,而这个this是通过绑定来的。
this写在类中,始终指的是React组件实例本身。
React中的事件

  • 使用小驼峰命名
  • 使用JSX表达式,表达式中指定事件处理函数
  • 不能使用return false,如果要阻止事件默认行为,使用event.preventDefault()
属性props

props就是组件属性properties
把React组件当做标签使用,可以为其增加属性
/<Toggle name=‘school’,parent={this} />
为上面的Toggle元素增加属性:
1、name=‘school’,这个属性会作为一个单一的对象传递给组件,加入到组件的props属性中
2、parent={this},注意这个this是在Root元素中,指的是Root组件本身
3、在Root中为使用JSX语法为Toggle增加子元素,这些子元素也会被加入到Toggle组件的props.children中

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

class Toggle extends React.Component{
    state={flag:true} //类中定义state
    handleClick(event){
        console.log(event.target.id)
        console.log(event.target===this)
        console.log(this)
        console.log(event)
        console.log(this.state)
        this.setState({flag:!this.state.flag})
    }
    render(){/*一定要绑定this,onClick写成小驼峰*/
        return <div id='t1' onClick={this.handleClick.bind(this)}>
            点击触发一个事件{this.state.flag.toString()} <br  />
            显示props<br />
            {this.props.name}:{this.props.parent.state.p1} {this.props.parent.state.p2}
            {this.props.children}
        </div>
    }
}

class Root extends React.Component{
    state={p1:'python',p2:'good'}
    render(){
        return(
            <Toggle name='school' parent={this}>
            {/*自定义2个属性通过props传给Toggle组件对象*/}
            <hr />
            <span> Toggle元素</span>    
            </Toggle>
        )
     
    }
}
ReactDom.render(<Root />,document.getElementById('root'))

尝试修改props中的属性值,会抛出Cannot assign to read only property ‘name’ of object '#
'异常,也就是说props在组件内部不能修改,只读。

  • 应该说,state是私有private的属于组件自己的属性,组件外无法直接访问嘛,可以修改state,但是建议使用setState方法
  • props是公有public属性,组件外也可以访问,但组件内只读
  • props是一种组件外部向组件内部传入数据的一种方式,只不过采用标签属性的方式
构造器constructor

使用ES6的构造器,要提供一个参数props,并把这个参数使用super传递给父类

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

class Toggle extends React.Component{
    // state={flag:true} //类中定义state
    constructor (props){
    //构造器部分
        super(props)
        this.state={flag:true}
    }
    handleClick(event){
        console.log(event.target.id)
        console.log(event.target===this)
        console.log(this)
        console.log(event)
        console.log(this.state)
        this.setState({flag:!this.state.flag})
    }
    render(){/*一定要绑定this,onClick写成小驼峰*/
        return <div id='t1' onClick={this.handleClick.bind(this)}>
            点击触发一个事件{this.state.flag.toString()} <br  />
            显示props<br />
            {this.props.name}:{this.props.parent.state.p1} {this.props.parent.state.p2}
            {this.props.children}
        </div>
    }
}
class Root extends React.Component{
    state={p1:'python',p2:'good'}
    render(){
        return(
            <Toggle name='school' parent={this} >
            {/*自定义2个属性通过props传给Toggle组件对象*/}
            <hr />
            <span> Toggle元素</span>    
            </Toggle>
        )     
    }
}
ReactDom.render(<Root />,document.getElementById('root'))
组件的声明周期

组件的生命周期可分为三个状态:

  • Mounting:已插入真实DOM
  • Updating:正在被重新渲染
  • Unmounting:已移除真实DOM
    组件的生命周期状态,说明在不同时机访问组件,组件正处在生命周期的不同状态上。在不同的生命周期状态访问,就产生了不同的方法。

在这里插入图片描述

生命周期的方法如下:

  • 装载组件触发

    • componentWillMount在渲染前调用,在客户端也在服务端,只会在装载之前调用一次
    • componenDidMount:在第一次渲染后调用,只在客户端,之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来访问。如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout,setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。只在装载完成后调用一次,在render之后
  • 更新组件触发,这些方法不会在首次render组件的周期调用

    • componentWillReceiveProp(nextProps)在组件接收到一个新的prop时被调用。这个方法在初始化render时不会被调用
    • shouldComponentUpdate(nextProps,nextState)返回一个布尔值,在组件接收到新的props或者state时被调用,在初始化或者使用forceUpdate时不被调用
      • 可以在确认不需要更新组件时使用
      • 如果设置为false,就是不允许更新组件,那么componentWillUpdate、componentDidUpdate不会执行
  • componentWillUpdate(nextProps,nextState)在组件接收到新的props或者state但还没有render时被调用,在初始化时不会被调用

  • componentDidUpdate(nextProps,nextState)在组件完成更新后立即调用。在初始化时不会被调用。

  • 卸载组件触发

    • componentWillUnmount在组件从DOM中移除的时候后立即被调用
      由图可知
      constructor构造器是最早执行的函数
      组件构建好之后,如果更新组件的state或props(注意在组件内props是只读),就会在render渲染前触发一系列的更新生命周期函数
import React from 'react';
import ReactDom from 'react-dom';

class Sub extends React.Component{
    constructor(props){
        console.log('Sub constructor')
        super(props)
        this.state={count:0}
    }

    handleClick(event){
        this.setState({count:this.state.count+1})
    }

    render(){
        console.log('Sub render')
        return (<div id="sub" onClick={this.handleClick.bind(this)}>
            Sub's count={this.state.count}
            </div>)
    }

    componentWillMount(){
        // constructor之后,第一次render之前
        console.log('sub componentWillMount')
    }

    componentDidMount(){
        //第一次render之后
        console.log('Sub componentDidMount')
    }

    componentWillUnmount(){
        //清理工作
        console.log('Sub componentWillUnmount')
    }
}

class Root extends React.Component{
    constructor(props){
        super(props) // 调用父类构造器
        console.log('Root Constructor')
        //定义一个对象
        this.state={}
    }
    render(){
        return (
            <div>
                <Sub />
            </div>
        )
    }
}
ReactDom.render(<Root />,document.getElementById('root'))

在这里插入图片描述
上面可以看到顺序是
constructor -> componentWillMount -> render -> componentDidMount ----state或props改变----> render

增加更新组件函数

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

class Sub extends React.Component{
    constructor(props){
        console.log('Sub constructor')
        super(props)//调用父构造器
        this.state={count:0}
    }

    handleClick(event){
        this.setState({count:this.state.count+1})
    }

    render(){
        console.log('Sub render')
        return (<div id="sub" onClick={this.handleClick.bind(this)}>
            Sub's count={this.state.count}
            </div>)
    }

    componentWillMount(){
        // constructor之后,第一次render之前
        console.log('sub componentWillMount')
    }

    componentDidMount(){
        //第一次render之后
        console.log('Sub componentDidMount')
    }

    componentWillUnmount(){
        //清理工作
        console.log('Sub componentWillUnmount')
    }

    componentWillReceiveProps(nextProps){
        //props变更时,接到新props了,交给shouldComponentUpdate
        //props组件内只读,只能从外部改变
        console.log(this.props)
        console.log(nextProps)
        console.log('Sub componentWillReceiveProps',this.state.count)
    }

    shouldComponentUpdate(nextProps,nextState){
        //是否组件更新,props或state方式改变时,返回布尔值,true才会更新
        console.log('Sub shouldComponentUpdate',this.state.count,nextState)
        return true //return false将拦截更新
    }
    componentWillUpdate(nextProps,nextState){
        //同意更新后,真正更新前,之后调用render
        console.log('Sub componentWillUpdate',this.state.count,nextState)
    }

    componentDidUpdate(prevProps,prevState){
        // 同意更新后,真正更新后,在render之后调用
        console.log('Sub componentDidUpdate',this.state.count,prevState)
    }
}

class Root extends React.Component{
    constructor(props){
        super(props) // 调用父类构造器
        console.log('Root Constructor')
        //定义一个对象
        this.state={flag:true,name:'root'}
    }
    handleClick(event){
        this.setState(
            {
                flag:!this.state.flag,
                name:this.state.flag?this.state.name.toLowerCase():this.state.name.toUpperCase()
            }
        )
    }
    render(){
        return (
            <div id="app" onClick={this.handleClick.bind(this)}>
                My Name is {this.state.name}
                <hr />
                <Sub />
            </div>
        )
    }
}
ReactDom.render(<Root />,document.getElementById('root'))

在这里插入图片描述

  • componentWillMount第一次装载,在首次render之前,例如控制state、props

  • conponentDidMount第一次装载结束,在首次render之后。例如控制state、props

  • componentWillReceiveProps在组件内部,props是只读不可变的,但是这个函数可以接收到新的props,可以对props做一些处理,componentWillReceiveProps触发,也会继续执行shouldComponentUpdate

  • shouldComponentUpdate判断是否需要组件更新,就是是否render,精确的控制渲染,提高性能

  • componentWillUpdate在除了首次render外,每次render前执行,componentDidUpdate在render之后调用。

不过,大多数情况下,用不上这些函数,这些钩子函数是为了精确的控制
修改Root组件render中的这一句为

,双击子控件不会重绘

如果子组件和父组件使用了相同的事件,可以认为点击子组件也是点击父组件,父组件重绘,就会把子组件props更新,引起子组件更新流程。

函数式组件

React从15.0开始支持函数式组件

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

function Root(props){
    return <div>{props.schoolName}</div>
}

ReactDom.render(<Root schoolName="python"/>,document.getElementById('root'))

开发中,很多情况下,组件很简单,不需要使用state状态,也不需要使用生命周期函数。
函数式组件的函数应该提供一个参数props,返回一个React元素
函数式组件的函数本身就是render函数

改写

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

let Root=props=>{return <div>{props.schoolName}</div>}
ReactDom.render(<Root schoolName="python"/>,document.getElementById('root'))

以前函数式组件还有个名字叫stateless components无状态组件,当前React发布了16.8,已经可以在函数式组件中使用state了,所以官方建议叫函数式组件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值