React 起步

react

第一眼看到React,其实我是拒绝的。一堆的HTMLJavascript混合在一起不闲热么!而且这不是我们应该极力避免的吗?然而依旧受那么前端攻城师的追捧…

1、React是什么?

  • 是一个前端界非常流行的javascript库,目前github上的星星数已经超过1w
  • 是一个开源项目,由牛逼的Facebook公司创建并维护
  • 不是一个前端框架
  • 用于构建前端用户界面(UI)
  • 是一个MVC应用(Model-View-Controller)的视图层

React的一个最重要的功能是你可以创建类似可自定义、可重复利用的HTML组件(components),能够快速、高效构建用户界面。同时通过使用 stateprops技术简化了数据的存储和处理。

2、如何开始?

我们有多种方式开始使用React,但通常学习一个新东西,我们习惯从最简单的开始接触,是吧。

2.1 静态模板文件

作为尝鲜,我们先在一个HTML模板上开始,就像当年Jquery(虽然属于jquery的时代已经结束〒_〒)一样。

ok… 官方提供的模板如下:

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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React</title>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<!-- Don't use this in production: -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
// 这里写React代码
</script>
<!--
Note: this page is a great way to try React but it's not suitable for production.
It slowly compiles JSX with Babel in the browser and uses a large development build of React.

Read this section for a production-ready setup with JSX:
https://reactjs.org/docs/add-react-to-a-website.html#add-jsx-to-a-project

In a larger project, you can use an integrated toolchain that includes JSX instead:
https://reactjs.org/docs/create-a-new-react-app.html

You can also use React without JSX, in which case you can remove Babel:
https://reactjs.org/docs/react-without-jsx.html
-->
</body>
</html>

注意: 改模板在头部使用了CDN加载了最新版的ReactReact-domBabelbody中有一个idrootdiv。script标签的类型为text/babel,表示告诉babel要对标签内的脚本进行编译

各个js库介绍如下:

  • React:使用React高级API必须的核心库
  • React Dom: React操作dom的核心库
  • Babel: 一个Javacript编译工具,通过它可以让我使用最新的ES6+特性

首先,使用ES6语法创建一个叫HelloReactReact组件。

1
2
3
4
5
6
7
class HelloReact extends React.Component {
render() {
return (
<h1>Hello React!!!</h1>
);
};
}
  • render()是React组件中唯一必须的方法,用于渲染Dom的节点。
  • render方法内部的return返回一个类似HTML的节点。注意到这里不是返回一个String,不能使用引导括起来。这就是著名的jsx
  • 最后,使用React DOM 的render()方法,将刚才创建的HelloReact组件渲染到htmlroot节点。
1
ReactDOM.render(<HelloReact />, document.getElementById('root'));

完整代码如下,相信如果没有调错代码,用浏览器打开页面就一定能看到硕大的“Hello React!!!”字样。。。

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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React</title>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<!-- Don't use this in production: -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
class HelloReact extends React.Component {
render() {
return (
<h1>Hello React!!!</h1>
);
};
}
ReactDOM.render(<HelloReact />, document.getElementById('root'));
</script>
</body>
</html>

image

这种方式仅仅适合运行一些简单的demo,不适合用在线上项目。因为babel编译时非常耗时的。可以看到在打开页面时会卡顿一小会,这是因为babel要将脚本编译。单纯一个“hello react”就已经很慢了,要是业务多些就更龟速。一般的,React官方推荐使用 Create React App创建可用于生产的React应用,当然完全可以自己DRY

2.2 Create React App

Create React App, React官方推出的用于快速创建React应用的脚手架。整合了诸如:热更新、webpack打包编译、JSX、ES6、auto-prefix css、eslint等功能。

安装命令如下:(确保nodejs版本> 5.2)

1
npx create-react-app my-app

漫长的等待… 完成之后,运行:

1
2
cd my-app
npm start

此时,会在本地启动一个监听3000端口的node服务,访问http://localhost:3000即可看到效果。

2.3 React Developer Tools

React提供了一个叫React Developer Tools的浏览器扩展,可以方便调试。chrome扩展下载第地址: React DevTools for Chrome

安装好以后,可以看到开发控制台多了一个React标签,如下图。通过该扩展,你可以查看组件原始代码。当页面组件复杂度高时候,改扩展更见奇效。

image

基本上,React的相关工具都熟悉了,那么就可以接入往下学习了。

3、JSX

可以你已经发现,React中有比较多类似HTML的代码,但又不是HTML。这些奇怪的标签语法就是鼎鼎大名的JSX, 一种JavaScript的语法扩展。

1
const heading = <h1 className="site-heading">Hello, React</h1>;

JSX乍看起来可能比较像是模版语言,但事实上它完全是在JavaScript内部实现的。而且虽然React官方虽然推荐使用,但也并非是强制性的。在JSX背后,是通过运行createElement创建标签的,完全可以用纯javascript代码代替JSX,如:

1
2
3
4
5
const heading = React.createElement(
'h1',
{className: 'site-heading'},
'Hello, React!'
);

事实上JSX更接近于Javascript而不是HTML,所以在编写JSX代码的时候需要注意以下几点:

  • 必须使用 className 代替html上的class属性,因为classJavascript属于保留关键字。
  • JSX中,属性和方法必须使用驼峰写法。如onclick需要改为onClick
  • 自我关闭的标签必须在尾部加上/,如 <img />

Javascript表达式可以通过花括号{}嵌入到JSX代码中。花括号内可以是变量,函数及属性。

1
2
const name = 'React';
const heading = <h1>Hello, {name}</h1> ;

