2023年 5月 17日 来源: 新华社微博 字号:默认 超大

文章目录

    • 一、React 框架简介
    • 二、配置环境
    • 三、组件(Component)
    • 四、Component 组件的组合与交互

一、React 框架简介


  1. 介绍

    CS 与 BS结合:像 ReactVue 此类框架,转移了部分服务器的功能到客户端。将CSBS 加以结合。客户端只用请求一次服务器,服务器就将所有js代码返回给客户端,所有交互类操作都不再依赖服务器。 客户端只有在需要服务器的数据时才会使用json通信一下,其他时间都在客户端利用js操作、暂存数据这样就极大减轻了服务器压力。

  2. React 特性

    ○ 虚拟DOM树

    React 通过对 DOM 的模拟,最大限度地减少与DOM的交互。将网页的DOM树完全复制一份虚拟的 DOM 树放到内存中。

    ○ 数据驱动

    维护虚拟 DOM 树,当发现某些节点发生变化时,并不一定修改原来的 DOM 树(在网页中看到的每一个区域),比如某些分支可能会发生该改变时,首先会修改虚拟树中的这几个节点,而后将这几个节点与原节点对比。才会对真正发生改变的节点做出修改。
    React 框架
    ○ JSX文件

    为了构建交互式用户界面,使用了 JSXJSX 的完整版本是一个JavaScript语法扩展,它极大地简化了组件的创建。它支持HTML引用并使子组件渲染更容易。它本质上是一组 React 编写快捷方式。使用带有一些规则的 createElement 可以使源代码更具可读性和直接性。首先写jsx文件,运行时会先将所编写的jsx编译为js,编译完之后,将js文件运行在浏览器。

  3. React 作用

    将语言框架通用和优化,React主要用于构建UI。可以在React里传递多种类型的参数,如声明代码,帮助你渲染出UI、也可以是静态的HTML DOM元素、也可以传递动态变量、甚至是可交互的应用组件,性能出众,代码逻辑简单。

二、配置环境


  1. 安装 Git Bash
    Git bash 下载链接
    Git Bash 安装教程
  2. 安装 Node.js
    Nodejs 下载链接
    Nodejs 安装教程
  3. 安装 create-react-app
    打开 Git Bash,直接输入下面命令执行
    npm i -g create-react-app
    
  4. 创建 React 项目,名为 React App
    在目标目录(自己存放项目的目录)下右键打开 Git Bash,执行下面的命令
    create-react-app react-app  # react-app可以替换为其它名称
    
  5. 启动项目
    进入目录,目录进到 react-app 这一层,打开 Git Bash,输入下面命令
    npm start  # 启动应用
    

三、组件(Component)


