部门内部培训 react 系列讲义一:React 入门

React 入门,based on React.16.4.2

本次教程基于环境、使用工具以及版本:

  • nodejs:8.11.3

  • react:16.4.2

  • npm:6.2.0

  • SPA(单页应用)

  • create-react-app:2.0.0

1. 创建项目

1.1 - 安装脚手架create-react-app

1
npm install -g create-react-app@2.0.0-next.3e165448

1.2 - 创建项目

1
create-react-app react-demo

1.3 - 运行项目

1
2
3
npm start
或者
yarn start

2. 项目结构说明

创建好项目之后的结构目录:结构目录
其中:

  • /public/index.html是整个项目的入口文件,我们可以在该文件中添加一些CDN资源,如jQuery等。

    index.html文件内容:


    P.S.:

    • 在index.html中,有一个根容器:id=“root”

    • 这个是整个项目的总容器,创建的组件最终都生成在该容器中。

    • 对于该文件,一般情况下,我们不会在这个html文件中增加我们的业务代码

  • src/index.js:跟index.html相关联,会将该js中声明的组件渲染到public/index.html中 “id=root” 的容器内

    • ReactDOM.render是react最基本的方法,用于将模板转换为HTML语言,并插入到指定的DOM节点。
    • 像上面的代码的意思就是把App这个组件转换成html后插入到root节点中。
    • 在这里,可以将 认为是整个项目的根组件,要使用App组件,需要引入该组件,见L4(第四行)
      • 通过 import App from ‘./App’ 进行引用,默认情况下,webpack自动将文件作为js文件进行处理,所以这里在引入App.js的时候,不用显式指明 “./App.js”

为什么从项目结构中并看不出任何跟webpack相关的文件?

3. 项目解包

1
npm run eject

项目解包后的入口文件、入口函数图示:

  • 解包之后的项目无法通过 npm start 这样的命令直接启动项目(解包的同时会从node_modules中删除包)

  • 此时需要运行 npm install 进行安装package.json中定义的包。

4. JSX语法

脚手架创建的react项目中的App.js,默认使用了JSX语法

  • 使用JSX语法需要导入 react

  • JSX语法与非JSX语法的对比:

Demo

Demo1 - 在JSX中,通过花括号 {} ,可以使用javascript表达式,如:

1
2
3
4
5
6
7
8
9
10
render() {
return (
<div className="App">
<span>hello react.</span>
<div id="demo">
1+1={1 + 1} //最终结果:1+1=2
</div>
</div>
);
}

Demo2 - JSX中无法使用 if-else,但可以使用三元表达式来代替,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
render() {
let isDisplayOne = true;
let one = "i am One";
let two = "i am Two";
return (
<div className="App">
<span>hello react.</span>
<div id="demo">
{isDisplayOne ? one : two} //通过三元表达式代替if-else,如果?号后面的内容过多,建议抽取后通过变量赋值
</div>
</div>
);
}

Demo3 - JSX中,html标签内部的注释需要用花括号括起来,如:

1
2
3
4
5
6
7
8
9
10
render() {
return (
<div className="App">
<span>hello react.</span>
<div id="demo">
{/* <span>test annotation</span> */}
</div>
</div>
);
}

Demo4 - JSX中,使用数组,会将数组中的内容自动按照顺序填充到html标签中,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
render() {
let arr = [
{/*如果要在jsx中使用内联属性元素,则属性值在赋值的时候需要用双花括号括起来。*/}
<span style={{ color: 'blue' }}>1.hello</span>,
<span style={{ color: 'yellow' }}>2.world</span>
];
return (
<div className="App">
<span>hello react.</span>
<div id="demo">
<div>{arr}</div>
</div>
</div>
);
}

Demo5 - 在JSX中,html中的一些元素属性:如class,for,需要使用 className 和 htmlFor 来做代替,如:

1
2
3
4
5
6
7
8
render() {
return (
<div className="App">
{/*以下内容最终生成的html为:<span class="testClass" for="using htmlFor">hello react.</span> */}
<span className="testClass" htmlFor="using htmlFor">hello react.</span>
</div>
);
}

Demo6 - jsx中,属性事件

