【笔记】React学习笔记

前言

React(有时叫React.js或ReactJS),是一个为数据提供渲染为HTML视图的开源JavaScript 库。React视图通常采用包含以自定义HTML标记规定的其他组件的组件渲染。React为程序员提供了一种子组件不能直接影响外层组件(”data flows down”)的模型,数据改变时对HTML文档的有效更新,和现代单页应用中组件之间干净的分离。(维基百科

JSX文件

  • JSX是JS的一种扩展语言,它的本质还是JS
  • 通过babel编译器编译JSX文件

JSX文件的语法格式

  • 编译前和HTML语法相同
1
<div>hello</div>
  • 编译后实质上是JS文件
1
React.createElement("div", null, "hello");

引入编译器

  • 引入babel编译器到html文件,实现自动编译jsx语法代码
1
2
3
4
<script src="https://unpkg.com/[email protected]/babel.min.js"></script>
<script type="text/babel">
let html = <div>hello</div>;
</script>

手动编译JSX文件

  • 通过babel编译器手动编译jsx文件

项目初始化

1
npm -y init

下载脚手架

  • 下载babel脚手架@babel-cli
1
npm install --save-dev @babel/core @babel/cli @babel/preset-react

创建一个babel配置文件

  • 创建一个babel.config.js或者.babelrc文件
babelrc
1
2
3
{
"presets": ["@babel/preset-react"]
}

使用脚手架

  • 使用脚手架编译.jsx文件

<input>:编译之前的.jsx文件
<output>:编译之后的.js文件

1
npx babel <input>.jsx -o <output>.js

JSX表达式

  • JSX本身也是表达式
  • 无论在JSX内容还是属性还是JSX本身,它都可以使用JSX表达式
1
2
3
let name = "";
let jsx = <div>{name}</div>;
let jsx = <div name={name}></div>;

className

  • JSX中不能使用class给元素添加类名,因为jsx本质上是js,在js中class是关键字,所以需要用className代替class为元素添加类名
1
let jsx = <div className="div"></div>;

自动展开数组

  • JSX中会自动对数组进行遍历
1
2
3
4
5
6
7
let arrs = [
<div key="1">1</div>,
<div key="2">2</div>,
<div key="3">3</div>
]
let jsx = <div>{arrs}</div>
ReactDOM.render(jsx, document.getElementById("app"));

运算符

  • JSX语法也支持运算符

  • 根据布尔值动态展现数据

三目运算

1
2
3
let bool = true;
let jsx = <div>{bool?<div>hello</div>:null}</div>;
ReactDOM.render(jsx, document.getElementById("app"));

且运算

1
2
3
let bool = true;
let jsx = <div>{bool&&<div>hello</div>}</div>;
ReactDOM.render(jsx, document.getElementById("app"));

循环

  • 在JSX中循环不应该使用for,而应该使用map
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let arrs = [
{id: 1, name: "zhangsan"},
{id: 2, name: "lisi"},
{id: 3, name: "wangwu"}
];
let jsx =
<div>
<ul>
{
arrs.map(function(p, index) {
return <li key={index}>{p.name}</li>
})
}
</ul>
</div>;
ReactDOM.render(jsx, document.getElementById("app"));

事件

  • 通过on+事件名,给组件添加事件
  • 这种写法默认会传递事件对象作为参数
1
2
3
4
5
function fn(event) {
console.log("hello");
}
let jsx = <button onClick={fn}>按钮</button>;
ReactDOM.render(jsx, document.getElementById("app"));

传递自定义参数

通过箭头函数
  • 可以使用箭头函数,传递一个自定义参数
  • 此时第二个参数为事件对象
1
2
3
4
5
function fn(e, event) {
console.log(e);
}
let jsx = <button onClick={()=>{fn("hello")}}>按钮</button>;
ReactDOM.render(jsx, document.getElementById("app"));
通过bind函数
  • 也可以使用bind传递自定义参数。传递的自定义参数要在第二个参数上定义
1
2
3
4
5
function fn(e) {
console.log(e);
}
let jsx = <button onClick={fn.bind(null, "hello")}>按钮</button>;
ReactDOM.render(jsx, document.getElementById("app"));

引入React

通过CDN引入

1
2
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

hello world

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/[email protected]/babel.min.js"></script>
</head>
<body>

<div id="app">

</div>

</body>
<script type="text/babel">

let html1 = <div>hello world</div>;
ReactDOM.render(html1, document.getElementById("app"));

</script>
</html>

React函数

创建一个标签

  • 也可以通过jsx代码直接创建一个组件
  • 返回一个虚拟对象

第一个参数:标签名
第二个参数:标签属性
第三个参数:标签内容

1
React.createElement("div", null, "hello");

为标签添加内容

第一个参数:内容
第二个参数:绑定的标签

1
2
let html = <div>hello</div>;
ReactDOM.render(html, document.getElementById("app"));

React中的组件

定义类组件

  • 组件名首字母必须大写。首字母大小写是React区分标签的方法

  • 组件中返回的jsx代码必须有一个顶层代码

1
2
3
4
5
6
class Person extends React.Component {
render() {
return <div>hello</div>;
}
}
ReactDOM.render(<Person/>, document.getElementById("app"));

定义函数组件

1
2
3
4
function Person() {
return <div>hello</div>;
}
ReactDOM.render(<Person/>, document.getElementById("app"));

组件嵌套

  • 组建当作标签使用时,如果使用双标签要有结束标签,如果使用单标签要有闭合标签
1
2
3
4
5
6
7
8
9
10
11
class Son extends React.Component {
render() {
return <div><p>这是子组件</p></div>
}
}
class Father extends React.Component {
render() {
return <div><p>这是父组件</p><Son></Son></div>
}
}
ReactDOM.render(<Father/>, document.getElementById("app"));

在组件内调用数据

  • 在调用组件时传递参数,可以使用props属性中的对应属性来传递参数
  • 在组件内部的props是只读的,不可以修改
  • 如果props的数据源被修改,那么组件内得到的数据也会被修改

类组件

1
2
3
4
5
6
7
class Person extends React.Component {
render() {
console.log(this.props);
return <div>hello {this.props.name}</div>;
}
}
ReactDOM.render(<Person name="world"/>, document.getElementById("app"));

函数组件

1
2
3
4
5
function Person(props) {
console.log(props);
return <div>hello {props.name}</div>;
}
ReactDOM.render(<Person name="world"/>, document.getElementById("app"));

设置props的初始值

类组建

  • 通过定义defaultProps来定义props的初始值
1
2
3
4
5
6
7
8
9
10
class Person extends React.Component {
static defaultProps = {
name: "world"
};
render() {
console.log(this.props);
return <div>hello {this.props.name}</div>;
}
}
ReactDOM.render(<Person/>, document.getElementById("app"));

函数组件

  • 直接在组件中添加一个defaultProps属性,就可以初始化props值
1
2
3
4
5
6
7
function Person(props) {
return <div>hello {props.name}</div>;
}
Person.defaultProps = {
name: "world"
};
ReactDOM.render(<Person/>, document.getElementById("app"));

双标签内的子标签

  • 通过调用props.children属性来调用双标签内的子标签

类组建

1
2
3
4
5
6
class Person extends React.Component {
render() {
return <div>hello {this.props.children}</div>;
}
}
ReactDOM.render(<Person><span>world</span></Person>, document.getElementById("app"));

函数标签

1
2
3
4
function Person(props) {
return <div>hello {props.children}</div>;
}
ReactDOM.render(<Person><span>world</span></Person>, document.getElementById("app"));

this绑定

  • 当通过子组件的函数调用父组件的函数时,需要使用构造器constructor进行this绑定,把父组件的this绑定到子组件的this上,否则函数中的this都是undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Child extends React.Component {
constructor() {
super();
this.click = this.click.bind(this);
}
click() {
this.props.onCli();
}
render() {
return <div><button onClick={this.click}>按钮</button></div>;
}
}
class Father extends React.Component {
fn() {
console.log("hello world");
}
render() {
return <div><Child onCli={this.fn}></Child></div>;
}
}
ReactDOM.render(<Father/>, document.getElementById("app"));

组件的状态

  • 通过构造器给组件添加生命周期的状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class App extends React.Component {
constructor() {
super();
// 添加state状态
this.state = {
name: "hello world"
}
// 事件函数中this绑定
this.click = this.click.bind(this);
}
click(){
// 修改state状态
this.setState({
name: "hello"
});
}
render() {
return <div>{this.state.name}<button onClick={this.click}>点击更改state</button></div>;
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
  • 函数组件不能直接添加state属性

数据双向绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class App extends React.Component {
constructor() {
super();
// 添加state状态
this.state = {
name: "hello world"
}
// 事件函数中this绑定
this.change = this.change.bind(this);
}
change(event){
// 修改state状态
this.setState({
name: event.target.value
});
}
render() {
return <div>{this.state.name}<input type="text" value={this.state.name} onChange={this.change}/></div>;
}
}
ReactDOM.render(<App/>, document.getElementById("app"));

组件生命周期

  • Mounting

    • constructor():构造器函数。
      初始化数据状态,比如state的初始化

    • render():返回一个虚拟DOM

    • componentDidMount():DOM插入后立即调用。
      网络请求
      setState()更改数据状态

  • Updating:当状态数据发生改变时触发

    • render():再次插入DOM

    • componentDidUpdate():数据状态更新之后调用
      DOM操作
      网络请求
      setState()

  • Unmounting

    • componentWillUnmount():组件卸载之前调用

      • 手动卸载组件:ReactDOM.unmountComponentAtNode(container)

    清除timer计时器
    取消网络请求
    清除componentDidMount()中创建的订阅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class App extends React.Component {
// 组件初始化
constructor() {
super();
this.state = {
name: "hello world"
}
console.log("constructor()");
// this绑定
this.click = this.click.bind(this);
}
click() {
this.setState({
name: "hello"
});
}
// DOM节点插入之后
componentDidMount() {
console.log("componentDidMount()")
}
// 组件更新之后
componentDidUpdate() {
console.log("componentDidUpdate()")
}
// 组件卸载之后
componentWillUnmount() {
console.log("componentWillUnmount()");
}
render() {
console.log("render()");
return <div>
<p>{this.state.name}</p>
<button onClick={this.click}>更新组建</button>
<button onClick={()=>{ReactDOM.unmountComponentAtNode(document.getElementById("app"))}}>卸载组件</button>
</div>
}
}
ReactDOM.render(<App/>, document.getElementById("app"));

完成

参考文献

哔哩哔哩——Python小清风