组件类似于一个 class,将一些 html、data(数据)、事件函数组合在一起成为一个组件

  1. 定义组件:定义好组件之后,需要将组件渲染出来,index.js 就是所有 js 的入口, 并引入React与Component组件。

    // box.js 文件
    import React, { Component } from 'react';  		// 快捷键:imrc 
    // 引入React原因:将jsx编译为js
    // 通过React.createElement()
    class Box extends Component {					// 快捷键:CC
        state = {  } 								//局部变量
        // component类的函数,用来返回当前组件最后的渲染html结构
        // render方法只能返回一个标签及所包含内容
        render() { 
            return (
                <h1> Hello world</h1>
            );
        }
    }
    export default Box;
    
    // index.js 文件
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import './index.css';
    import 'bootstrap/dist/css/bootstrap.css';   		// 引入bootstrap库
    import Box from './components/box';        			// 引入box
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
    <React.StrictMode>
    <Box /> 										// Box组件,见box.js
    </React.StrictMode>
    );
    
  2. React Fragment 介绍与使用:

    (1) render()方法只能返回一个标签及所包含内容,想返回多个并列标签时需要包含在一个标签内。
    (2) React.Fragment 组件能够在不额外创建 DOM 元素的情况下,让render()方法中返回多个元素。Fragments 允许将子列表分组,而无需向DOM添加额外标签。

    理解起来就是在我们定义组件的时候,return里最外层包裹的div往往不想渲染到页面,那么就要用到Fragment组件了。

    class Box extends Component {
    state = {  } 
    render() { 
        return (
            <React.Fragment>
                <h1> Hello world</h1>
                <button>left</button>
                <button>right</button>
            </React.Fragment>
        );
    }
    }
    
  3. 在 jsx 中写 js 与 html 标签
    jsx 中可以在任意地方定义 html 标签,但要注意,jsx 里的 html 标签中写 js 代码时都需要加 {} 括起来,{} 中只能写表达式。

    render() {
        return (
            <React.Fragment>
                <h1>{this.toString()}</h1>  		// html 标签内写js
                <button className="btn btn-primary">left</button>
                <button>right</button>
            </React.Fragment>
        );
    }
    toString(){
        return `x: ${this.state.x}`;
        // 或者
        // const {x} = this.state; //ES6写法相当于const xxx = this.state.xxx
        // return `x:${x}`;
    }
    
  4. 设置样式

    jsx文件下的html 标签中设置类名以用于 css 样式时,需要将 class =" "写为 className。 由于 jsx 下 html 标签与 js 语句混合,写 class 可能会与实际js中的同名类产生冲突。

    className

    return (
            <React.Fragment>
                <h1>{this.toString()}</h1>     
                <button className="btn btn-primary m-2">left</button>
                <button className="btn btn-success m-2">right</button>
            </React.Fragment>
            // m-2 为 bootstrap 中 margin=2 的简写方式
    );
    

    style

    render() { 
    return (
    <React.Fragment>
    // style样式:第一层{}代表里面是表达式,第二层{}代表里面是对象,即样式变量的内容
    <div style={{
    width: "50px",
    height: "50px",
    backgroundColor: "pink",
    color: "white",
    textAlign: "center",
    lineHeight: "50px",
    borderRadius: "5px",
    }}>{this.toString()}</div>
    <button className="btn btn-primary m-2">left</button>
    <button className="btn btn-success m-2">right</button>
    </React.Fragment>
    );
    }
    

    等价于:

    styles = {
    width: "50px",
    height: "50px",
    backgroundColor: "pink", 					// css中所有 - 命名均改为驼峰命名法
    }
    render() {
    return (    								// 标签内 style={this.styele} 即可
    <React.Fragment>
    <div style={this.styles}>{this.toString()}</div>
    <button className="btn btn-primary m-2">left</button>
    <button className="btn btn-success m-2">right</button>
    </React.Fragment>
    );
    }
    
  5. 数据驱动改变 style

    stylestate(局部变量) 值相关联,通过改变 state 里的值来改变 style。当局部变量改变时,通过接口实现所有被这个值影响的组件都改变。

    state = {
        x: 1,
     } 
    getStyles() {
        let styles = {
            width: "50px",
            height: "50px",
            backgroundColor: "pink", 
            color: "white",
            textAlign: "center",
            lineHeight: "50px",
            borderRadius: "5px",
            margin: '5px',
        };
        if (this.state.x === 0){
            styles.backgroundColor = 'orange'; // 数据驱动改变style
        }
        return styles;
    }
    
     render() { 
    return (
    // 直接调用 getStyles()函数
    <React.Fragment>
    <div style={this.getStyles()}>{this.toString()}</div>
    <button className="btn btn-primary m-2">left</button>
    <button className="btn btn-success m-2">right</button>
    </React.Fragment>
    );
    }
    
  6. 渲染列表

    使用 map 函数

    遍历类写法需给每个标签元素定义唯一 key 属性,用来帮助React快速找到被修改的DOM元素

    class Box extends Component {
        state = { 
            x: 1,
            colors: ['red','green','blue'],   		// 定义渲染列表,这里用来修改div元素内容
         } 
        render() { 
    	        return (
    	            <React.Fragment>
    	                {this.state.colors.map(color => (
    	                    <div key={color}>{color}</div> // 这里建立div并将内容赋值为上述列表
    	                ))}             
    	            </React.Fragment>
       	        );
        }
    	}
    
  7. Conditional Rendering
    A && B && C ...:从前往后,返回第一个为 false 的表达式。( 如果全为 true,就返回最后一个 true 表达式)
    A || B || C ...:从前往后,返回第一个为 true 的表达式。(如果全为 false,就返回最后一个 false 表达式。)

    即利用逻辑表达式短路原则:

     render() {
        return (     
         	<React.Fragment>   
                {this.state.colors.length === 0 && <p> No colors</p>}   
                // 即当 colors 有元素时无操作, 即当 colors 无元素时显示 No color
                {this.state.colors.map(color => (
                    <div key={color}>{color}</div>
                ))}             
            </React.Fragment>           
        );
    }
    
  8. 绑定事件

    e.q 给 button 按钮绑定事件:

    class Box extends Component {
        handleClickLeft(){
            console.log("click left",this); 
        }
        handleClickRight(){
            console.log("click right",this);
        }
        render() { 
            //仅仅是绑定函数,而不是在渲染时就将返回值传过来,因此handleClickleft不加()    
            return (
                <React.Fragment>
                    <div style={this.getStyles()}>{this.toString()}</div> 
                    <button onClick={this.handleClickLeft} className="btn btn-primary m-2">left</button>
                    <button onClick={this.handleClickRight} className="btn btn-success m-2">right</button>          
                </React.Fragment>
        }
    }
    

    此时,输出的 this 不是 Box 类,而是 undifind

    如何使得方法里的 this 仍属于 Box 类:
    // 法一:箭头函数(推荐)
    // 法二: bind 函数

    代码如下:

    handleClickLeft=()=>{						 // 法一:箭头函数
        console.log("click left",this); 
    }
    handleClickRight(){
        console.log("click right",this);
    }
    render() { 
         return (
            <React.Fragment>
                <div style={this.getStyles()}>{this.toString()}</div> 
                <button onClick={this.handleClickLeft} className="btn btn-primary m-2">left</button>
                <button onClick={this.handleClickRight.bind(this)} className="btn btn-success m-2">right</button>          
            </React.Fragment>					// 法二:bind函数
        );
    }
    
  9. 修改 state 里的值

    直接 this.state.x-- 不会影响到页面div的显示的x值,
    如果想要让 state 里的 x 的修改影响到render函数的话, 必须用 setState() 函数 (通过重新调用 render 修改 div 里 x 值)

    class Box extends Component {
        state = { 
            x: 1,
        } 
        handleClickLeft = () => { 
               this.setState({				// setState() 函数
                x: this.state.x - 1
            });
        }
        handleClickRight = () => {
            this.setState({					// setState() 函数
                x: this.state.x + 1
            });
        }
        render() {    
            return (
                <React.Fragment>
                    <div style={this.getStyles()}>{this.toString()}</div> 
                    <button onClick={this.handleClickLeft} className="btn btn-primary m-2">left</button>
                    <button onClick={this.handleClickRight} className="btn btn-success m-2">right</button>          
                </React.Fragment>
            );
        }
    }
    
  10. 通过按钮修改css属性:

    state 里的值赋值给某一样式的属性,通过按钮修改state 值从而修改 css 样式

    class Box extends Component {
        state = { 
            x: 10, 							// state值
        } 
        handleClickLeft = () => { 
        this.setState({
            x: this.state.x - 10			// setState() 修改 state值, 重新调用 render() 函数
        });
    	}
        handleClickRight = () => {
            this.setState({
                x: this.state.x + 10		// setState() 修改 state值,重新调用 render() 函数
            });
        }
        render() { 
            return (
                <React.Fragment>
                    <div style={this.getStyles()}>{this.toString()}</div> 
                    <button onClick={this.handleClickLeft} className="btn btn-primary m-2">left</button>
                    <button onClick={this.handleClickRight} className="btn btn-success m-2">right</button>          
                </React.Fragment>
            );
        }
        getStyles() {
            let styles = {
                width: "50px",
                height: "50px",
                backgroundColor: "pink", 
                color: "white",
                textAlign: "center",
                lineHeight: "50px",
                borderRadius: "5px",
                margin: '5px',
                marginLeft: this.state.x,              //  state值赋值给 css 属性值
            };
            if (this.state.x === 0){
                styles.backgroundColor = 'orange';
            }
            return styles;
        }		
    }
    
  11. 给事件函数添加参数

    handleClickRight = (step) => {
        this.setState({
            x: this.state.x + step
        });
    }
    
    handleClickRightTmp = () => {
    return this.handleClickRight(50);
    }
    
     render() {
    return (
    <React.Fragment>
    <div style={this.getStyles()}>{this.toString()}</div>
    <button onClick={this.handleClickLeft} className="btn btn-primary m-2">left</button>
    <button onClick={this.handleClickRightTmp} className="btn btn-success m-2">right</button>
    </React.Fragment>
    );
    }
    

    handleClickRight() 函数写为箭头匿名函数后等价于:

    render() {
    //    绑定了一个调用含参 函数 handleClickLeft =(step)=>{ }  的匿名函数  
    return (
    <React.Fragment>
    <div style={this.getStyles()}>{this.toString()}</div>
    <button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">left</button>
    <button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">right</button>
    </React.Fragment>
    );
    }
    

综上,Box组件的构建步骤:

  1. 定义 state 变量,并使得数据驱动 style
  2. 构造 handleClickLeft =(step)=>{ } 带参函数,利用 setState() 函数改变 state 值;
    调用setState()能够重新加载 render 函数,才可以对里面的 div 显示进行修改
  3. 给按钮的单击事件onClick绑定所写函数。
    这里绑定了一个调用含参函数 handleClickLeft =(step)=>{ } 的匿名函数,()=>this.handleClickRight(10)
    class Box extends Component {
    	// 1. 定义 state,并使得数据驱动style
        state = { 
            x: 10,
        } 
    	// 2. 通过 handleClickLeft =(step)=>{ } 带参函数 与 setState()  改变state值
    	//    并能够重新加载 render 函数来对里面的 div 显示进行操作
        handleClickLeft = (step) => { 
            this.setState({
                x: this.state.x - step
            });
        }
        handleClickRight = (step) => {
            this.setState({
                x: this.state.x + step
            });
        }
        render() { 
        	// 3. 给事件绑定函数:通过 render 函数里,按钮事件绑定函数。
        	//    绑定了一个调用含参函数 handleClickLeft =(step)=>{ }  的匿名函数  
            return (
                <React.Fragment>
                    <div style={this.getStyles()}>{this.toString()}</div> 
                    <button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">left</button>
                    <button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">right</button>          
                </React.Fragment>
            );
        }
        getStyles() {
            let styles = {
                width: "50px",
                height: "50px",
                backgroundColor: "pink", 
                color: "white",
                textAlign: "center",
                lineHeight: "50px",
                borderRadius: "5px",
                margin: '5px',
                marginLeft: this.state.x,	// 数据驱动 style
            };
            if (this.state.x === 0){
                styles.backgroundColor = 'orange';
            }
            return styles;
        }
    }
    

四、Component 组件的组合与交互


  1. 【组合 Component 】组件的构建

    组合多个上述定义的 Box 组件,形成 Boxes 组件,并完成 属性 值的传递。
    <注:多个相同子组件时,每个子组件需要有唯一 key 值>

    ○ 建立 Boxes 类组件,内含多个 Box组件

    import React, { Component } from 'react';
    import Box from './box';
    class Boxes extends Component {
    	// 1. 设置 state 变量,包括 Box 组件的唯一 key 值与 x 坐标值。
        state = {  
            boxes:[
                {id: 1, x: 10},
                {id: 2, x: 10},
                {id: 3, x: 100},
                {id: 4, x: 10},
                {id: 5, x: 10},
            ]
        } 
        // 2. render 函数返回多个 box 组件,通过 map 列表,逐一建立并赋值多个 Box 组件
       	//    将 box.id 赋值给组件唯一 key,将 box.x 赋值给 Box 组件的 x
        render() { 
            return (
                <React.Fragment>
                   {this.state.boxes.map((box)=>(
                        <Box
                            key = {box.id} // id
                            x = {box.x}    // 这里会自动找到 Box 组件里的 x 赋值并存储在 props 中
                            			   // 但仅仅是修改了x,并不会改变前端的显示
                        />
                   ))}
                </React.Fragment>
            );
        }
    }
    export default Boxes;
    

    【注】在react组件之间的通信是通过props属性来完成的,比如父组件需要将数据传递给子组件,那么组件在渲染子组件的时候,直接将数据作为子组件的属性传参。

    ○ state 值传递:通过 props Boxes定义的属性值返回传递给 Boxstate

    class Box extends Component {
    state = {
    // props类似于state,存储除key以外属于 box 的所有属性
    // Boxes 建立的 Box 赋值的 x 存到了 props 里
    // 通过 props 传递给了每个 Box
    x: this.props.x,
    }
    handleClickLeft = (step) => {
    this.setState({
    x: this.state.x - step
    });
    }
    handleClickRight = (step) => {
    this.setState({
    x: this.state.x + step
    });
    }
    render() {
    return (
    <React.Fragment>
    <div style={this.getStyles()}>{this.toString()}</div>
    <button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">left</button>
    <button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">right</button>
    </React.Fragment>
    );
    }
    getStyles() {
    let styles = {
    width: "50px",
    height: "50px",
    backgroundColor: "pink",
    color: "white",
    textAlign: "center",
    lineHeight: "50px",
    borderRadius: "5px",
    margin: '5px',
    marginLeft: this.state.x,
    };
    if (this.state.x === 0){
    styles.backgroundColor = 'orange';
    }
    return styles;
    }
    toString(){
    return `x: ${this.state.x}`;
    }
    }
    export default Box;
    

    ○ 标签传递:通过 propsBoxes 增加的Box.children子标签,传递给 Box

     // Boxes.jsx文件
    render() {
    return (
    <React.Fragment>
    {this.state.boxes.map((box)=>(
    <Box key = {box.id} x = {box.x}>
    <p>Box :</p>				// 将 Box 的闭合标签写为双标签
    <div>{box.id}</div>			// 可在 Box 标签内增加其它标签,属性名为 Box.children
    </Box>							// 并存储到了 props 中																
    ))}
    </React.Fragment>
    );
    }
    
    // Box.jsx 文件
    render() {
    console.log(this.props);
    return (
    <React.Fragment>
    {this.props.children[0]}			// 通过 props 所存储的 children 将增加的标签传递给Box
    <div style={this.getStyles()}>{this.toString()}</div>
    <button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">left</button>
    <button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">right</button>
    </React.Fragment>
    );
    }
    

    ○ 方法传递1:React 子组件调用父组件的方法。

    Box子组件调用Boxes父组件内的方法,依旧通过props。实现在 Box 组件中触发 onClick 事件后,在Boxes组件中删除key值对应的Box,即在Box标签内调用Boxes标签的方法

     // Boxes.jsx 文件
    // 1. Boxes.jsx 文件中写删除的方法
    handleDelete = (id) => {
    // filter: boxes列表的元素依次判断,若表达式为true则留下,否则删掉
    // 即若id不等留下来,相等删除
    const newboxes = this.state.boxes.filter(
    (x)=>(x.id !== id)
    );
    this.setState({
    boxes: newboxes
    })
    }
    render() {
    if(this.state.boxes.length === 0){
    return <div className='alert alert-dark'>没有元素可以删除了!!!</div>
    }
    return (
    // 2. 将所写删除方法定义为标签的 onDelete 属性传递给 Box(会存储在 props中)
    <React.Fragment>
    {this.state.boxes.map((box)=>(
    <Box key = {box.id} id={box.id} x = {box.x} onDelete = {this.handleDelete}>
    <p>Box :</p>
    <div>{box.id}</div>
    </Box>
    ))}
    </React.Fragment>
    );
    }
    
    // Box.jsx 文件
    render() {
    return (
    <React.Fragment>
    {this.props.children[0]}
    <div style={this.getStyles()}>{this.toString()}</div>
    <button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">left</button>
    <button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">right</button>
    // 3. Box 调用 Boxes 的删除方法 :
    //    Box 中的 Button 的 onClick 事件,绑定匿名函数来调用含参的删除方法        
    <button onClick={()=>this.props.onDelete(this.props.id)} className='btn btn-danger m-2'> Delete </button>
    </React.Fragment>
    );
    }
    

    ○ 方法传递2:React 父组件调用子组件的方法。

    (仅能调用一个子组件方法,无法调用列表子组件)

    // 父组件
    class Boxes extends Component {
    // 1. Boxes 父组件中写入
    setChildRef = (ref) => {
    this.ChildRef = ref;
    }
    // 3. Boxes 父组件中写调用 Box 子组件的方法
    handleReset = () =>{
    this.ChildRef.handleRE()
    }
    render() {
    return (
    <React.Fragment>
    // 4. 将父组件方法绑定onClick单击事件中,即可实现单击调用子组件的方法
    <button onClick={this.handleReset} className='btn btn-primary'> Clear </button>
    {this.state.boxes.map((box)=>(
    // 2. Boxes 父组件的 Box 子组件标签内增加 ref 属性,并将 setChildRef 传递过来
    <Box key = {box.id} ref={this.setChildRef} id={box.id} x = {box.x} onDelete = {this.handleDelete}>
    <p>Box :</p>
    <div>{box.id}</div>
    </Box>
    ))}
    </React.Fragment>
    );
    }
    }
    
    // 子组件
    class Box extends Component {
    state = {
    x: this.props.x,
    }
    //  子组件中被调用的方法
    handleRE = () =>{
    this.setState({
    x: 0
    });
    }
    render() {
    return (
    <React.Fragment>
    ......
    </React.Fragment>
    );
    }
    }