笔记内容转载自AcWing的Web应用课讲义,课程链接:AcWing Web应用课。
CONTENTS
1. React配置环境
React官网:React。
React是一个声明式,高效且灵活的用于构建用户界面的JavaScript库。使用 React 可以将一些简短、独立的代码片段组合成复杂的UI界面,这些代码片段被称作components
。React能够构建那些数据会随时间改变的大型应用。
React特性:
- React为了能够方便地去维护我们的页面,它在内存里面创建了一个虚拟的DOM树:
Virtual DOM
,这是一个轻量级的虚拟的DOM,就是React抽象出来的一个对象,描述DOM应该什么样子的,应该如何呈现。通过这个Virtual DOM
去更新真实的DOM,由这个Virtual DOM
管理真实DOM的更新。 - 数据驱动:当某一个元素里的数据发生变化后,React会重新将有可能修改的元素都修改一遍,然后与真实的DOM树对比是否有区别,React分析完后最终只会修改真实改变的结点。由于在内存里修改对象的速度很快,因此React效率很高。
- React一般不直接手写JS,而是通过编写JSX文件,JSX比JS更好写一点,React会先将JSX编译成JS。
(1)安装Git Bash
:Windows安装Git教程(2022.11.18 Git2.38.1)。
(2)安装NodeJS
:NodeJS的安装及配置。
(3)安装create-react-app
:
打开Git bash
,执行以下命令:
npm i -g create-react-app
如果速度很慢,可以先修改镜像源再尝试安装:
npm config set registry https://registry.npm.taobao.org
npm i -g create-react-app
如果安装完成后出现警告:npm WARN deprecated [email protected]: This version of tar is no longer supported, and will not receive security updates. Please upgrade asap.
,可以先更新tar
试试:
npm install -g tar
如果还是有警告,且创建项目时(例如执行create-react-app react-app
)报错:bash: create-react-app: command not found
,使用npx
创建项目:
npx create-react-app my-app
或者用npm
创建项目:
npm init react-app my-app
创建好后进入项目文件夹启动项目:
cd my-app
npm start
启动后的效果如下(ctrl+c
即可停止服务):
(4)配置VS Code插件:Simple React Snippets、Prettier - Code formatter
Simple React Snippets
是React智能化自动补全插件:
例如输入imrc
即可补全出以下内容:
import React, {
Component } from 'react';
输入cc
即可补全出以下内容:
class Example extends Component {
state = {
}
render() {
return ();
}
}
export default Example;
Prettier - Code formatter
是代码格式化插件:
(5)创建React App
:
在目标目录下右键打开Git Bash:
在终端中执行:
npx create-react-app react-app # react-app是新建项目的名字,可以替换为其他名称
cd react-app
npm start # 启动应用
启动成功后会在本地开一个3000端口,页面效果已在上文展示。此时使用VS Code打开react-app
文件夹:
其中,node_modules
用来维护各种JS库,未来安装的所有依赖项都会放在该文件夹下;public
中的index.html
就是我们未来渲染出的页面,该文件中只有一个<div id="root"></div>
;src
中的index.js
代码如下:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root')); // 获取div同时创建为一个React对象
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
其中App
的定义在App.js
中:
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={
logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
该App
组件就定义了页面的具体内容,且我们能够发现该JS文件中有HTML代码,因此该文件即为JSX文件,能够在JavaScript的基础上支持XML(可扩展标记语言),HTML也是一种特殊的XML。
JSX是React中的一种语言,会被Babel编译成标准的JavaScript。
2. ES6语法补充
ES6,全称ECMAScript 6.0,是JavaScript的版本标准。此处添加一些React中常用的语法糖。
(1)使用bind()
函数绑定this
取值
在JavaScript中,函数里的this
指向的是执行时的调用者,而非定义时所在的对象。例如:
const person = {
name: "abc",
talk: function() {
console.log(this);
}
}
person.talk();
const talk = person.talk;
talk();
运行结果为:
{name: 'abc', talk: f}
undefined
使用bind()
函数绑定this
的取值为person
。例如:
const talk = person.talk.bind(person);
运行结果为:
{name: 'abc', talk: f}
{name: 'abc', talk: f}
(2)箭头函数的简写方式
当函数参数只有一个时可以将括号去掉,当函数体只有一个return
语句时可以把return
和{}
一起去掉,例如:
const f = (x) => {
return x * x;
};
等价于:
const f = x => x * x;
(3)箭头函数不重新绑定this
的取值
const person = {
talk: function() {
setTimeout(function() {
console.log(this);
}, 1000);
}
};
person.talk(); // 输出Window
const person = {
talk: function() {
setTimeout(() => {
console.log(this);
}, 1000);
}
};
person.talk(); // 输出 {talk: f}
(4)对象的解构
const person = {
name: "abc",
age: 18,
height: 180,
};
const {
name : new_name, age} = person; // new_name是name的别名
(5)数组和对象的展开
let a = [1, 2, 3];
let b = [...a]; // b是a的复制,和a不是一个数组,如果写let b = a则b和a是同一个数组
let c = [...a, 4, 5, 6]; // c = [1, 2, 3, 4, 5, 6]
let d = {
name: "abc"};
let e = {
age: 18, height: 180};
let f = {
...d, ...e, weight: 120}; // f = {name: "abc", age: 18, height: 180, weight: 120}
(6)Named exports与Default exports
Named Export
:可以export
多个,import
的时候需要加大括号,名称需要匹配,即之前使用的方式。Default Export
:最多export
一个,import
的时候不需要加大括号,可以直接定义别名。
3. Components
(1)创建项目
首先创建一个新项目box-app
:
npx create-react-app box-app
cd box-app
npm start
安装bootstrap
库:
npm i bootstrap
bootstrap
的引入方式:
import 'bootstrap/dist/css/bootstrap.css';
(2)创建Component
在src
文件夹中创建一个文件夹components
存放组件,然后在components
文件夹中创建一个JSX文件box.jsx
(使用.js
后缀也一样,只是用.jsx
后区分起来跟明显一点)其框架如下:
import React, {
Component } from 'react'; // 输入imrc即可补全
class Box extends Component {
// 输入cc即可补全
state = {
}
render() {
// Component类的函数,用来返回当前组件最后渲染的HTML结构是什么
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';
import Box from './components/box';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Box />
</React.StrictMode>
);
(3)创建按钮
由于Component中的render()
函数只能return
一个元素,因此当子节点数量大于1时,可以用<div>
或<React.Fragment>
将其括起来。
(4)内嵌表达式
JSX中使用{}
在HTML标签中嵌入表达式。
(5)设置属性
由于class
是JS中的关键字,因此HTML标签中的class
需要改为className
。CSS属性也需要修改,例如:background-color
修改为backgroundColor
,其它属性也是类似的。
综合示例:
import React, {
Component } from 'react'; // 输入imrc即可补全
class Box extends Component {
// 输入cc即可补全
state = {
x: 0
};
styles = {
width: '50px',
height: '50px',
backgroundColor: 'lightblue',
color: 'white',
textAlign: 'center',
lineHeight: '50px',
borderRadius: '5px'
};
render() {
// Component类的函数,用来返回当前组件最后渲染的HTML结构是什么
return (
// HTML标签中可以使用{}写一个表达式
<React.Fragment>
<div style={
this.styles}>{
this.state.x}</div>
<button className='btn btn-primary m-2'>Left</button>
<button className='btn btn-success m-2'>Right</button>
</React.Fragment>
);
}
}
export default Box;
(6)数据驱动改变Style
例如:
import React, {
Component } from 'react'; // 输入imrc即可补全
class Box extends Component {
// 输入cc即可补全
state = {
x: 1
};
render() {
// Component类的函数,用来返回当前组件最后渲染的HTML结构是什么
return (
// HTML标签中可以使用{}写一个表达式
<React.Fragment>
<div style={
this.getStyles()}>{
this.state.x}</div>
<button className='btn btn-primary m-2'>Left</button>
<button className='btn btn-success m-2'>Right</button>
</React.Fragment>
);
}
getStyles() {
let styles = {
width: '50px',
height: '50px',
backgroundColor: 'lightblue',
color: 'white',
textAlign: 'center',
lineHeight: '50px',
borderRadius: '5px'
};
if (this.state.x === 0) {
styles.backgroundColor = 'orange';
}
return styles;
}
}
export default Box;
(7)渲染列表
可以使用map
函数渲染一个列表,每个元素需要具有唯一的key
属性,用来帮助React快速找到被修改的DOM元素,例如:
import React, {
Component } from 'react'; // 输入imrc即可补全
class Box extends Component {
// 输入cc即可补全
state = {
x: 1,
colors: ['red', 'green', 'blue']
};
render() {
// Component类的函数,用来返回当前组件最后渲染的HTML结构是什么
return (
// HTML标签中可以使用{}写一个表达式
<React.Fragment>
<div>{
this.state.x}</div>
<button className='btn btn-primary m-2'>Left</button>
<button className='btn btn-success m-2'>Right</button>
{
this.state.colors.map(color => (
<div key={
color}>{
color}</div>
))}
</React.Fragment>
);
}
}
export default Box;
(8)Conditional Rendering
利用逻辑表达式的短路原则:
- 与表达式中
expr1 && expr2
,当expr1
为假时返回expr1
的值,否则返回expr2
的值。 - 或表达式中
expr1 || expr2
,当expr1
为真时返回expr1
的值,否则返回expr2
的值。
(9)绑定事件
例如可以使用onClick
绑定按钮的点击事件,注意需要妥善处理好绑定事件函数的this
,示例如下:
import React, {
Component } from 'react'; // 输入imrc即可补全
class Box extends Component {
// 输入cc即可补全
state = {
x: 1
};
handleClickLeft() {
// 如果不用箭头函数或者下面调用函数时使用bind(this)的话this会变成undefined
console.log('click left', this);
}
handleClickRight = () => {
console.log('click right', this);
}
render() {
// Component类的函数,用来返回当前组件最后渲染的HTML结构是什么
return (
// HTML标签中可以使用{}写一个表达式
<React.Fragment>
<div>{
this.state.x}</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>
);
}
}
export default Box;
(10)修改state
需要使用this.setState()
函数,每次调用this.setState()
函数后,会重新调用this.render()
函数,用来修改虚拟DOM树。React只会修改不同步的实际DOM树节点。例如:
import React, {
Component } from 'react'; // 输入imrc即可补全
class Box extends Component {
// 输入cc即可补全
state = {
x: 1
};
handleClickLeft = () => {
// this.state.x--; 这样写的话React不会修改前端页面的效果
this.setState({
x: this.state.x - 1
});
}
handleClickRight = () => {
this.setState({
x: this.state.x + 1
});
}
render() {
// Component类的函数,用来返回当前组件最后渲染的HTML结构是什么
return (
// HTML标签中可以使用{}写一个表达式
<React.Fragment>
<div style={
this.getStyles()}>{
this.state.x}</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: 'lightblue',
color: 'white',
textAlign: 'center',
lineHeight: '50px',
borderRadius: '5px',
position: 'relative',
left: this.state.x
};
if (this.state.x === 0) {
styles.backgroundColor = 'orange';
}
return styles;
}
}
export default Box;
(11)给事件函数添加参数
可以定义一个临时函数绑定事件,然后在该函数中调用原函数并传入参数,或者直接在绑定事件的时候用一个临时的箭头函数返回传入参数的原函数。例如:
import React, {
Component } from 'react'; // 输入imrc即可补全
class Box extends Component {
// 输入cc即可补全
state = {
x: 1
};
handleClickLeft = (step) => {
this.setState({
x: this.state.x - step
});
}
handleClickRight = (step) => {
this.setState({
x: this.state.x + step
});
}
handleClickLeftTmp = () => {
this.handleClickLeft(10);
}
render() {
// Component类的函数,用来返回当前组件最后渲染的HTML结构是什么
return (
// HTML标签中可以使用{}写一个表达式
<React.Fragment>
<div style={
this.getStyles()}>{
this.state.x}</div>
<button onClick={
this.handleClickLeftTmp} 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: 'lightblue',
color: 'white',
textAlign: 'center',
lineHeight: '50px',
borderRadius: '5px',
position: 'relative',
left: this.state.x
};
if (this.state.x === 0) {
styles.backgroundColor = 'orange';
}
return styles;
}
}
export default Box;