React初体验--谁说前端很简单的??

预备知识

  • Node.js: 基于Chrome V8引擎的JavaScript 运行环境,使用了事件驱动、非阻塞I/O的模式
  • npm: node包管理工具
  • webpack:前端资源加载、打包工具,根据模块的依赖生成静态资源
  • ES6: js语言标准

React核心思想:声明式编程(注重结果而非实现),组件化(自定义label),虚拟DOM(减少渲染次数,diff算法)

环境搭建

利用脚手架create-react-app来安装react

1
2
3
4
5
npm install -g create-react-app  //安装脚手架

C:\Users\18771\AppData\Roaming\npm\create-react-app -> C:\Users\18771\AppData\Roaming\npm\node_modules\create-react-app\index.js
+ create-react-app@3.3.0
added 91 packages in 96.802s
1
create-react-app my-app //会自动创建好Server Webpack 和 Babel

此时报错:bash: create-react-app: command not found

应该是路径问题 改用命令npx create-react-app my-app , 还是不行

npm root命令寻找npm 的根目录发现是 C:\Users\18771\node_modules 解决方法参考

1
2
npm config set prefix /usr/local
npm i -g react-create-app

又报错 Unexpected end of JSON input while parsing near '...5mVz30VuXnFvjkEbnA5Oc'

进入 C:\Users\18771\AppData\Roaming\npm-cache 目录 执行 npm cache clean --force 解决方法参考

重新安装

切换到要创建项目的目录,执行 npx create-react-app my-app

报错 :

1
2
npm ERR! code EINTEGRITY
npm ERR! sha512-k1g3gRQ4fwfJoIfgUpz78AovicSWKFANmvTfkAHP24MgJHjWfZI6ya7tsQZt1sLczvP4G9BE5G5MgADHdmJB/w== integrity checksum failed when using sha512: wanted sha512-k1g3gRQ4fwfJoIfgUpz78AovicSWKFANmvTfkAHP24MgJHjWfZI6ya7tsQZt1sLczvP4G9BE5G5MgADHdmJB/w== but got sha512-WkbI2UIeavrFcTouh/pYNWeOYEQFVDIs8JjFZLRlZPSyyYVpmqLA0jN+BTmrDVMN23qY0wBFktjQhVBJ1PO8Yw==. (698 bytes)

似乎是package-json 文件的问题

执行 npm cache verify

再次尝试成功创建

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
npx create-react-app my-app

