react+antd 项目笔记

Atwood 定律

Any application that can be written in JavaScript, will eventually be written in JavaScript. (任何能够用JavaScript实现的应用,最终都必将由JavaScript实现。) – Jeff Atwood

记录react学习进程,以及使用 antdreactdemo 时候遇到的问题。

项目地址:https://github.com/popring/ofo-ms/tree/ofo-ms-js

记录学习React心路历程,以及开发过程中遇到的疑难杂症

React

React 生命周期(按顺序从上往下)

Mounting

lifecycle namedescription
constructorreact 组件挂载前会调用它的构造函数
componentWillMount过时,挂载前调用
renderclass组件中唯一必须实现的方法,最好为纯函数,代码更加简洁易懂
componentDidMount组件挂载后调用

Updating

lifecycle namedescription
componentWillReceiveProps`过时,在已挂载的组件接收新的props之前被调用
shouldComponentUpdate(nextProps, nextState)根据此函数的返回值判断 React 组件的输出是否受当前 state 或 props 更改的影响,默认为truestateprops 更新时是否需要重新渲染视图
componentWillUpdate过时,当组件接收新的props或state时,会在渲染之前调用
componentDidUpdate(preveProps, prevState, snapshot)组件更新后调用,首先渲染不会被调用

Unmounting

lifecycle namedescription
componentWillUnmount组件卸载及销毁前调用,此方法中执行必要的清除操作(例如:清除定时器,取消网络请求,清除在 componentDidMount() 中创建的订阅)

组件内部的函数 this 指向问题

多半刚入门 react 的新手都会遇到这个问题,组件内函数this的指向问题

React组件中函数不能直接这么写,调用的时候使用setState会有问题,需要解决this指向问题

1
2
3
4
5
6
7
8
9
import React from 'react'

export default class Life extends React.Component {
handleBindClick() {
this.setState({
count: this.state.count + 1
})
}
}

解决1, 绑定this指向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from 'react'

export default class Life extends React.Component {
handleBindClick() {
this.setState({
count: this.state.count + 1
})
}

render() {
return (
<div>
<button onClick={this.handleBindClick.bind(this)}>+1</button>
</div>
)
}
}

解决2, 直接使用箭头函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from 'react'

export default class Life extends React.Component {
handleBindClick = () => {
this.setState({
count: this.state.count + 1
})
}

render() {
return (
<div>
<button onClick={this.handleBindClick}>+1</button>
</div>
)
}
}

State

状态管理

1
2
3
4
5
6
7
8
9
10
class Example extends React.Component {
// 可以在这里定义
state = {}

constructor(props) {
super(props)
// 也可以在这里定义
this.state = {}
}
}

修改状态

  • 不要直接修改 state ,使用 this.setState()
  • state 可能是异步的

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
26
27
28
29
30
// 子组件
class Children extends React.Component {
render() {
console.log(this.props); // 输出父组件传过来的参数
return (
<div>
<button>子组件 --- {this.props.flag}</button>
</div>
);
}
}
// 父组件
class Father extends React.Component {
state = {
flag: 1
};
handleClick = () => {
this.setState({
flag: this.state.flag + 1
});
};
render() {
return (
<div>
<button onClick={this.handleClick}>这是父组件</button>
<Children flag={this.state.flag} />
</div>
);
}
}

子传父

父组件

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
// 子组件
class Children extends React.Component {
state = {
flag: 0
}
handleClick = ()=>{
const flag = this.state.flag + 1;
this.setState({
flag
})
this.props.fn(flag);
}
render() {
return (
<button onClick={this.handleClick}>点击flag加1</button>
)
}
}

// 父组件
class Father extends React.Component {
state = {
flag: 0
}

fn = (cflag)=>{
this.setState({
flag: cflag
})
}
render() {
return (
<div>
<Children fn={this.fn} />
<p>这是父组件.显示子组件flag值: {this.state.flag}</p>
</div>
)
}
}

React.render(<Father />, document.getElementById('app'))

Tips: 这样也有明显的缺点,如果父子间传值层数或者值过多,这个方法显然过于麻烦,因此 React 官方给出了解决方法。 Context

调用方法

父调用子方法

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
class Children extends React.Component {
constructor(props) {
super();
props.onRef(this);
}
handleClick() {
console.log("调用了子组件的方法");
}
render() {
return (
<div>
<button onClick={this.handleClick}>子组件</button>
</div>
);
}
}

class Father extends React.Component {
handleClick = () => {
this.child.handleClick();
};

render() {
return (
<div>
<button onClick={this.handleClick}>这是父组件</button>
<Children onRef={ref => (this.child = ref)} />
</div>
);
}
}

ReactDOM.render(<Father />, 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
class Children extends React.Component {
handleClick = () => {
this.props.handleClick();
};
render() {
return (
<div>
<button onClick={this.handleClick}>子组件</button>
</div>
);
}
}

class Father extends React.Component {
handleClick = () => {
console.log("调用了父组件的方法");
};

render() {
return (
<div>
<button onClick={this.handleClick}>这是父组件</button>
<Children handleClick={this.handleClick} />
</div>
);
}
}

Context

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。

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
const MyContext = React.createContext(0);

class Children extends React.Component {
static contextType = MyContext;
render() {
return (
<p>子组件组件显示父组件flag值 {this.context}</p>
)
}
}

class Father extends React.Component {
state = {
flag: 0
}
handleClick = ()=>{
const flag = this.state.flag+1;
this.setState({
flag
})
}
render() {
return (
<div>
<MyContext.Provider value={this.state.flag}>
<Children />
</MyContext.Provider>
<p>父组件 flag值 {this.state.flag}</p>
<button onClick={this.handleClick}>点击flag加1</button>
</div>
)
}
}

ReactDOM.render(<Father />, document.getElementById('app'))

Fragments

单组件返回多个元素,解决不需要根节点包裹的情况。

段语法

1
2
3
4
5
6
7
8
9
10
class Columns extends React.Component {
render() {
return (
<>
<td>Hello</td>
<td>World</td>
</>
);
}
}

配置相对路径

配置后 **@**表示根目录下的 src路径,配置 jsconfigvscode 提示更加友好

webpack.config.js

1
2
3
4
alias: {
//...,
'@': path.resolve(__dirname, '../src')
}

jsconfig.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"allowSyntheticDefaultImports": true,
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"exclude": [
"node_modules"
]
}

jsconfig 配置详情

Koa

异步延时函数

setTimeout 会被加入异步队列,需使用promise来解决

为此还专门在思否提问

1
2
3
4
5
6
7
8
9
10
11
12
13
async function delayer(time = 2000) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
}

async (ctx, next) => {
await delayer(1000);
await next();
ctx.body = 'hello';
}

其他

对象的操作

对象的赋值

假设

1
2
3
4
5
obj1 = {
a: 1,
b: 2,
c: 3
}
1
2
3
4
obj2 = {
c: 33,
d: 44
}

将对象 obj2 属性的值赋值到 obj1中的属性 ,相同的属性覆盖,但不修改obj1的其他值。俗称,浅拷贝

1
2
3
4
Object.assign(obj1,obj2);

// 或者使用ES6语法,更简单
obj1 = { ...obj1, ...obj2};

判断对象中是否含有某个属性

仍以上面两个对象为例,则

1
2
3
4
5
// true
obj1.hasOwnProperty('a');

// false
obj2.hasOwnProperty('a');

过滤对象属性

props: 原对象

arr: 需要过滤掉的键

newProps: 过滤后剩下的键值组成的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const props = {
onRef: "1",
apiGetList: "2",
post: 3,
ok: "ok",
omg: "111"
};
const arr = ["onRef", "apiGetList"];
const newProps = {};

Object.keys(props)
.filter(item => !arr.includes(item))
.forEach(key => {
newProps[key] = props[key];
});

console.log(newProps);
// {post: 3, ok: "ok", omg: "111"}

操作函数拆分解释

Object.keys(obj)

返回 由 obj组成的数组

Array.prototype.filter

数组过滤的方法,传入的函数中的返回值为 true 则过滤,为 false 则留下。返回一个新数组,不会改变原数组。

Array.prototype.includes()

判断数组内是否包含指定的值。包含返回 true,反之 fales。

forEach

遍历对象和数组。