chapter1-React基本语法
# chapter1-React基本语法
- 今日学习目标
- 一、React简介
- 二、React特点
- 三、创建React脚手架项目
- 四、hello_world程序编写
- 五、VSCode中JSX的编写优化
- 六、ES5与ES6面向对象复习
- 1、ES5封装与继承
- 2、ES6封装与继承
- 七、组件化开发
- 八、JSX语法糖
- 九、VSCode中,代码片段扩展的安装
- 十、事件、state与setState
- 十一、state的简写
- 十二、setState是异步的
- 十三、累加
- 十四、双向数据绑定
- 十五、state中数组的修改
- 十六、【验证】为什么修改state只能通过setState
- 十七、【验证】key用id好还是index好?
- 十八、案例:Tab栏
- 十九、作业
# 今日学习目标
- 了解React的MVC设计理念
- 能够使用脚手架创建React项目
- 掌握JSX语法
- 掌握组件化开发
# 一、React简介
React 是一个用于构建用户界面的 JavaScript 库,它是 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。React 主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)。由于拥有较高的性能,且代码逻辑非常简单,越来越多的人已开始关注和使用它。
中文官网:https://react.docschina.org
# 二、React特点
1、声明式设计
react是面向数据编程,不需要直接去控制dom,你只要把数据操作好,react自己会去帮你操作dom,可以节省很多操作dom的代码。这就是声明式开发。
2、使用JSX语法
JSX 是 JavaScript 语法的扩展。React 开发大部分使用 JSX 语法(在JSX中可以将HTML于JS混写)。
3、灵活
react所控制的dom就是id为root的dom,页面上的其他dom元素你页可以使用jq等其他框架 。可以和其他库并存。
4、使用虚拟DOM、高效
虚拟DOM其实质是一个JavaScript对象,当数据和状态发生了变化,都会被自动高效的同步到虚拟DOM中,最后再将仅变化的部分同步到DOM中(不需要整个DOM树重新渲染)。
5、组件化开发
通过 React 构建组件,使得代码更加容易得到复用和维护,能够很好的应用在大项目的开发中。
6、单向数据流
react是单向数据流,父组件传递给子组件的数据,子组件能够使用,但是不能直接通过this.props修改。 这样让数据清晰代码容易维护。
# 三、创建React脚手架项目
# 1、构建一个名为 demo
项目
# React不再支持全局安装create-react-app,因此官方建议使用npx来安装
npx create-react-app demo
2
注意:项目路径不能有中文,不能有怪异符号,包括“!”也不行!!!!
如图,创建成功:
如果安装过于缓慢,或者有报错,请参考下文:
https://www.jianshu.com/p/91bf816cc1cc
# 2、进入这个文件夹,并且跑起来
cd demo && yarn start
项目运行,自动打开谷歌浏览器看见项目页面(第一次项目运行会很慢,要耐心等待)
# 四、hello_world程序编写
1、删除在src目录下所有文件
2、src目录下新建index.js文件作为入口文件
//1、引入React两个核心模块
import React from 'react';
import ReactDOM from 'react-dom';
//2、通过JSX语法将组件/标签渲染到指定标签上
ReactDOM.render(
<h1>
hello world!
</h1>
, document.getElementById('root'));
2
3
4
5
6
7
8
9
10
11
ReactDOM.render(参数1,参数2)
参数1 是JSX语法的标签/组件
参数2 是要把参数1这个标签渲染到的位置
# 五、VSCode中JSX的编写优化
在vscode中的 文件>首选项>设置
中,直接搜索 include Language
,进入 settings.json
:
找到 "emmet.includeLanguages"
字段,添加:
"emmet.includeLanguages": {
"javascript": "javascriptreact"
}
2
3
搜索 trigger
,找到如图位置,打钩:
设置后就可以通过 标签名+Tab 键快速码出标签
# 六、ES5与ES6面向对象复习
# 1、ES5封装与继承
我们声明一个构造函数:
function Animal(vname){
this.name = vname;
this.sayInfo = function(){
console.log('这是一只'+this.name)
}
}
var animal = new Animal('动物')
animal.sayInfo();// 这是一只动物
2
3
4
5
6
7
8
9
现在我想用一个Dog类来继承这个Animal类,那么就可以:
function Animal(vname){
this.name = vname;
this.sayInfo = function(){
console.log('这是一只'+this.name)
}
}
function Dog(vname){
// 在子类型构造函数的内部调用超类型构造函数,通过使用apply()和call()方法可以在新创建的对象上执行构造函数
Animal.call(this, vname);
}
var dog = new Dog('狗狗');
dog.sayInfo();// 这是一只狗狗
2
3
4
5
6
7
8
9
10
11
12
13
14
# 2、ES6封装与继承
我们声明一个类:
class Animal {
// 构造器
constructor(vname){
this.name = vname;
}
sayInfo(){
console.log('这是一只'+this.name);
}
}
let animal = new Animal('动物');
animal.sayInfo();
2
3
4
5
6
7
8
9
10
11
12
再定义一个Dog类来继承Animal类:
class Animal {
// 构造器
constructor(vname){
this.name = vname;
}
sayInfo(){
console.log('这是一只'+this.name);
}
}
class Dog extends Animal {
constructor(props){
// 超类,接收父类传递过来的属性
super(props)
}
}
let dog = new Dog('狗狗');
dog.sayInfo()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 七、组件化开发
在React的项目中,都是使用组件化开发的模式,所以,我们可以把刚才的hello world的h1定义为一个组件:
定义组件分为3步:
1、导入React核心模块
2、定义组件类
3、导出组件
在src目录下新建App.js文件:
//1、导入React核心模块
import React from 'react'
//2、定义组件类
class Hello extends React.Component{ //类
render(){ //函数
return ( //返回值
<div>
hello world !!! 我是组件222
</div>
)
}
}
//3、导出组件
export default Hello
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
在需要引入该组件的index.js中,导入,就可以直接使用这个组件了:
//import 组件名 from '文件路径'
import Hello from './App' //1、导入Hello组件 (首字母必须大写)
ReactDOM.render(
<Hello /> // 2、使用Hello组件 (首字母必须大写)
, document.getElementById('root'));
//注意:Hello是组件名,在使用的时候就应该写JSX标签写法,而不能直接写Hello
2
3
4
5
6
7
8
!!注意:
1、定义组件的时候,return 后面只能有一个根标签,不能有多个,但这个标签内部可以有其他多个标签
2、使用组件的时候,首字母必须大写
3、如果最外层实在不想放置一层div根目录,可以使用 <></>
空标签代替
# 八、JSX语法糖
React 使用 JSX 来替代常规的 JavaScript。JSX 是一个看起来很像 XML 的 JavaScript 语法扩展,在React中会被babel编译为JavaScript。
# 1、JSX的特点
- JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
- 它是类型安全的,在编译过程中就能发现错误。
- 使用 JSX 编写模板更加简单快速。
# 2、JSX几个注意的格式:
1、React的JSX是使用大写和小写字母来区分本地组件和HTML组件
(如:html就用 div p h1 , 组件就用 MyButton App Home List等 )
2、JSX和html的标签属性的区别
HTML标签属性 | JSX | 原因 |
---|---|---|
for | htmlFor | for在JS中为for循环关键字 |
class | className | class在JS中为声明类关键字 |
style | 需使用JS对象(使用双花括号--{{}}) |
组件中:
import React, { Component } from 'react'
var MyImg = require('./assets/01.jpg')
export default class App2 extends Component {
render() {
return (
<>
<label htmlFor="ipt">label</label>
<input type="text" id="ipt" className="ipt" style={{background: 'pink', color: 'green'}} />
<hr/>
<img src={MyImg} alt=""/>
</>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
注意:图片查找路径在src目录,所以引入的时候从src目录触发查找文件
# 3、React的JSX创建出来的是虚拟DOM,而不是HTML
在index.js中:
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App3'
ReactDOM.render(
<App />,
document.getElementById('root')
);
const virtualDOM = React.createElement('div')
const realDOM = document.createElement('div')
let virtualNum = 0;
for(var key in virtualDOM){
virtualNum++;
}
let realNum = 0;
for(var key in realDOM){
realNum++;
}
console.log('虚拟DOM的属性个数:'+virtualNum)// 7
console.log('真实DOM的属性个数:'+realNum)// 298
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 4、JSX变量引用、三目运算符、for循环
在JSX中,想要调用变量,需要在return中直接使用单花括号--**{}**调用
index.js中:
//第四节:JSX中变量引用、三目运算符、for循环的使用
import App3 from './App3'
//2、通过JSX语法将标签/组件渲染到指定标签上
ReactDOM.render(
<App3 />
, document.getElementById('root'));
2
3
4
5
6
7
App3.js中
import React, { Component } from 'react'
let name = "小明", num1=20, num2=30, arr=[1, 2, 3, 4, 5]
export default class App3 extends Component {
render() {
return (
<div>
{/* 这是注释的格式 */}
{/* JSX中引用变量需要加单花括号 */}
<p>{name}</p>
{/* 三目运算符的使用 */}
<p>num1和num2中比较大的是:{num1>num2? num1: num2}</p>
<p>{gender === 0 ? '男' : (gender === 1 ? '女' : '保密')}</p>
{/* for循环的使用 */}
<ul>
{/* 数组名.map(函数) */}
{
//格式1:
arr.map((v,k)=>(
<li key={k}>{v}</li>
))
}
{
//格式2:可以把下面的大括号和return换成小括号
arr.map((v,k)=>{
return <li key={k}>{v}</li>
})
}
</ul>
</div>
)
}
}
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
总结:在jsx语法中,需要书写js代码的时候,请先加上{ } 再书写js语法
# 九、VSCode中,代码片段扩展的安装
在扩展搜索栏中搜索react,选择下图的扩展进行安装
安装后,通过 rcc+Tab ,得到组件的代码片段, clg+回车 快速得到console.log()代码
# 十、事件、state与setState
App5.js中:
//事件讲解
import React, { Component } from 'react'
//1、实现点击弹框效果(事件基本格式)
export default class App5 extends Component {
handleClick(){
alert(132456)
}
render() {
return (
<div>
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
}
//2、实现累加的功能 (状态的使用1)
export default class App4 extends Component {
constructor(props){
super(props)
this.state = {
num: 0
}
// 按钮3的函数写法的前提
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
this.setState({
num: this.state.num+1
})
}
render() {
return (
<div>
<h2>{ this.state.num }</h2>
{/* 通过bind来改变this的指向 */}
{/* <button onClick={this.handleClick.bind(this)}>按钮1</button> */}
{/* 箭头函数默认没有this,所以this指向数组对象 */}
{/* <button onClick={()=>this.handleClick()}>按钮2</button> */}
<button onClick={this.handleClick}>按钮3</button>
</div>
)
}
}
//3、实现双向数据绑定 (状态的使用2)
export default class App5 extends Component {
constructor(p){
super(p)
this.state = {
name: "你好,世界"
}
}
handleChange(e){
console.log(e.currentTarget.value)
console.log(e.target.value)
this.setState({
name: e.target.value
})
}
render() {
return (
<div>
<h2>{ this.state.name }</h2>
<input type="text" value={this.state.name} onChange={this.handleChange.bind(this)} />
</div>
)
}
}
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# 十一、state的简写
App6.js中:
import React, { Component } from 'react'
export default class App6 extends Component {
// 写在construtor中的state也可以简写
state = {
username: "张三"
}
render() {
return (
<div>
<h1>{this.state.username}</h1>
<button onClick={this.handleClick.bind(this)}>按钮</button>
</div>
)
}
handleClick(){
this.setState({
username: "李四"
})
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 十二、setState是异步的
我们可以通过以下代码验证 setState
是异步或是同步:
import React, { Component } from 'react'
export default class App5 extends Component {
state = {
msg: "你好世界"
}
render() {
return (
<div>
<h2>{this.state.msg}</h2>
<button onClick={this.handleClick.bind(this)}>改变msg</button>
</div>
)
}
handleClick() {
// this.setState是异步还是同步呢?
this.setState({
msg: "hello world" // 修改msg
})
console.log(this.state.msg) // 同步则输出“hello world”,异步则输出“你好世界”
// 验证结果:this.setState是异步的
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
另外 setState
还有回调函数:
this.setState({
msg: "hello world"
}, ()=>{
console.log(this.state.msg); // hello world
})
2
3
4
5
如果想要在修改数据后调用该数据,就可以使用回调函数。
# 十三、累加
我们来实现一个累加的效果:
点击按钮则数字加1
import React, { Component } from 'react'
export default class App7 extends Component {
state = {
num: 0
}
render() {
return (
<div style={{textAlign: 'center'}}>
<h2>{this.state.num}</h2>
<button onClick={this.addNum.bind(this)}>累加</button>
</div>
)
}
// 累加
addNum(){
// num++ (num = num + 1) num++是会修改num的
// this.state.num++ this.state.num被修改了
// 唯一能修改state的方法是setState
this.setState({
num: this.state.num+1
})
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
这里考点在于 ++
会修改原数据,而在 react 中,修改state中的数据必须通过setState,因此这里不能对 this.state.num
直接进行 ++
操作。
# 十四、双向数据绑定
React中没有类似vue的 v-model
指令,因此要实现双向数据绑定,只能操作 value
和 onChange(或onInput)事件
。
import React, { Component } from 'react'
// 双向数据绑定
export default class App8 extends Component {
state = {
msg: "你好世界"
}
render() {
return (
<div>
<input type="text" value={this.state.msg} onChange={this.changeFn.bind(this)} />
<h2>{this.state.msg}</h2>
</div>
)
}
// input的输入事件
changeFn(e){
/*
currentTarget与target有什么区别?
当只有一层元素的时候,两者没区别
当存在两层元素嵌套时,就会有冒泡现象,那么真正指向被你事件触发的元素,是currentTarget
*/
// console.log(e.currentTarget)
// console.log(e.target)
this.setState({
msg: e.currentTarget.value
})
}
}
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
这里做个补充:
如果input上写的是箭头函数,那么应当这么写,才能获取到事件对象e:
<input type="text" value={this.state.msg} onChange={(e)=>this.changeFn(e)} />
# 十五、state中数组的修改
我们对数组进行操作时,大部分数组方法会修改到原数组,因此建议先将数组深拷贝一次,修改后再重新赋值:
import React, { Component } from 'react'
export default class App9 extends Component {
state = {
arr: ["张飞", "赵云", "刘备"]
}
render() {
return (
<div>
<ul>
{
this.state.arr.map((item, index)=>{
return <li key={index}>{item}</li>
})
}
</ul>
<button onClick={this.addFn.bind(this)}>追加一项到arr的最后</button>
</div>
)
}
// 追加一项到arr的最后
// 修改state的方式只能是setState
// 数组的push方法会修改原数组
// this.state.arr.push() 会造成直接修改了state中的arr
// 修改数组,不能直接修改this.state.arr
addFn(){
// 如果我声明一个变量,等于this.state.arr,并且让这个变量和this.state.arr完全脱离关系[深拷贝]
let newArr = JSON.parse(JSON.stringify(this.state.arr));
// 往newArr中push一项
newArr.push('关羽');
// 把最新的newArr赋值给arr
this.setState({
arr: newArr
})
}
}
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
# 十六、【验证】为什么修改state只能通过setState
只有setState可以重新触发render函数对页面进行再次渲染,如果直接对state进行修改,那么是无法显示到界面中的,因此想要修改state,只能通过setState。
import React, { Component } from 'react'
// 验证为什么修改state只能通过setState
export default class App10 extends Component {
state = {
msg: "你好世界"
}
render() {
return (
<div>
<h2>{this.state.msg}</h2>
<button onClick={this.changeMsg.bind(this)}>修改msg</button>
</div>
)
}
// 修改msg
changeMsg(){
// this.setState({
// msg: "hello world"
// })
this.state.msg = "hello world";
console.log('修改后的msg:'+this.state.msg)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 十七、【验证】key用id好还是index好?
当然是id好,看代码:
import React, { Component } from 'react'
export default class App11 extends Component {
state = {
books: [
{id: 0, name: '老人与海'},
{id: 1, name: '西游记'},
{id: 2, name: '三国演义'}
]
}
render() {
return (
<div>
<ul>
{
this.state.books.map((item, index)=>{
return (
// 失去了唯一标识符的意义
<li key={item.id}>
<input type="text" />
<strong>{item.name}</strong>
</li>
)
})
}
</ul>
<button onClick={this.addItem.bind(this)}>追加一项</button>
</div>
)
}
addItem(){
let obj = {id: 3, name: '红楼梦'}
// 深拷贝
let newBooks = JSON.parse(JSON.stringify(this.state.books));
newBooks.unshift(obj)
this.setState({
books: newBooks
})
// 假如追加的这一项会导致整个数组重排,那怎么办?
// 如果key使用了index,当数组重排时,就会导致索引对不上
// 解决方案就是key尽量不适用index作为唯一标识符,尽量使用id
}
}
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
# 十八、案例:Tab栏
App7.js中:
import React, { Component } from 'react'
import './com.css';
export default class App7 extends Component {
state = {
arr: ["新闻", "体育", "知识"],
activeNum: 0
}
render() {
return (
<div className="banner">
<ul>
{
this.state.arr.map((item, index) => {
// this.handleClick.bind(this, 参数)
return <li key={index} onClick={this.handleClick.bind(this, index)} className={this.state.activeNum==index ? 'active' : ''}>{item}</li>;
})
}
</ul>
<ol>
{
this.state.arr.map((item, index) => {
return <li key={index} className={this.state.activeNum==index ? 'active' : ''}>{item}</li>;
})
}
</ol>
</div>
)
}
handleClick(index){
// console.log(index) // 得到索引值
this.setState({
activeNum: index
})
}
}
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
com.css中:
*{margin: 0;padding: 0;border: 0;list-style: none;}
.banner{
width: 500px;
height: 300px;
margin: 100px auto;
border: 1px solid #000;
}
ul{
height: 60px;
line-height: 60px;
display: flex;
text-align: center;
justify-content: space-between;
}
ul li{
height: 60px;
width: 30%;
background: #efefef;
}
ul li.active{
background: darkred;
color: #fff;
}
ol li{
height: 240px;
background: darkred;
text-align: center;
line-height: 240px;
color: #fff;
display: none;
}
ol li.active{
display: block;
}
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
# 十九、作业
请使用下列数组:
var arr = [
{txt: '6月25日峡谷龙舟赛挑战局数异常修复公告', time: '06/25'},
{txt: '6月25日峡谷龙舟赛挑战局数异常说明', time: '06/25'},
{txt: '6月25日体验服停机更新公告', time: '06/25'},
{txt: '6月24日峡谷龙舟赛异常修复公告', time: '06/24'},
{txt: '6月24日“演员”惩罚名单', time: '06/24'},
{txt: '6月24日外挂专项打击公告', time: '06/24'}
];
2
3
4
5
6
7
8
完成以下效果: