文章目录
-
- 一、React 框架简介
- 二、配置环境
- 三、组件(Component)
- 四、Component 组件的组合与交互
一、React 框架简介
-
介绍
CS 与 BS结合:像
React
,Vue
此类框架,转移了部分服务器的功能到客户端。将CS
和BS
加以结合。客户端只用请求一次服务器,服务器就将所有js代码返回给客户端,所有交互类操作都不再依赖服务器。 客户端只有在需要服务器的数据时才会使用json
通信一下,其他时间都在客户端利用js
操作、暂存数据这样就极大减轻了服务器压力。 -
React 特性
○ 虚拟DOM树
React
通过对DOM
的模拟,最大限度地减少与DOM
的交互。将网页的DOM
树完全复制一份虚拟的DOM
树放到内存中。○ 数据驱动
维护虚拟
DOM
树,当发现某些节点发生变化时,并不一定修改原来的DOM
树(在网页中看到的每一个区域),比如某些分支可能会发生该改变时,首先会修改虚拟树中的这几个节点,而后将这几个节点与原节点对比。才会对真正发生改变的节点做出修改。
○ JSX文件为了构建交互式用户界面,使用了
JSX
。JSX
的完整版本是一个JavaScript
语法扩展,它极大地简化了组件的创建。它支持HTML
引用并使子组件渲染更容易。它本质上是一组React
编写快捷方式。使用带有一些规则的createElement
可以使源代码更具可读性和直接性。首先写jsx文件,运行时会先将所编写的jsx
编译为js
,编译完之后,将js
文件运行在浏览器。 -
React 作用
将语言框架通用和优化,
React
主要用于构建UI
。可以在React
里传递多种类型的参数,如声明代码,帮助你渲染出UI
、也可以是静态的HTML DOM
元素、也可以传递动态变量、甚至是可交互的应用组件,性能出众,代码逻辑简单。
二、配置环境
- 安装
Git Bash
Git bash 下载链接
Git Bash 安装教程 - 安装
Node.js
Nodejs 下载链接
Nodejs 安装教程 - 安装
create-react-app
打开Git Bash
,直接输入下面命令执行npm i -g create-react-app
- 创建 React 项目,名为 React App
在目标目录(自己存放项目的目录)下右键打开 Git Bash,执行下面的命令create-react-app react-app # react-app可以替换为其它名称
- 启动项目
进入目录,目录进到 react-app 这一层,打开 Git Bash,输入下面命令npm start # 启动应用
三、组件(Component)
组件类似于一个 class,将一些 html、data(数据)、事件函数组合在一起成为一个组件
-
定义组件:定义好组件之后,需要将组件渲染出来,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> );
-
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> ); } }
-
在 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}`; }
-
设置样式
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> ); }
-
数据驱动改变 style
将
style
与state
(局部变量) 值相关联,通过改变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> ); }
-
渲染列表
使用
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> ); } }
-
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> ); }
-
绑定事件
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函数 ); }
-
修改 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> ); } }
-
通过按钮修改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; } }
-
给事件函数添加参数
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组件的构建步骤:
- 定义
state
变量,并使得数据驱动style
; - 构造 handleClickLeft =(step)=>{ } 带参函数,利用
setState()
函数改变state
值;
调用setState()能够重新加载 render 函数,才可以对里面的 div 显示进行修改
- 给按钮的单击事件
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 组件的组合与交互
-
【组合 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
定义的属性值返回传递给Box
的state
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;
○ 标签传递:通过
props
将Boxes
增加的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> ); } }