• chapter2.2、react技术


    React

    简介

    React是Facebook开发并开源的前端框架。

    当时他们的团队在市面上没有找到合适的MVC框架,就自己写了一个Js框架,用来架设Instagram(图片分享社交网络)。2013年React开源。

    React解决的是前端MVC框架中的View视图层的问题。

    Virtual DOM

    DOM(文档对象模型Document Object Model)

    文档

    <html>
        <body>
            <h1>Title</h1>
         <p>A <em>word</em></p> </body> </html>

    将网页内所有内容映射到一棵树型结构的层级对象模型上,浏览器提供对DOM的支持,用户可以是用脚本调用DOM API来动态的修改DOM结点,从而达到修改网页的目的,这种修改是浏览器中完成,浏览器会根据DOM的改变重绘改变的DOM结点部分。

    修改DOM重新渲染代价太高,前端框架为了提高效率,尽量减少DOM的重绘,提出了Virtual DOM,所有的修改都是现在Virtual DOM上完成的,通过比较算法,找出浏览器DOM之间的差异,使用这个差异操作DOM,浏览器只需要渲染这部分变化就行了。

    React实现了DOM Diff算法可以高效比对Virtual DOM和DOM的差异。

     支持JSX语法

     JSX是一种JavaScript和XML混写的语法,是JavaScript的扩展。

    React.render(
        <div>
            <div>
                <div>content</div>
            </div>
        </div>,
    document.getElementById('example')
    );        

    测试程序

     替换 /src/index.js 为下面的代码

    import React from "react";
    import ReactDom from "react-dom";
    
    class Root extends React.Component{
        render() {
            return <div>Hello World</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(<Root/>, document.getElementById('root')); 第一个参数是JSXElement对象,第二个是DOM的Element元素。将React元素添加到DOM的Element元素中并渲染。

    还可以使用React.createElement创建react元素,第一参数是React组件或者一个HTML的标签名称(例如div、span)。

    return React.createElement('div', null, 'why are you so cool.');
    ReactDom.render(React.createElement(Root), document.getElementById('root'));

    改写的代码为

    import React from "react";
    import ReactDom from "react-dom";
    
    class Root extends React.Component{
        render() {
            // return <div>Hello World</div>;
            return React.createElement('div', null, 'why are you so cool.');
        }
    }
    
    // ReactDom.render(<Root/>,document.getElementById("root"));
    ReactDom.render(React.createElement(Root),document.getElementById("root"));

    使用JSX更简洁易懂,推荐使用JSX语法。

    增加一个子元素

    import React from "react";
    import ReactDom from "react-dom";
    
    class Sub extends React.Component{
        render() {
            return <div>Sub content</div>
        }
    }
    
    class Root extends React.Component{
        render() {
            return (<div>
                <h2>hello, I'm Riper</h2>
                <br />
                <Sub />
            </div>);
        }
    }
    
    ReactDom.render(<Root/>,document.getElementById("root"));

    注意:

    1、React组件的render函数return,只能是一个顶级元素

    2、JSX语法是XML,要求所有元素必须闭合,注意 <br /> 不能写成 <br>

     JSX规范

    • 约定标签中首字母小写就是html标记,首字母大写就是组件
    • 要求严格的HTML标记,要求所有标签都必须闭合。br也应该写成 <br /> ,/前留一个空格。
    • 单行省略小括号,多行请使用小括号
    • 元素有嵌套,建议多行,注意缩进
    • JSX表达式:表达式使用{}括起来,如果大括号内使用了引号,会当做字符串处理,例如 <div>{'2>1?true:false'}</div> 里面的表达式成了字符串了

     组件状态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 : "hello ",
            p2 : "world."
        };
        render () {
            this.state.p1 = "where is "; //不推荐在render中修改state
            // this.setState({p1: "where is"}) // 递归了,不可以对还在更新中的state使用setState
            console.log("!!!!!!!!!!!!!!!!") 
            // setTimeout(() => this.setState({p1:"where is "}),5000) 
            //控制台会每隔五秒打印一次!!!!!!!!!!!,因为setState会导致render重绘,这里还是会循环
            return (
                <div>
                    <div>this say {this.state.p1}{this.state.p2}</div>   
                </div>);
        }
    }
    
    ReactDom.render(<Root />,document.getElementById("root"));

    如果将 this.state.p1 = 'where is ' 改为 this.setState({p1:'where is '}); 就会出警告。

    可以使用延时函数 setTimeout(() => this.setState({ p1: 'where is ' }), 5000); 即可。如果网页需要每一段时间更新一次,也可以使用该方法。

    import React from "react"
    import ReactDom from "react-dom"
    
    class Root extends React.Component{
        state = {
            count : 0
        };
    
        handleClick(event) {
            console.log(event)
            console.log(event.target);
            this.setState({'count': ++this.state.count})
        };
        render () {
            return (
                <div onClick={this.handleClick.bind(this)}> // 绑定,否则会有传递的this的坑
                    {this.state.count}
                </div>);
        }
    }
    
    ReactDom.render(<Root />,document.getElementById("root"));

     复杂状态

    将脚本放在网页中,传统网页实现

    <html>
    
    <head>
        <script type="text/javascript">
            function getEventTrigger(event) {
                x = event.target; // 从事件中获取元素
                alert("触发的元素的id是:" + x.id);
            }
        </script>
    </head>
    
    <body>
        <div id="t1" onmousedown="getEventTrigger(event)">
            点击这句话,会触发一个事件,并弹出一个警示框
        </div>
    </body>
    
    </html>

    div的id是t1,鼠标按下事件捆绑了一个函数,只要鼠标按下就会触发调用getEventTrigger函数,浏览器会送给它一个参数event。event是事件对象,当事件触发时,event包含触发这个事件的对象。

    HTML DOM的JavaScript事件

    属性     此事件发生在何时
    onabort     图像的加载被中断
    onblur     元素失去焦点
    onchange     域的内容被改变
    onclick     当用户点击某个对象时调用的事件句柄
    ondblclick     当用户双击某个对象时调用的事件句柄
    onerror     在加载文档或图像时发生错误
    onfocus     元素获得焦点
    onkeydown     某个键盘按键被按下
    onkeypress     某个键盘按键被按下并松开
    onkeyup     某个键盘按键被松开
    onload     一张页面或一幅图像完成加载
    onmousedown     鼠标按钮被按下
    onmousemove     鼠标被移动
    onmouseout     鼠标从某元素移开
    onmouseover     鼠标移到某元素之上
    onmouseup     鼠标按键被松开
    onreset     重置按钮被点击
    onresize     窗口或框架被重新调整大小
    onselect     文本被选中
    onsubmit     确认按钮被点击
    onunload     用户退出页面

    使用React实现上面的传统的HTML

    import React from 'react';
    import ReactDom from 'react-dom';
    
    class Toggle extends React.Component {
        state = {flag : true};
        handleClick(event) {
            console.log(event.targer === this, event.target.id);
            console.log(this, 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 {
        state = {p1: "Are you kidding me"}
        render () {
            setTimeout(() => this.setState({p1: "Yes!"}),5000)
            return (
                <div>
                    <div>{this.state.p1}</div>
                    <br />
                    <Toggle />
                </div>
            )
        }
    }
    
    ReactDom.render(<Root />, document.getElementById('root'));

    分析:

    Toggle类,有自己的state属性

    当render完成后,网页上有一个div标签,div标签对象捆邦了一个click事件,div标签内有文本内容。

    点击左键后,触发了click方法关联的handleClick函数,改变了状态值。

    状态的改变会导致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是通过绑定来的。

    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) {
            // event.preventDefult()
            console.log(event.target.id);
            console.log(event.target === this);
            console.log(this);
            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 />
            {this.props.name} : {this.props.parent.state.p1 + this.props.parent.state.p2}<br />
            {this.props.children}
            </div>;
        }
    }
    class Root extends React.Component {
        // 定义一个对象
        state = { p1: '会发生', p2: '变化' }; // 构造函数中定义state
        // handleClick(event) {
        //     console.log(this.state)
        // };
        
        render() {
            // setTimeout(() => this.setState({ p1: '已经' }), 5000);
            setInterval(() => this.setState({ p1: '已经' }), 5000);
            return (
                <div>
                    <div>这里5秒 {this.state.p1}{this.state.p2}</div>
                    <br />
                    <Toggle name="teststr" parent={this}>
                    <hr />
                    <span>我是Toggle元素的子元素</span>
                    </Toggle>
                </div>);
        }
    }
    ReactDom.render(<Root />, document.getElementById('root'));

    有了props,可以在子组件中控制父组件中的state。

    尝试修改props中的属性值,会抛出 TypeError: Cannot assign to read only property 'name' of object '#<Object>' 异常。

    应该说,state是私有private的属于组件自己的属性,组件外无法直接访问。可以修改state,但是建议使用setState方法。

    props是公有public属性,组件外也可以访问,但只读。

     在修改state属性时,注意

    react在处理事件时,会在传递给子组件后,再传递给父组件。比如子组件和父组件都绑了onclick事件。一般不会同时接受。

    构造器constructor

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

    import React from 'react';
    import ReactDom from 'react-dom';
    class Toggle extends React.Component {
        constructor(prpos){
            super(prpos)
        this.state = { flag: true }; // 类中定义state
        }
        handleClick(event) {
            this.setState({ flag: !this.state.flag });
            console.log(this.props.parent);
            let r = this.props.parent;
            r.setState({p1:"hello !!!"})
        }
        render() {/* 注意一定要绑定this onClick写成小驼峰 */
            return <div style={{padding:"20px"}}>
            <span id="t1" onClick={this.handleClick.bind(this)} >点击这句话,会触发一个事件。{this.state.flag.toString()}</span>
            <br />
            {this.props.name} : {this.props.parent.state.p1 + this.props.parent.state.p2}<br />
            {this.props.children}
            </div>;
        }
    }
    class Root extends React.Component {
        constructor(prpos){
            super(prpos)
        this.state = { p1: '会发生', p2: '变化' }; // 构造函数中定义state
        }
        
        render() {
            // setTimeout(() => this.setState({ p1: '已经' }), 5000);
            setInterval(() => this.setState({ p1: '已经' }), 5000);
            return (
                <div>
                    <div>这里5秒 {this.state.p1}{this.state.p2}</div>
                    <br />
                    <Toggle name="teststr" parent={this}>
                    <hr />
                    <span style={{backgroundColor:"white"}}>我是Toggle元素的子元素</span>
                    </Toggle>
                </div>);
        }
    }
    ReactDom.render(<Root />, document.getElementById('root'));

    组件的生命周期 *

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

    • Mounting:已插入真实 DOM
    • Updating:正在被重新渲染
    • Unmounting:已移出真实 DOM

    组件的生命周期的方法

    装载时触发

    componentWillMount: 渲染前调用,只在装载前调用一次,在客户端和服务端都执行

    componentDidMount: 在第一次渲染后执行,只在客户端执行,之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异部操作阻塞UI)。只在装载完成后调用一次,在render之后

    更新时触发,不会再首次render时调用

    componentWillReceiveProps(nextProps) 在组件接收到一个新的prop时被调用。这个方法在初始化render时不会被调用

    shouldComponentUpdate(nextProps, nextState) 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。

    可以在你确认不需要更新组件时使用。

    如果设置为false,就是不允许更新组件,那么componentWillUpdate、componentDidUpdate不会执行。

    componentWillUpdate(nextProps, nextState) 在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。

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

    卸载组件触发

    componentWillUnmount在组件从 DOM 中移除的时候立刻被调用。

    由图可知
    constructor构造器是最早执行的函数。

    触发 更新生命周期函数 ,需要更新state或props。

    因此,重新编写/src/index.js。

    构造两个组件,在子组件Sub中,加入所有生命周期函数。

    下面的例子添加是装载、卸载组件的生命周期函数

    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 style={{ height: 200 + 'px', color: 'red', backgroundColor: '#f0f0f0' }}>
                <a id="sub" onClick={this.handleClick.bind(this)}>
                    Sub's count = {this.state.count}
                </a>
            </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) {
            console.log('Root Constructor')
            super(props); // 调用父类构造器
            // 定义一个对象
            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="root" onClick={this.handleClick.bind(this)}>
                    My Name is {this.state.name}
                    <hr />
                    <Sub /> {/*父组件的render,会引起下一级组件的更新流程,导致props重新发送,即使子组件props没有
    改变过*/}
                </div>);
        }
    }
    ReactDom.render(<Root />, document.getElementById('root'));

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

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

    componentWillReceiveProps 在组件内部,props是只读不可变的,但是这个函数可以接收到新的props,可以对props做一些处理,this.props = {name:'roooooot'};这就是偷梁换柱。componentWillReceiveProps触发,也会走shouldComponentUpdate。

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

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

    不过,大多数时候,用不上这些函数,这些钩子函数是为了精确的控制。shouldComponentUpdate可以在程序中拦截,控制属性的值。

    无状态组件

    import React from "react";
    import ReactDom from "react-dom"
    
    let Root = props => <div>{props.schoolName}</div>
    
    ReactDom.render(<Root schoolName="magedu" />, document.getElementById('root'));

    无状态组件,也叫函数式组件

    开发中,很多情况下,组件其实很简单,不需要state状态,也不需要使用生命周期函数。无状态组件很好的满足了需要。

    无状态组件函数应该提供一个参数props,返回一个React元素。

    无状态组件函数本身就是render函数。

  • 相关阅读:
    Hypercall机制
    python 基础-----数字,字符串,if while 循环 数据类型的转换简单介绍
    计算机基础知识
    Proxmox初步了解
    Centos7-安装py3
    KVM-virsh常用命令
    Centos7-VNC安装
    Centos7-bond模式介绍
    KVM管理工具
    Win10-无法启动虚拟机
  • 原文地址:https://www.cnblogs.com/rprp789/p/9986262.html
Copyright © 2020-2023  润新知