1
2
<button onClick="XXXXX">button</button>
这里在定义点击事件的时候,不再是全小写的“onclick”,而是“onClick”--- C 大写。命名上,类似于camel命名

使用:

1
2
3
4
5
6
7
8
9
10
11
假设在声明了如下方法:
//无参方法
sayHello(){
console.log("say hello");
}
//带参方法
sayHelloTo(name) {
console.log("hello " + name);
}
通过按钮点击事件使用 sayHello方法
< div > <button onClick={this.sayHello} >button</button></div >

在调用方法的时候,需要注意以下几点:

  1. 若调用的方法不带参数,如 sayHello:

    • 若调用时方法上带上了圆括号,如 onClick={this.sayHello()},则在页面加载的时候,该方法会自动执行一遍。
    • 若调用时没有带圆括号,如 onClick={this.sayHello},则页面加载的时候,该方法不会自动执行,需要点击该按钮后才会被执行。
  2. 若调用的方法带参数,如 sayHelloTo:

    • 若调用方式为:onClick={this.sayHelloTo(‘Lucy’)},则页面加载的时候,该方法就自动执行,且之后按钮再怎么点击也无效
    • 若想让带参方法不自动执行,方法有两种:
      • 1.通过箭头函数:onClick={()=>this.sayHelloTo(“Lucy”)}
      • 2.使用bind:onClick={this.sayHelloTo.bind(this,“Lucy”)} //第一个参数表示当前指向的对象,这里传的this表示的当前js中定义的class(因为该this是JSX中的this),第二个参数开始就是调用方法的入参

5. 组件:Component

一个简单例子:在src目录中创建如下目录和文件

5.1 - 创建自定义组件:Student.js

Student.js:

1
2
3
4
5
6
import React from 'react'
function student(props){
return <div>大家好,我是学生</div>
}

export default student;

5.2 - 在App.js中使用Student组件

App.js:

1
2
3
4
5
6
7
8
9
10
11
导入组件:import Student from './Components/Student/Student';
使用组件:
render() {
return (
<div className="App" htmlFor="html for for for demo">
<Student />
<Student />
<Student />
</div>
);
}

5.3 - 组件中如何传值?

5.3.1 - 通过 props

Student.js:

1
2
3
4
5
6
import React from 'react'
function student(props){
return <div>大家好,我是学生:{props.name},班级:{props.class}</div>
}

export default student;

App.js

1
2
3
4
5
6
7
8
9
render() {
return (
<div className="App" htmlFor="html for for for demo">
<Student name="A" class="class_1"/>
<Student name="B" class="class_2"/>
<Student name="C" class="class_1"/>
</div>
);
}

5.3.2 - 通过 state

  • 什么是 state,如何使用?

    • state是定义在Component中的一个属性,组件必须是class组件继承自Component

定义 state:

App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class App extends Component {
{/*定义state*/}
state = {
students:[
{id:1, name:"John", class:"class_A"},
{id:2, name:"Steve", class:"class_C"},
{id:3, name:"Trump", class:"class_A"},
]
}

render() {
return (
<div className="App" htmlFor="html for for for demo">
<Student name={this.state.students[0].name} class={this.state.students[0].class}/>
<Student name={this.state.students[1].name} class={this.state.students[1].class}/>
<Student name={this.state.students[2].name} class={this.state.students[2].class}/>
</div>
);
}
}

如何动态修改数据?

  • 通过 this.setState()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
changeStudent = () => {
this.setState({
students: [
{ id: 1, name: "John_______", class: "class_A" },
{ id: 2, name: "Steve", class: "class_C" },
{ id: 3, name: "Trump", class: "class_A" },
]
});
}

render() {
return (
<div className="App" htmlFor="html for for for demo">
<Student name={this.state.students[0].name} class={this.state.students[0].class} />
<Student name={this.state.students[1].name} class={this.state.students[1].class} />
<Student name={this.state.students[2].name} class={this.state.students[2].class} />
<button onClick={this.changeStudent}>修改</button>
</div>
);
}

Demo1 - 数据的双向绑定:修改年级信息