npm WARN react-scripts@3.3.0 requires a peer of typescript@^3.2.1 but none is installed. You must install peer dependencies yourself.
npm WARN sass-loader@8.0.0 requires a peer of node-sass@^4.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN sass-loader@8.0.0 requires a peer of sass@^1.3.0 but none is installed. You must install peer dependencies yourself.
npm WARN sass-loader@8.0.0 requires a peer of fibers@>= 3.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN tsutils@3.17.1 requires a peer of typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta but none is installed. You must install peer dependencies yourself.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.11 (node_modules\jest-haste-map\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.11: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.11 (node_modules\chokidar\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.11: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.1.2 (node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

removed 1 package in 29.248s

Initialized a git repository.

Success! Created my-app at C:\Users\18771\Desktop\my_app\my-app
Inside that directory, you can run several commands:

npm start
Starts the development server.

npm run build
Bundles the app into static files for production.

npm test
Starts the test runner.

npm run eject
Removes this tool and copies build dependencies, configuration files
and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:

cd my-app
npm start

执行start 会自动在本地3000端口打开一个页面


在VS Code中安装 Simple React SnippetsPrettier插件

同时在setting.json中添加一行"editor.formatOnSave": true, 自动格式化后保存

在Chrome中添加插件 React Developer Tools

实践

Babel 可以将 JSX 翻译为 react

boostraps

为了设计CSS ,安装bootstrap ,直接在VS Code 的集成终端输入 npm i bootstrap@4.1.1

在index.js 中导入 import "bootstrap/dist/css/bootstrap.css";

components

基本用法

新建counter.jsx文件写入组件代码

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, { Component } from "react"; //imrc
class Counter extends Component {
//cc
state = {
count: 1,
//imageUrl: "https://picsum.photos/200",
tags: ["tag1", "tag2", "tag3"]
};
styles = {
fontWeight: "bold",
fontSize: 20
};

render() {
return (
<React.Fragment>
<span style={this.styles} className={this.getBadgeClasses()}>
{this.formatCount()}
</span>
<button className="btn btn-secondary btn-sm">Increment</button>
<ul>
{this.state.tags.map(tag => (
<li key={tag}>{tag}</li>
))}
</ul>
<img src={this.state.imageUrl} alt="" />
</React.Fragment>
);
}

getBadgeClasses() {
let classes = "badge m-2 badge-";
classes += this.state.count === 0 ? "warning" : "primary";
return classes;
}

formatCount() {
const { count } = this.state;
return count === 0 ? "Zero" : count;
}
}

export default Counter;
  • 由于装了插件,可以直接用简写;因为render()只能默认产生一个元素,所以多个标签要用<React.Fragment>包起来;
  • {}可以引用类中的成员或方法;
  • className=”btn btn-secondary btn-sm”是bootstrap中的样式;
  • style={this.styles} 自定义样式;
  • classes += this.state.count === 0 ? “warning” : “primary”; 可按数值变化样式
  • {this.state.tags.map(tag =>
  • {tag}
  • )} map是一种遍历方法,为每个值设置一个key是为了react能区分出元素
  • 除了用class,还可以用stateless funciton 来写组件(sfc) 这也是一种比较常用的写法,只是不能用this关键字

然后在index.js中引用

1
2
3
import Counter from "./components/counter";

ReactDOM.render(<Counter />, document.getElementById("root"));
按不同条件进行渲染
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
import React, { Component } from "react"; 
class Counter extends Component {
state = {
count: 1,
tags: ["tag1", "tag2", "tag3"]
};

renderTags() {
if (this.state.tags.length === 0) return <p>There are no tags!</p>;
return (
<ul>
{this.state.tags.map(tag => (
<li key={tag}>{tag}</li>
))}
</ul>
);
}

render() {
return (
<React.Fragment>
{this.state.tags.length === 0 && "Please create a new tags!"}
{this.renderTags()}
</React.Fragment>
);
}
}

export default Counter;
  • this.state.tags.length === 0 && “Please create a new tags!” 在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, { Component } from "react"; //imrc
class Counter extends Component {
//cc
state = {
count: 1
};

constructor() {
super();
this.handleIncrement = this.handleIncrement.bind(this);
}

handleIncrement() {
this.setState({ count: this.state.count + 1 });
}

render() {
return (
<React.Fragment>
<span className={this.getBadgeClasses()}>{this.formatCount()}</span>
<button
onClick={this.handleIncrement}
className="btn btn-secondary btn-sm"
>
Increment
</button>
</React.Fragment>
);
}

getBadgeClasses() {
let classes = "badge m-2 badge-";
classes += this.state.count === 0 ? "warning" : "primary";
return classes;
}

formatCount() {
const { count } = this.state;
return count === 0 ? "Zero" : count;
}
}

export default Counter;
  • button 中添加 *onClick= {this.handleIncrement} —注意没有跟() 并写好handleIncrement() 方法
  • this.handleIncrement = this.handleIncrement.bind(this); 主要是为了将this与当前的Counter 对象绑定—-还可以用箭头函数
  • this.setState({ count: this.state.count + 1 }); 需要用setstate显式更改状态才能显示在页面上
复合组件

props可以作为一个参数传入,将任意类型的数据传给组件,但是不能修改,定义时也要声明为static

state是在组件内部使用并可以修改的

父组件和子组件通信可以通过props进行信息传递,当子组件要更新父组件时,可以让父组件传一个回调函数给子组件,子组件通过调用实现父组件更新——很常用的一种做法

同级之间如果要相互通信,可以将一个的函数上升为共同的父组件的回调函数;或者采用react的上下文方式,子组件可以直接访问祖先组件的属性和方法

为避免写太多this.props ,可以定义 const {func1, func2...} = this.props 选出要用的方法,之后就可以不加this.props前缀了

分清是模块控制还是全局控制

lifecycle&hooks

  • 挂载期:constructor()—先调用super() ; render() —所有父子组件渲染完后才会挂载; componentDidMount() —常用于Ajax与Server交互
  • 更新期:componentDidUpdate()
  • 卸载期: componentWillUnmount() —从DOM树中删除组件之前调用,用于做一些数据清零工作避免内存泄露

每个阶段都有对应的钩子函数,按序执行

只能用于class组件不能用于stateless funciton组件

tips

  1. 同时修改前后标签,选中标签,Ctrl+d
  2. 为了更好的封装,选中要转为方法的语句,右键,vscode提供了一个refactor功能,可以自动抽取出一个方法
  3. true && ‘hi’ —> ‘hi’ 在js中 &&结果为真时,返回最后一个表达式
  4. bootstrap一些设计案例,甚至可以直接搜索如“Navbar”, 会给出很多例子
  5. react同时在内存保存有新和旧两份virtualDOM,通过比对,决定只需要对哪些部分进行更新