4、Components(组件)

我们已经创建过HelloReactReact组件。通常React应用是由大大小小组件拼装而成的,而React中组件可以分为两类:class componentssimple components

4.1、 Class Components

一个简单的class components如下:

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
import React, {Component} from 'react';

class Table extends Component {
render() {
return (
<table>
<thead>
<tr>
<th>名字</th>
<th>性别</th>
</tr>
</thead>
<tbody>
<tr>
<td>王帅</td>
<td>男</td>
</tr>
<tr>
<td>李雷</td>
<td>男</td>
</tr>
<tr>
<td>韩梅梅</td>
<td>女</td>
</tr>
<tr>
<td>亚瑟</td>
<td>召唤师</td>
</tr>
</tbody>
</table>
);
}
}

export default Table;

以上便是一个简单的class components组件,其他不用多解释。

4.2、Simple Components

Simple Components说白了其实就是一个函数,这类组件不需要使用class关键词。上面的组件可以改写为:

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
39
40
41
42
43
44
45
46
47
48
49
50

// 表头组件
const TableHeader = () => {
return (
<thead>
<tr>
<th>名字</th>
<th>性别</th>
</tr>
</thead>
);
}

// 表内容组件
const TableBody = () => {
return (
<tbody>
<tr>
<td>王帅</td>
<td>男</td>
</tr>
<tr>
<td>李雷</td>
<td>男</td>
</tr>
<tr>
<td>韩梅梅</td>
<td>女</td>
</tr>
<tr>
<td>亚瑟</td>
<td>召唤师</td>
</tr>
</tbody>
);
}

// 使用以上简单组件组合成更复杂的组件
class Table extends Component {
render() {
return (
<table>
<TableHeader />
<TableBody />
</table>
);
}
}

export default Table;

注意: 一个class component必须包含render()方法,同时内部的return语句必须返回只有一个父节点的xml

5、Props

PropsReact的一大核心特点。为降低组件之间的耦合,及提高组件的复用。约定只能通过props给组件传递数据。上面的列子简单改写如下:

–table.js

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
import React from 'react';

const TableBody = (props) => {
const rows = props.characterData.map((row, index) => {
return (
<tr key={index}>
<td>{row.name}</td>
<td>{row.sex}</td>
</tr>
);
});
return <tbody>{rows}</tbody>;
}
const TableHeader = () => {
return (
<thead>
<tr>
<th>名字</th>
<th>性别</th>
</tr>
</thead>
);
}

class Table extends React.Component {
render() {
const { characterData } = this.props;

return (
<table>
<TableHeader />
<TableBody characterData={characterData} />
</table>
);
}
}

export default Table;

–app.js

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

import React from 'react';
import Table from './table';

class App extends React.Component {
render() {
const characterData = [
{
'name': '王帅',
'sex': '男'
},
{
'name': '李雷',
'sex': '男'
},
{
'name': '韩梅梅',
'sex': '女'
},
{
'name': '亚瑟',
'sex': '召唤师'
}
];
return (
<div className="container">
<Table characterData={characterData}/>
</div>
);
};
}

export default App;

注意: 在表格的每一行都加上了一个key值,这是为了React在遍历渲染的时候减少性能消耗。

5、State

state简单实用列子如下:

–app.js

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

import React from 'react';
import Table from './table';
import Form from './form';

class App extends React.Component {
state = {
characters: []
};
render() {
return (
<div className="container">
<Table
characterData={this.state.characters}
removeCharacter={this.removeCharacter}
/>
<Form handleSubmit={this.handleSubmit}/>
</div>
);
};
removeCharacter = index => {
const { characters } = this.state;

this.setState({
characters: characters.filter((character, i) => {
return i !== index;
})
})
}

handleSubmit = character=> {
this.setState({characters: [...this.state.characters, character]});
}
}

export default App;

–form.js

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import React from 'react';

class Form extends React.Component {
constructor(props) {
super(props);

this.initialState = {
name: '',
sex: ''
};

this.state = this.initialState;
}

handleChange = event => {
const {name, value} = event.target;

this.setState({
[name]: value
});
};

submitForm = () => {
console.log(this.props);
console.log(this);
console.log({a:1, b:2});
this.props.handleSubmit(this.state);
this.setState(this.initialState);
};
render() {
const { name, sex} = this.state;
return (
<form>
<label>Name</label>
<input
type="text"
name="name"
vale={name}
onChange={this.handleChange}
/>
<label>sex</label>
<input
type="text"
name="sex"
vale={sex}
onChange={this.handleChange}
/>
<input
type="button"
value="Submit"
onClick={this.submitForm}
/>
</form>
);
};
}

export default Form;

–table.js

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
39
40
41
42
43
import React from 'react';

const TableBody = (props) => {
const rows = props.characterData.map((row, index) => {
return (
<tr key={index}>
<td>{row.name}</td>
<td>{row.sex}</td>
<td><button onClick={() => props.removeCharacter(index)}>Delete</button></td>
</tr>
);
});
return <tbody>{rows}</tbody>;
}
const TableHeader = () => {
return (
<thead>
<tr>
<th>名字</th>
<th>性别</th>
<th>操作</th>
</tr>
</thead>
);
}

class Table extends React.Component {
render() {
const { characterData, removeCharacter } = this.props;

return (
<table>
<TableHeader />
<TableBody
characterData={characterData}
removeCharacter={removeCharacter}
/>
</table>
);
}
}

export default Table;

index.js

1
2
3
4
5
6
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';
import './index.css';

ReactDOM.render(<App />, document.getElementById('root'));

大致体验了一下React的开发…