App.js

    1. 增加grade信息容器
    1. 增加输入行用于更新grade信息
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
class App extends Component {
state = {
students: [
{ id: 1, name: "John", class: "class_A" },
{ id: 2, name: "Steve", class: "class_C" },
{ id: 3, name: "Trump", class: "class_A" },
],
gradeInfo:"grade one"
}
...
changeGradeInfo = (event)=>{
this.setState({
gradeInfo:event.target.value
})
}

render() {
return (
<div className="App" htmlFor="html for for for demo">
<div>{this.state.gradeInfo}</div>
...
<input type="text" onChange={ this.changeGradeInfo}></input>
<button onClick={this.changeStudent}>修改</button>
</div>
);
}
}

Demo2 - 修改学生姓名

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
class App extends Component {
...
changeStudentTo = (event,id) => {
let students = this.state.students;
let index = students.findIndex(ent=>ent.id===id);
console.log(index);
let student = students[index];
student.name = event.target.value
students[index] = student;
this.setState({
students: students
});
}

render() {
return (
<div className="App" htmlFor="html for for for demo">
<div>{this.state.gradeInfo}</div>
<Student id={this.state.students[0].id} onChangeStudentTo={this.changeStudentTo} name={this.state.students[0].name} class={this.state.students[0].class} />
<Student id={this.state.students[1].id} onChangeStudentTo={this.changeStudentTo} name={this.state.students[1].name} class={this.state.students[1].class} />
<Student id={this.state.students[2].id} onChangeStudentTo={this.changeStudentTo} name={this.state.students[2].name} class={this.state.students[2].class} />
<input type="text" onChange={ this.changeGradeInfo}></input>
<button onClick={this.changeStudent}>修改</button>
</div>
);
}
}

Student.js:

1
2
3
4
5
6
7
8
9
10

import React from 'react'
function student(props) {
return (
<div>
<div>大家好,我是学生:{props.name},班级:{props.class}</div>
<input type="text" onChange={(e)=>props.onChangeStudentTo(e, props.id)} />
</div>);
}
export default student;

这里需要注意下,react的event和我们平时认为的JS的event不一样,相关文档见此: https://reactjs.org/docs/events.html

  • 什么时候用 state?

    • 需要动态改变组建内值的时候
    • 需要使用组件的生命周期函数的时候

6. React中的样式

6.1 - 通过 import 导入样式

在Student.js同级目录创建Student.css

1
2
3
4
Student.css
div{
color:blue;
}

在Student.js中使用该样式

1
import './Student.css'

6.2 - 直接在组件中定义样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Student.js
import React from 'react'
var myStyle = {
color:"orange",
fontSize:"22px"
}

function student(props) {

return (
<div>
<div style={myStyle}>大家好,我是学生:{props.name},班级:{props.class}</div>
<input type="text" onChange={props.onChangeStudentTo.bind(this,props.id)}></input>
</div>);
}
export default student;

7. 流程控制语句

7.1 - 条件判断语句

  • 一种是在JSX中使用三元表达式

1
2
JSX语法
<div>{this.state.isShowGradeInfo?this.state.gradeInfo:""}</div>
  • 一种是在JSX语法外,先把需要判断的东西计算出结果后,直接使用结果

1
2
3
4
5
6
7
8
9
10
11
12
13
render() {
let gradeInfo = null;
if(this.state.isShowGradeInfo){
gradeInfo = this.state.gradeInfo;
}
return (
<div className="App" htmlFor="html for for for demo">
...
<div>{gradeInfo}</div>
...
</div>
);
}

7.2 - 循环语句

  • 一种是常规的for循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
render() {
...
var studentArray = [];
for(let i=0;i<this.state.students.length;i++){
studentArray.push(<Student id={this.state.students[i].id} onChangeStudentTo={this.changeStudentTo} name={this.state.students[i].name} class={this.state.students[i].class} />)
}
return (
<div className="App" htmlFor="html for for for demo">
...
{/* {studentArray} */}
{this.state.students.map(student=>{
return <Student id={student.id} onChangeStudentTo={this.changeStudentTo} name={student.name} class={student.class} />
})}
...
</div>
);
}
  • 一种是使用map方法(推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
render() {
...

return (
<div className="App" htmlFor="html for for for demo">
...
{
this.state.students.map(student=>{
return <Student id={student.id} onChangeStudentTo={this.changeStudentTo} name={student.name} class={student.class} />
});
}
...
</div>
);
}