React入门004之React通信Redux代替eventHub.html

eventHub模式的理解

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
var money = {
amount : 100000
}

var eventMap = {};
var eventHub = {
trigger(eventName,data){
let fnList = eventMap[eventName];
if(!fnList) return
for(var i=0;i<fnList.length;i++){
fnList[i](data)
}
},
on(eventName,fn){
if(!eventMap[eventName]){
eventMap[eventName] = []
}
eventMap[eventName].push(fn)
}
}

var 管家 = {
init(){
eventHub.on('我想花钱',(data)=>{
money.amount -= data
// 此时要调用render才能实现 数据的同步
render();
})
}
}

管家.init()

特点

  1. 所有的数据放在顶层 上一篇我们的money放在了 app中 Redux 推荐把所有数据放归置在一个地方,然后把数据传递给最顶层组件(App)

此时有一个问题发生了,假如我有多个数据呢? 你就要一个一个属性的传下去

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
var money = {
amount : 100000
}
var user = {
id:321312,
nickName:'土豪'
}

// 此时需要在app里
class App extends React.Component{
constructor(){
super()
this.state = {
money:money,
user:user
}

}
render(){
return (
<div className="root">
<div>app</div>
<FirstFather money={this.state.money}
user={this.state.user}
/>
</div>
)
}
}

eventHub 深入

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.bootcss.com/react/16.7.0-alpha.2/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<style>
.root{
display: flex;
justify-content: center;
align-items: center;
padding:10px;
border:1px solid black;
}
.papa{
border:1px solid grey;
padding:10px;
}
.son{
border:1px solid grey;
padding:10px;
}
</style>
</head>
<body>
<div id="root"></div>
<script>
var user = {
id:3213123,
nickName:'土豪'
}
var money = {
amount : 100000
}

var store = {
money:money,
user:user
}

var eventMap = {};
var eventHub = {
trigger(eventName,data){
let fnList = eventMap[eventName];
if(!fnList) return
for(var i=0;i<fnList.length;i++){
fnList[i](data)
}
},
on(eventName,fn){
if(!eventMap[eventName]){
eventMap[eventName] = []
}
eventMap[eventName].push(fn)
}
}

var 管家 = {
init(){
eventHub.on('我想花钱',(data)=>{ //订阅 subscribe
store.money.amount -= data // reducer 对数据的变动
// 此时要调用render才能实现 数据的同步
render();
})
}
}

管家.init()

class App extends React.Component{
constructor(){
super()
this.state = {
store:store
}

}
render(){
return (
<div className="root">
<div>app</div>
<FirstFather money={this.state.store.money}/>
<SecondFather money={this.state.store.money}/>
</div>
)
}
}



class FirstFather extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="papa">
<div>大爸 {this.props.money.amount}</div>
<Son1 money={this.props.money}/>
<Son2 money={this.props.money}/>
</div>
)
}
}

class SecondFather extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="papa">
<div>二爸 {this.props.money.amount}</div>
<Son3 money={this.props.money}/>
<Son4 money={this.props.money}/>
</div>
)
}
}

class Son1 extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="son">
儿子1 {this.props.money.amount}
</div>
)
}
}


class Son2 extends React.Component{
constructor(){
super()
}
x(){
// active
eventHub.trigger('我想花钱' /* action type */,100) //100叫做 payload
}
render(){
return (
<div className="son">
儿子2 {this.props.money.amount}
<button onClick={()=>this.x()}>消费</button>
</div>
)
}
}


class Son3 extends React.Component{
constructor(){
super()
eventHub.on('我要花钱',(data)=>{
this.setState({
money:money
})
})
}
render(){
return (
<div className="son">
儿子3 {this.props.money.amount}
</div>
)
}
}


class Son4 extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="son">
儿子4 {this.props.money.amount}
</div>
)
}
}

function render(){
ReactDOM.render(
<App/>,
document.querySelector('#root')
)
}

render();
</script>
</body>
</html>
  • store 就是 集中管理数据的地方
  • action 就是事件
  • 我要花钱就是 action type
  • 100金额 就是 payload 载荷
  • reducer 对数据的变动
  • subscribe 订阅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var user = {
id:3213123,
nickName:'土豪'
}
var money = {
amount : 100000
}

var store = {
money:money,
user:user
}


eventHub.trigger('我想花钱' /* action type */,100) //100叫做 payload

eventHub.on('我想花钱',(data)=>{ //订阅 subscribe
store.money.amount -= data // reducer 对数据的变动
render();
})

用 Redux代替 eventHub

  • bootcdn 里 搜索 redux 并引入
1
<script src="https://cdn.bootcss.com/redux/4.0.0/redux.min.js"></script>
  • 由于我们是 JS引入 和 webpack或许有差异
  • 此时我们不知道咋用,咋办? crm
  1. 谷歌搜 Redux 科学上网的重要性
  2. https://cn.redux.js.org/ (翻墙可看)
  3. 找到 例子 示例 –> https://cn.redux.js.org/docs/introduction/Examples.html
  4. Counter 例子 https://github.com/reduxjs/redux/tree/master/examples/counter
  5. crm
  6. 抄它怎么调的redux https://github.com/reduxjs/redux/blob/master/examples/counter/src/index.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import React from 'react'
    import ReactDOM from 'react-dom'
    import { createStore } from 'redux'
    import Counter from './components/Counter'
    import counter from './reducers'

    const store = createStore(counter)
    const rootEl = document.getElementById('root')

    const render = () => ReactDOM.render(
    <Counter
    value={store.getState()}
    onIncrement={() => store.dispatch({ type: 'INCREMENT' })}
    onDecrement={() => store.dispatch({ type: 'DECREMENT' })}
    />,
    rootEl
    )

    render()
    store.subscribe(render)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//别忘记引入redux.js
// 通过抄别人的 我们造出了 自己的store
let createStore = Redux.createStore;
let reducers = (state = 0 ,action)=>{
state = state ||{
money:{ amoune:100000 }
}
switch(action.type){
case 'a':
return state + 1
case 'b':
return state - 1
default:
return state
}
}

const store = createStore(reducers);
console.log(store.getState())

此时之前的 eventHub 和 管家都不需要了

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.bootcss.com/react/16.7.0-alpha.2/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<script src="https://cdn.bootcss.com/redux/4.0.0/redux.min.js"></script>
<style>
.root{
display: flex;
justify-content: center;
align-items: center;
padding:10px;
border:1px solid black;
}
.papa{
border:1px solid grey;
padding:10px;
}
.son{
border:1px solid grey;
padding:10px;
}
</style>
</head>
<body>
<div id="root"></div>
<script>
let createStore = Redux.createStore;

//管家 ——一个函数 他会得到(之前的state,花钱的动作)
let reducers = (state = 0 ,action)=>{
//初始化
state = state ||{
money:{ amount:100000 }
}
switch(action.type){
case '我想花钱':
return {
money:{
amount:state.money.amount - action.payload
}
}
default:
return state
}
}

const store = createStore(reducers);
console.log(store.getState())

class App extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="root">
<div>app</div>
<FirstFather money={this.props.store.money}/>
<SecondFather money={this.props.store.money}/>
</div>
)
}
}



class FirstFather extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="papa">
<div>大爸 {this.props.money.amount}</div>
<Son1 money={this.props.money}/>
<Son2 money={this.props.money}/>
</div>
)
}
}

class SecondFather extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="papa">
<div>二爸 {this.props.money.amount}</div>
<Son3 money={this.props.money}/>
<Son4 money={this.props.money}/>
</div>
)
}
}

class Son1 extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="son">
儿子1 {this.props.money.amount}
</div>
)
}
}


class Son2 extends React.Component{
constructor(){
super()
}
x(){
// active
// eventHub.trigger('我想花钱' /* action type */,100) //100叫做 payload
store.dispatch({type:'我想花钱',payload:100})
}
render(){
return (
<div className="son">
儿子2 {this.props.money.amount}
<button onClick={()=>this.x()}>消费</button>
</div>
)
}
}


class Son3 extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="son">
儿子3 {this.props.money.amount}
</div>
)
}
}


class Son4 extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="son">
儿子4 {this.props.money.amount}
</div>
)
}
}

function render(){
ReactDOM.render(
<App store={store.getState()}/>,
document.querySelector('#root')
)
}

render()
// 订阅数据的改变同步更新
store.subscribe(render)
</script>
</body>
</html>

是不是觉得Redux很啰嗦

  • eventHub能看懂
  • 用 redux反而很恶心

Redux为啥这样做

  • 所有的框架都有一个使命——防呆(怕你的队友是个猪队友)
  • 所以Redux最大的功能就是防呆

eventHub的问题

  • 事件名可能任意的,分散在各处,而你用redux强制要求你集中的在 switch里

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    let reducers = (state = 0 ,action)=>{
    //初始化
    state = state ||{
    money:{ amount:100000 }
    }
    switch(action.type){
    case '我想花钱':
    return {
    money:{
    amount:state.money.amount - action.payload
    }
    }
    default:
    return state
    }
    }
  • redux的约束 你必须每个事件给我一个事件名和 payload 并且列在一起

  • 你只能用 props的方式 使用 store={store.getState()}

    1
    2
    3
    4
    5
    6
    7
    8
    //getState()只读的方式告诉你  不要修改
    //因为通过 props能防猪队友 

    // 猪队友
    // 通过引用修改数据
    this.props.money.amount -= 200
    // 即使用redux 依然也是可以通过引用改的
    这是 JS的 bug
  • redux可以让不懂英语的前端 走远一点

  • 比如 payload 实际就是个 data

代码仓库

React入门004之React通信eventHub

组件通信的回顾

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
59
60
61
62
63
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.bootcss.com/react/16.7.0-alpha.2/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
</head>
<body>
<div id="root"></div>
<script>

class App extends React.Component{
constructor(){
super()
this.state = {
msg :'你好!'
}
}
changeMsg(){
this.setState({
msg:'你也好!'
})
}
render(){
return (
<div>
<div>
<Box msg={this.state.msg}
fn={this.changeMsg.bind(this)}
/>
</div>
</div>
)
}
}



// 计时器
function Box(props){
return (
<div>
<h2>得到父组件的消息为{props.msg}</h2>
<div onClick={props.fn}>回复</div>
</div>
)
}


function render(){
ReactDOM.render(
<App/>,
document.querySelector('#root')
)
}

render();
</script>
</body>
</html>

一个家族的通信模型

  • 打开jsbin
  • 添加react支持的库文件
  • Javascript选择 React(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
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdn.bootcss.com/react/16.7.0-alpha.2/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<style>
.root{
display: flex;
justify-content: center;
align-items: center;
padding:10px;
border:1px solid black;
}
.papa{
border:1px solid grey;
padding:10px;
}
.son{
border:1px solid grey;
padding:10px;
}
</style>
</head>
<body>
<div id="root"></div>
<script>

class App extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="root">
<div>app</div>
<FirstFather/>
<SecondFather/>
</div>
)
}
}

function FirstFather(props){
return (
<div className="papa">
<div>大爸</div>
<Son1/>
<Son2/>
</div>
)
}

function SecondFather(props){
return (
<div className="papa">
<div>二爸</div>
<Son3/>
<Son4/>
</div>
)
}

function Son1(){
return (
<div className="son">
儿子1
</div>
)
}
function Son2(){
return (
<div className="son">
儿子2
</div>
)
}

function Son3(){
return (
<div className="son">
儿子3
</div>
)
}

function Son4(){
return (
<div className="son">
儿子4
</div>
)
}

function render(){
ReactDOM.render(
<App/>,
document.querySelector('#root')
)
}

render();
</script>
</body>
</html>

一个家族里爷爷留给后代的所有财产 100000元,后代们一起使用

  1. 二儿子用100 其他人怎么同步余额
  2. 把所有金额通知给所有人
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
var money = {
amount : 100000
}

class App extends React.Component{
constructor(){
super()

}
render(){
return (
<div className="root">
<div>app</div>
<FirstFather/>
<SecondFather/>
</div>
)
}
}

class FirstFather extends React.Component{
constructor(){
super()
this.state = {
money : money
}
}
render(){
return (
<div className="papa">
<div>大爸 {this.state.money.amount}</div>
<Son1/>
<Son2/>
</div>
)
}
}

class SecondFather extends React.Component{
constructor(){
super()
this.state = {
money : money
}
}
render(){
return (
<div className="papa">
<div>二爸 {this.state.money.amount}</div>
<Son3/>
<Son4/>
</div>
)
}
}

class Son1 extends React.Component{
constructor(){
super()
this.state = {
money : money
}
}
render(){
return (
<div className="son">
儿子1 {this.state.money.amount}
</div>
)
}
}

class Son2 extends React.Component{
constructor(){
super()
this.state = {
money : money
}
}
x(){
money.amount -= 100
this.setState({
money:money
})
}
render(){
return (
<div className="son">
儿子2 {this.state.money.amount}
<button onClick={()=>this.x()}>消费</button>
</div>
)
}
}

class Son3 extends React.Component{
constructor(){
super()
this.state = {
money : money
}
}
render(){
return (
<div className="son">
儿子3 {this.state.money.amount}
</div>
)
}
}

class Son4 extends React.Component{
constructor(){
super()
this.state = {
money : money
}
}
render(){
return (
<div className="son">
儿子4 {this.state.money.amount}
</div>
)
}
}

function render(){
ReactDOM.render(
<App/>,
document.querySelector('#root')
)
}

render();

现在点击消费按钮,只有二儿子的数据更新了

  • 虽然用的是同一个 money 对象
  • 但是这些组件你都没有去调用它的setState方法,所以它们不知道你消费的动作,所以一直保留旧的金额

一种假设 假如 大儿子 二儿子 都是败家子

  1. 二儿子去酒店把100000 花完了
  2. 大儿子不知道钱已经没了 还觉得有10万块,去夜店消费 ,消费后发现 没钱了——被暴打了一顿
  3. 这样就很不好

如果用上一篇的办法就会导致,用如下迂回的方式

  1. 二儿子消费了100后,通知它大爸
  2. 大爸通知大儿子 通知二爸
  3. 二爸通知三儿子 二爸通知四儿子
  • 这样一看就很垃圾

有木有一种方案让任意两个组件之间都可以通信

  • 雇一个管家 让他专门管理财务
  • 此时二儿子通知管家我要花钱 如果是小金额 管家直接给了
  • 如果是大金额 管家就通知 大爸 这钱能不能给二儿子 大爸说可以 就把钱给 二儿子
  • 这就是我们现实生活中所有人共用一个数据 矛盾的情况

雇一个人专门管这件事

发布订阅模式(EventHub)

代码实现

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
//订阅的事件
var eventMap = {
// 蒙牛:['张三','李四','王五'],
// 光明:['熊大','光头强']
};
var eventHub = {
trigger(eventName,data){
let fnList = eventMap[eventName];
if(!fnList) return
for(var i=0;i<fnList.length;i++){
fnList[i](data)
}
},
on(eventName,fn){
if(!eventMap[eventName]){
eventMap[eventName] = []
}
eventMap[eventName].push(fn)
}
}

// 订阅——事件
eventHub.on('我要用钱',function fn1(data){
console.log(data)
})

// 发布——事件
eventHub.trigger('我要用钱',100)

代码修改如下 二儿子花钱了 通知 三儿子

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
var money = {
amount : 100000
}

var eventMap = {};
var eventHub = {
trigger(eventName,data){
let fnList = eventMap[eventName];
if(!fnList) return
for(var i=0;i<fnList.length;i++){
fnList[i](data)
}
},
on(eventName,fn){
if(!eventMap[eventName]){
eventMap[eventName] = []
}
eventMap[eventName].push(fn)
}
}

class App extends React.Component{
constructor(){
super()

}
render(){
return (
<div className="root">
<div>app</div>
<FirstFather/>
<SecondFather/>
</div>
)
}
}

class FirstFather extends React.Component{
constructor(){
super()
this.state = {
money : money
}
}
render(){
return (
<div className="papa">
<div>大爸 {this.state.money.amount}</div>
<Son1/>
<Son2/>
</div>
)
}
}

class SecondFather extends React.Component{
constructor(){
super()
this.state = {
money : money
}
}
render(){
return (
<div className="papa">
<div>二爸 {this.state.money.amount}</div>
<Son3/>
<Son4/>
</div>
)
}
}

class Son1 extends React.Component{
constructor(){
super()
this.state = {
money : money
}
}
render(){
return (
<div className="son">
儿子1 {this.state.money.amount}
</div>
)
}
}

class Son2 extends React.Component{
constructor(){
super()
this.state = {
money : money
}
}
x(){
money.amount -= 100
eventHub.trigger('我要花钱',100)
this.setState({
money:money
})
}
render(){
return (
<div className="son">
儿子2 {this.state.money.amount}
<button onClick={()=>this.x()}>消费</button>
</div>
)
}
}

class Son3 extends React.Component{
constructor(){
super()
this.state = {
money : money
}
eventHub.on('我要花钱',(data)=>{
this.setState({
money:money
})
})
}
render(){
return (
<div className="son">
儿子3 {this.state.money.amount}
</div>
)
}
}

class Son4 extends React.Component{
constructor(){
super()
this.state = {
money : money
}
}
render(){
return (
<div className="son">
儿子4 {this.state.money.amount}
</div>
)
}
}

function render(){
ReactDOM.render(
<App/>,
document.querySelector('#root')
)
}

render();

新问题:如果 大爸 二爸 二儿子的兄弟 都想知道呢?

  • 总不能每个组件都 监听这个 事件吧,这样就太麻烦了

之所以会这样是因为:你们这些人自己随便花钱,二儿子花了钱谁也不告诉,直接把钱花了,然后在发信息

  • 先斩后奏(不行)
  • 该如何处理呢?

这个钱你们谁也不准改

  1. 花钱必须通知管家,(你不能把钱花了在告诉管家,管家帮你擦屁股)
  2. 所有组件不能修改money
  3. 不能修改money我咋花钱呢? 花钱可以——发消息
  4. 移除所有组件里 money的引用 也就是不要用 state
  5. 改用 props 放在 顶层的app里
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.bootcss.com/react/16.7.0-alpha.2/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<style>
.root{
display: flex;
justify-content: center;
align-items: center;
padding:10px;
border:1px solid black;
}
.papa{
border:1px solid grey;
padding:10px;
}
.son{
border:1px solid grey;
padding:10px;
}
</style>
</head>
<body>
<div id="root"></div>
<script>
var money = {
amount : 100000
}

var eventMap = {};
var eventHub = {
trigger(eventName,data){
let fnList = eventMap[eventName];
if(!fnList) return
for(var i=0;i<fnList.length;i++){
fnList[i](data)
}
},
on(eventName,fn){
if(!eventMap[eventName]){
eventMap[eventName] = []
}
eventMap[eventName].push(fn)
}
}

var 管家 = {
init(){
eventHub.on('我想花钱',(data)=>{
money.amount -= data
// 此时要调用render才能实现 数据的同步
render();
})
}
}

管家.init()

class App extends React.Component{
constructor(){
super()
this.state = {
money:money
}

}
render(){
return (
<div className="root">
<div>app</div>
<FirstFather money={this.state.money}/>
<SecondFather money={this.state.money}/>
</div>
)
}
}

class FirstFather extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="papa">
<div>大爸 {this.props.money.amount}</div>
<Son1 money={this.props.money}/>
<Son2 money={this.props.money}/>
</div>
)
}
}

class SecondFather extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="papa">
<div>二爸 {this.props.money.amount}</div>
<Son3 money={this.props.money}/>
<Son4 money={this.props.money}/>
</div>
)
}
}

class Son1 extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="son">
儿子1 {this.props.money.amount}
</div>
)
}
}

class Son2 extends React.Component{
constructor(){
super()
}
x(){
eventHub.trigger('我想花钱',100)
}
render(){
return (
<div className="son">
儿子2 {this.props.money.amount}
<button onClick={()=>this.x()}>消费</button>
</div>
)
}
}

class Son3 extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="son">
儿子3 {this.props.money.amount}
</div>
)
}
}

class Son4 extends React.Component{
constructor(){
super()
}
render(){
return (
<div className="son">
儿子4 {this.props.money.amount}
</div>
)
}
}

function render(){
ReactDOM.render(
<App/>,
document.querySelector('#root')
)
}

render();
</script>
</body>
</html>
  • 点击消费按钮时候——如何触发的更新呢?
1
2
3
chrome里查看dom解构,只有文字改变了元素还在

这就是 React的 NB之处——只更新需要更新的地方 DOM diff

这就是——单向数据流

  • 数据放在顶层
  • 通过props一层层的传递下去

以前我们只能这样:有上有下 (就不是单向数据流)

代码仓库

React入门003之React通信

生趣例子——龟兔赛跑

  • 请使用 jsbin
  • 拷贝对应代码到对应区域
  • JavaScript 选择 (React)Javascript
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.bootcss.com/react/16.7.0-alpha.2/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<style>
.header{
display:flex;
justify-content:center;
}

.track{
border:1px solid black;
}
</style>
</head>
<body>
<div id="root"></div>
<script>

function App(){
return (
<div>
<div class="header">
<Time1/>
<Judge/>
<Time2/>
</div>
<Track1/>
<Track2/>
</div>
)
}

// 计时器
function Time1(){
return (
<div>
<h2>🐰用时</h2>
<div>0</div>
</div>
)
}

// 计时器
function Time2(){
return (
<div>
<h2>🐢用时</h2>
<div>0</div>
</div>
)
}

// 裁判
function Judge(){
return (
<div>裁判</div>
)
}
// 跑道
function Track1(){
return (
<div>
<div>🐰</div>
<div class="track"></div>
</div>
)
}
// 跑道
function Track2(){
return (
<div>
<div>🐢</div>
<div class="track"></div>
</div>
)
}

function render(){
ReactDOM.render(
<App/>,
document.querySelector('#root')
)
}

render();
</script>
</body>
</html>

需求2:如何让兔子动?

  • css就可以做到
1
2
只要让 Track1里的  
<div style="transform: translateX(12%);">🐰</div>
  • 新问题:让兔子动就要改变 这个div的style===》如何改变div的style
  1. 你不能在内部改变函数组件的东西
  2. 你需要把 Track1变成一个 class组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Track1 extends React.Component{
constructor(){
this.state = {
progress:0
}
}
render(){
return(
<div>
<div style="transfrom:translateX(0%);">🐰</div>
<div class="track"></div>
</div>
)
}
}

卡壳了:React如何设置 style

google搜 react style

得到一个结果 Inline Styles | React点进去

给了个例子

1
2
3
4
5
6
7
8
var divStyle = {
color: 'white',
backgroundImage: 'url(' + imgUrl + ')',
WebkitTransition: 'all', // note the capital 'W' here
msTransition: 'all' // 'ms' is the only lowercase vendor prefix
};

ReactDOM.render(<div style={divStyle}>Hello World!</div>, mountNode);

继续改写我们的 Track

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Track1 extends React.Component{
constructor(){
super() // 必须调用 super 否则报错
this.state = {
style:{
transform:`translateX(20%)`
}
}
}
render(){
return(
<div>
<div style={this.state.style}>🐰</div>
<div class="track"></div>
</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
class Track1 extends React.Component{
constructor(){
super() // 必须调用 super 否则报错
let n = 0;
this.state = {
style:{
transform:`translateX(${n}%)`
}
};
setInterval(()=>{
n += 1
this.setState({
style:{transform:`translateX(${n}%)`}
})
},100)
}
render(){
return(
<div>
<div style={this.state.style}>🐰</div>
<div class="track"></div>
</div>
)
}
}

兔子速度快一点,乌龟跑的慢一点(改写两个track)

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
// 跑道
class Track1 extends React.Component{
constructor(){
super() // 必须调用 super 否则报错
let n = 0;
this.state = {
style:{
transform:`translateX(${n}%)`
}
};
setInterval(()=>{
n += 10
this.setState({
style:{transform:`translateX(${n}%)`}
})
},1000)
}
render(){
return(
<div>
<div class="player" style={this.state.style}>🐰</div>
<div class="track"></div>
</div>
)
}
}
// 跑道
class Track2 extends React.Component{
constructor(){
super() // 必须调用 super 否则报错
let n = 0;
this.state = {
style:{
transform:`translateX(${n}%)`
}
};
setInterval(()=>{
n += 1
this.setState({
style:{transform:`translateX(${n}%)`}
})
},1000)
}
render(){
return(
<div>
<div class="player" style={this.state.style}>🐢</div>
<div class="track"></div>
</div>
)
}
}

为什么一跳一跳的?

1
2
3
4
// 线性匀速
.player{
transition:all 1s linear;
}

跑到终点就停下

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
class Track1 extends React.Component{
constructor(){
super() // 必须调用 super 否则报错
let n = 0;
this.state = {
style:{
transform:`translateX(${n}%)`
}
};
let timerId = setInterval(()=>{
n += 10
this.setState({
style:{transform:`translateX(${n}%)`}
})
if(n>=100){
window.clearInterval(timerId)
}
},1000)
}
render(){
return(
<div>
<div class="player" style={this.state.style}>🐰</div>
<div class="track"></div>
</div>
)
}
}

重点环节 (通信)

跑到终点告诉裁判我停了

  • 父组件传递监听的事件 success1/success2
  • props传递属性 success
  • 事件完成后 调用 props.success()
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

function App(){
let success1 = ()=>{console.log('兔子跑完了')}
let success2 = ()=>{console.log('乌龟跑完了')}
return (
<div>
<div class="header">
<Time1/>
<Judge/>
<Time2/>
</div>
<Track1 success={success1}/>
<Track2 success={success2}/>
</div>
)
}

// 计时器
function Time1(){
return (
<div>
<h2>🐰用时</h2>
<div>0</div>
</div>
)
}

// 计时器
function Time2(){
return (
<div>
<h2>🐢用时</h2>
<div>0</div>
</div>
)
}

// 裁判
function Judge(){
return (
<div>裁判</div>
)
}
// 跑道
class Track1 extends React.Component{
constructor(props){
super() // 必须调用 super 否则报错
let n = 0;
this.state = {
style:{
transform:`translateX(${n}%)`
}
};
let timerId = setInterval(()=>{
n += 10
this.setState({
style:{transform:`translateX(${n}%)`}
})
if(n>=100){
window.clearInterval(timerId)
//通知 我跑完了
this.props.success()
}
},1000)
}
render(){
return(
<div>
<div class="player" style={this.state.style}>🐰</div>
<div class="track"></div>
</div>
)
}
}
// 跑道
class Track2 extends React.Component{
constructor(props){
super() // 必须调用 super 否则报错
let n = 0;
this.state = {
style:{
transform:`translateX(${n}%)`
}
};
let timerId = setInterval(()=>{
n += 5
this.setState({
style:{transform:`translateX(${n}%)`}
})
if(n>=100){
window.clearInterval(timerId)
//通知 我跑完了
this.props.success()
}
},1000)
}
render(){
return(
<div>
<div class="player" style={this.state.style}>🐢</div>
<div class="track"></div>
</div>
)
}
}

function render(){
ReactDOM.render(
<App/>,
document.querySelector('#root')
)
}

render();

一个小bug就是 兔子没跑到终点就打印了 跑完了 因为 css里的 线性动画( 移除 player样式即可)

跑到终点所用时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function App(){
let t0 = new Date();

let success1 = ()=>{
console.log('兔子跑完了')
console.log(new Date() - t0)
}
let success2 = ()=>{
console.log('乌龟跑完了')
console.log(new Date() - t0)
}

// ...
// ...
}

将结束时间更新到 Time1/Time2上

1
2
3
4
5
6
7
8
9
10
11
12
13
function App(){
return (
<div>
<div class="header">
<Time1 result={}/>
<Judge/>
<Time2/>
</div>
<Track1 success={success1}/>
<Track2 success={success2}/>
</div>
)
}
  • 如何单独把结果render 到Timer1呢?
  • 不好意思函数组件是做不到的

用 class组件改写 app

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
class App extends React.Component{
constructor(){
super()
this.state = {
result1:0,
result2:0
}
this.t0 = new Date()
}
success1(){
console.log('兔子跑完了')
console.log(new Date() - this.t0)
}
success2(){
console.log('乌龟跑完了')
console.log(new Date() - this.t0)
}
render(){
return (
<div>
<div class="header">
<Time1 result={this.state.result1}/>
<Judge/>
<Time2 result={this.state.result1}/>
</div>
<Track1 success={this.success1.bind(this)}/>
<Track2 success={this.success2.bind(this)}/>
</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
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

class App extends React.Component{
constructor(){
super()
this.state = {
result1:0,
result2:0
}
this.t0 = new Date()
}
success1(){
let t1 = new Date() - this.t0;
console.log('兔子跑完了',t1)
this.setState({
result1:t1
})
}
success2(){
let t2 = new Date() - this.t0;
console.log('乌龟跑完了',t2)
this.setState({
result2:t2
})
}
render(){
return (
<div>
<div class="header">
<Time1 result={this.state.result1}/>
<Judge/>
<Time2 result={this.state.result2}/>
</div>
<Track1 success={this.success1.bind(this)}/>
<Track2 success={this.success2.bind(this)}/>
</div>
)
}

}

// 计时器
function Time1(props){
return (
<div>
<h2>🐰用时</h2>
<div>{props.result}</div>
</div>
)
}

// 计时器
function Time2(props){
return (
<div>
<h2>🐢用时</h2>
<div>{props.result}</div>
</div>
)
}

// 裁判
function Judge(){
return (
<div>裁判</div>
)
}
// 跑道
class Track1 extends React.Component{
constructor(props){
super() // 必须调用 super 否则报错
let n = 0;
this.state = {
style:{
transform:`translateX(${n}%)`
}
};
let timerId = setInterval(()=>{
n += 25
this.setState({
style:{transform:`translateX(${n}%)`}
})
if(n>=100){
window.clearInterval(timerId)
//通知 我跑完了
this.props.success()
}
},1000)
}
render(){
return(
<div>
<div class="player" style={this.state.style}>🐰</div>
<div class="track"></div>
</div>
)
}
}
// 跑道
class Track2 extends React.Component{
constructor(props){
super() // 必须调用 super 否则报错
let n = 0;
this.state = {
style:{
transform:`translateX(${n}%)`
}
};
let timerId = setInterval(()=>{
n += 15
this.setState({
style:{transform:`translateX(${n}%)`}
})
if(n>=100){
window.clearInterval(timerId)
//通知 我跑完了
this.props.success()
}
},1000)
}
render(){
return(
<div>
<div class="player" style={this.state.style}>🐢</div>
<div class="track"></div>
</div>
)
}
}

function render(){
ReactDOM.render(
<App/>,
document.querySelector('#root')
)
}

render();

面试题

请问 React的父子组件如何通信

1
2
父组件通过props传递一个函数给子元素 
子元素在恰当的时候调用这个函数

需求变更(添加操场)

  • App里是 操场 Playground
  • 操场Playground里有跑道Track

咋办呢? 很简单 把success传递给 操场
然后在操场里通过 props接受 再次传递给 Track

代码如下

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.bootcss.com/react/16.7.0-alpha.2/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<style>
*{margin:0;padding:0;box-sizing: border-box;}
.header{
display:flex;
justify-content:center;
}

.track{
border:1px solid black;
}
.player{
transition:all 1s linear;
}
.playground{
border:1px solid black;
background: green;
}
</style>
</head>
<body>
<div id="root"></div>
<script>

class App extends React.Component{
constructor(){
super()
this.state = {
result1:0,
result2:0
}
this.t0 = new Date()
}
success1(){
let t1 = new Date() - this.t0;
console.log('兔子跑完了',t1)
this.setState({
result1:t1
})
}
success2(){
let t2 = new Date() - this.t0;
console.log('乌龟跑完了',t2)
this.setState({
result2:t2
})
}
render(){
return (
<div>
<div class="header">
<Time1 result={this.state.result1}/>
<Judge/>
<Time2 result={this.state.result2}/>
</div>
<Playground success1={this.success1.bind(this)}
success2={this.success2.bind(this)}
/>
</div>
)
}

}

function Playground(props){
let success1 = props.success1
let success2 = props.success2
return (
<div class="playground">
<Track1 success={success1}/>
<Track2 success={success2}/>
</div>
)
}

// 计时器
function Time1(props){
return (
<div>
<h2>🐰用时</h2>
<div>{props.result}</div>
</div>
)
}

// 计时器
function Time2(props){
return (
<div>
<h2>🐢用时</h2>
<div>{props.result}</div>
</div>
)
}

// 裁判
function Judge(){
return (
<div>裁判</div>
)
}
// 跑道
class Track1 extends React.Component{
constructor(props){
super() // 必须调用 super 否则报错
let n = 0;
this.state = {
style:{
transform:`translateX(${n}%)`
}
};
let timerId = setInterval(()=>{
n += 25
this.setState({
style:{transform:`translateX(${n}%)`}
})
if(n>=100){
window.clearInterval(timerId)
//通知 我跑完了
this.props.success()
}
},1000)
}
render(){
return(
<div>
<div class="player" style={this.state.style}>🐰</div>
<div class="track"></div>
</div>
)
}
}
// 跑道
class Track2 extends React.Component{
constructor(props){
super() // 必须调用 super 否则报错
let n = 0;
this.state = {
style:{
transform:`translateX(${n}%)`
}
};
let timerId = setInterval(()=>{
n += 15
this.setState({
style:{transform:`translateX(${n}%)`}
})
if(n>=100){
window.clearInterval(timerId)
//通知 我跑完了
this.props.success()
}
},1000)
}
render(){
return(
<div>
<div class="player" style={this.state.style}>🐢</div>
<div class="track"></div>
</div>
)
}
}

function render(){
ReactDOM.render(
<App/>,
document.querySelector('#root')
)
}

render();
</script>
</body>
</html>

面试肯定问爷孙通信是咋实现的

1
就像上面一样 Playground 从 App哪里把属性props传递下来(继承下来) 传递给 Track

如果是曾爷爷怎么办

当然是顺着这个家族在多传递一次了

思考?如果是 兄弟之间如何通信呢?

下一篇我们来解决

代码仓库

React入门002之React组件

函数组件

上一篇文章我们实现了 JSX语法写 html

1
2
3
4
5
6
7
8
9
10
function render(){
ReactDOM.render(
<div class="container">
<span class="red"> { number } </span>
<button onClick= { onClickButton } >+</button>
<button onClick= { onClickButton2 } >-</button>
</div>,
document.querySelector('#root')
)
}

假如我们写淘宝页面呢?

  • 1000行铁定不够的
  • 总不能在一个render里 写1000行以上这样的结构吧
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
function render(){
ReactDOM.render(
<div>
<div class="container">
<span class="red"> { number } </span>
<button onClick= { onClickButton } >+</button>
<button onClick= { onClickButton2 } >-</button>
</div>
<div class="container">
<span class="red"> { number } </span>
<button onClick= { onClickButton } >+</button>
<button onClick= { onClickButton2 } >-</button>
</div>
<div class="container">
<span class="red"> { number } </span>
<button onClick= { onClickButton } >+</button>
<button onClick= { onClickButton2 } >-</button>
</div>
.... 写xxx行
<div class="container">
<span class="red"> { number } </span>
<button onClick= { onClickButton } >+</button>
<button onClick= { onClickButton2 } >-</button>
</div>
</div>,
document.querySelector('#root')
)
}

于是React作者就在想,如何能避免所有代码写在一坨呢?

  • 最简单的方法就是用一个——函数 来把代码包裹起来

看看我们的代码(请使用jsbin)

1
2
3
4
5
6
7
8
# 使用这个
https://jsbin.com/

# 引入
<script src="https://cdn.bootcss.com/react/16.7.0-alpha.2/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>

# javascript选择 (JSX)React 否则报错

一个非常大的坑 return 后面不要直接换行—— 那样返回的是 undefined

1
2
3
4
5
6
7
8
9
// 返回的是 undefined
// 返回的是 undefined
// 返回的是 undefined
function App(){
return
<div>
<span>{number}</span>
</div>
}

我就想换行咋办,加()

1
2
3
4
5
6
7
function App(){
return (
<div>
<span>{number}</span>
</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
42
43
44
45
46
47
let number = 0 ;
let add = ()=>{
number += 1;
render();
}
let minus = ()=>{
number -= 1;
render();
}
render()

function App(){
return (
<div class="container">
<span class="red"> { number } </span>
<button onClick= { add } >+</button>
<button onClick= { minus } >-</button>
</div>
)
}

function render(){
ReactDOM.render(
React.createElement(App),
document.querySelector('#root')
)
}

// ------华丽的分割线---------------------------------------------------
// render里 React.createElement(App), 是不是很丑
// 由于我们用的是 JSX
// 你可以这样
function render(){
ReactDOM.render(
<App/>,
document.querySelector('#root')
)
}

// 或者

function render(){
ReactDOM.render(
<App></App>,
document.querySelector('#root')
)
}
  • JSX语法
1
2
3
4
<App></App>  <==等价于==>  React.createElement(App)

标签还可以再次简写为 ,因为它不是 html语法 它是 xml语法(标签里没有任何东西,可以自闭合)
<App/>

优化一下

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
let number = 0 ;
let add = ()=>{
number += 1;
render();
}
let minus = ()=>{
number -= 1;
render();
}
render()

function App(){
return (
<div>
<Box1/>
<Box2/>
</div>
)
}

function Box1(){
return (
<div class="container">
<span class="red"> { number } </span>
<button onClick= { add } >+</button>
<button onClick= { minus } >-</button>
</div>
)
}

function Box2(){
return (
<div class="container">
<span class="red"> { number } </span>
<button onClick= { add } >+</button>
<button onClick= { minus } >-</button>
</div>
)
}

function render(){
ReactDOM.render(
<App/>,
document.querySelector('#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
30
31
32
33
34
35
36
37
38
39
40
41
let number = 0 ;
let add = ()=>{
number += 1;
render();
}
let minus = ()=>{
number -= 1;
render();
}
render()

function App(){
return (
<div>
<Box1 name="加一"/>
<Box2 name="加二"/>
</div>
)
}

function Box1(obj){
return (
<div class="container">
我的name是{obj.name}
<span class="red"> { number } </span>
<button onClick= { add } >+</button>
<button onClick= { minus } >-</button>
</div>
)
}

function Box2(obj){
return (
<div class="container">
我的name是{obj.name}
<span class="red"> { number } </span>
<button onClick= { add } >+</button>
<button onClick= { minus } >-</button>
</div>
)
}
  • react的作者非常聪明,他把属性理解为对象的一个key/value

React的创举就是

  • 虚拟dom
  • 标签就是函数,函数就是对象,标签的属性就是函数的参数

上述代码的Box1和Box2有一个bug

1
2
3
4
5
点击 Box1的 "+" Box2的数字也增加了。

因为点击 Box1的 时候 触发了 add 导致 number++ 然后 render()

在render()的时候重新渲染了,而且 Box1 和 Box2使用的是同一个 number

此时你肯定想,既然能给组件传递参数,那我们传递一个number 这样 Box1和Box2就互不影响了

  • 答案是不行,因为React规定了一个事——不允许修改别人传递给你的参数
  • 我就要改,会给你一个警告。
  • 永远不要修改别人给你的属性,你修改了,不会有什么后果,但是后果自负。肯定会有bug,可能你现在不会觉得有bug,但是过了几个月一定会有bug

最朴素的想法

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
let number1 = 0 ;
let add1 = ()=>{
number1 += 1;
render();
}
let minus1 = ()=>{
number1 -= 1;
render();
}

let number2 = 0 ;
let add2 = ()=>{
number2 += 1;
render();
}
let minus2 = ()=>{
number2 -= 1;
render();
}
render()

function App(){
return (
<div>
<Box1 name="加一"/>
<Box2 name="加二"/>
</div>
)
}

function Box1(obj){
return (
<div class="container">
我的name是{obj.name}
<span class="red"> { number1 } </span>
<button onClick= { add1 } >+</button>
<button onClick= { minus1 } >-</button>
</div>
)
}

function Box2(obj){
return (
<div class="container">
我的name是{obj.name}
<span class="red"> { number2 } </span>
<button onClick= { add2 } >+</button>
<button onClick= { minus2 } >-</button>
</div>
)
}

function render(){
ReactDOM.render(
<App/>,
document.querySelector('#root')
)
}

虽然不相互影响了

  • 但是多出了很多无用变量
  • 如果是100个组件呢? 声明100个这样的岂不是疯了

卡壳了

  • 我不能修改别人给我的属性
  • 如果有100个组件就要100个这样的重复声明,一看页面要懵了

又想到一个好办法,将 number属性内置

  • 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
    59
    60
    61
    62
    63
    64
    render()

    function App(){
    return (
    <div>
    <Box1 name="加一"/>
    <Box2 name="加二"/>
    </div>
    )
    }

    function Box1(obj){
    let number1 = 0 ;
    console.log('把number变成 0',number1)
    let add1 = ()=>{
    number1 += 1;
    console.log('把number变成 1',number1)
    render();
    }
    let minus1 = ()=>{
    number1 -= 1;
    render();
    }
    return (
    <div class="container">
    我的name是{obj.name}
    <span class="red"> { number1 } </span>
    <button onClick= { add1 } >+</button>
    <button onClick= { minus1 } >-</button>
    </div>
    )
    }

    function Box2(obj){
    let number2 = 0 ;
    let add2 = ()=>{
    number2 += 1;
    render();
    }
    let minus2 = ()=>{
    number2 -= 1;
    render();
    }
    return (
    <div class="container">
    我的name是{obj.name}
    <span class="red"> { number2 } </span>
    <button onClick= { add2 } >+</button>
    <button onClick= { minus2 } >-</button>
    </div>
    )
    }

    function render(){
    ReactDOM.render(
    <App/>,
    document.querySelector('#root')
    )
    }

    /*
    思路是没错的
    错就错在了 render
    */

此时 点击 add 没反应了

  • 思路很好将变量内置
  • 错就错在 render了
1
2
3
4
5
6
7
8
9
点击 add 触发 number+=1 
然后 调用 render()
render 执行会调用 Box1
Box1里 let number = 0
所以你怎么点击 都是0

--------------------------------
虽然思路非常好,但是现实把你打败了,你做不到,因为函数的功能过于单一了
此时再次陷入窘境

我们为什么出错,因为这个render,一render就render整个页面

如果不render整个页面,只render自己呢?

  • 肯定不能用函数来搞组件了
  • 不能render整个app

react作者想到,什么比函数高级呢?——类

class组件

语法规定

  • 继承React.Component
  • 必须实现 render方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class App2 extends React.Component{
render(){
return (
<div>app2</div>
)
}
}

function render(){
ReactDOM.render(
<App2/>,
document.querySelector('#root')
)
}

render();

传递参数呢?

  • 注意! 我们管接受到的参数叫 props
  • 注意! 我们管接受到的参数叫 props
  • 注意! 我们管接受到的参数叫 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
// 之前传递参数
function App(props){
return (
<div>
app {props.name}
</div>
)
}

// 现在使用参数
// 这个 props 是由父类React.Component构造的 你不继承就没有
class App2 extends React.Component{
render(){ // 局部 render
return (
<div>app2 {this.props.name} {this.props.age}</div>
)
}
}

function render(){
ReactDOM.render(
<App2 name="hjx" age="18"/>,
document.querySelector('#root')
)
}

render();
// 注意在 html里 没有 数字 只有字符串

组件的局部变量呢?可以了这次

  • 文档规定局部变量必须在 constructor 里设置
  • this.state = {k:v,k2:v2}
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
59
// 规定组件的参数叫做 props 
class App2 extends React.Component{
constructor(props){
super(props);
this.state = {
number:0
}
}
add(){

/*
//报错
this.state.number +=1
// 而且这里的this 是 undefined
// 因为react 调用 onClick的时候是这样调用的
onClick = add.call(undefined, 参数)
React强制把this变成 undefined
*/

// 这样也是不能修改的
// this.state.number +=1
// 必须要用 setState
this.setState({
number:this.state.number + 1
})

}
render(){
return (
<div>
app2{this.props.name}
<div>
{this.state.number}
<button onClick={this.add.bind(this)}>+</button>
</div>
</div>
)
// 新手必犯错 onClick = {this.add()}
// add()是函数的返回值
// <button onClick={this.add()}>+</button>

// 绑定this有两种方式
/*
第一种 bind(this)
onClick={this.add.bind(this)}
第二种 箭头函数
onClick={()=>this.add}
*/
}
}

function render(){
ReactDOM.render(
<App2 name="hjx"/>,
document.querySelector('#root')
)
}

render();

使用class组件的限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1. 必须继承  React.Component
2. 想自定义属性,必须写 constructor() constructor必须接受一个 props参数
3. 在 constructor里 必须用super(props) 这是 js规定的
4. 必须在 constructor 初始化 state
constructor(props) {
super(props);
this.state = {
x:0,
y:1
}
}

你的事件可以放到 class内部

你必须要有一个render函数 同时 render必须 返回一个标签 而且这个标签只能有一个根元素

如果你在标签里想用porps必须 是 {this.props.xxx}
如果你想用state 必须是 {this.state.xxx}

如果你想用 onClick 事件 必须 自己绑定this

用class组件实现 box1 box2

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class Box1 extends React.Component{
constructor(props){
super(props);
this.state = {
number:0
}
}
add(){
this.setState({
number:this.state.number + 1
})
}
minus(){
this.setState({
number:this.state.number - 1
})
}
render(){
return (
<div>
Box1-{this.props.name}
<div>
{this.state.number}
<button onClick={this.add.bind(this)}>+</button>
<button onClick={this.minus.bind(this)}>-</button>
</div>
</div>
)
}
}

class Box2 extends React.Component{
constructor(props){
super(props);
this.state = {
number:0
}
}
add(){
this.setState({
number:this.state.number + 1
})
}
minus(){
this.setState({
number:this.state.number - 1
})
}
render(){
return (
<div>
Box2-{this.props.name}
<div>
{this.state.number}
<button onClick={this.add.bind(this)}>+</button>
<button onClick={this.minus.bind(this)}>-</button>
</div>
</div>
)
}
}

function App(props){
return (
<div>
<Box1 name="hjx1"/>
<Box2 name="hjx2"/>
</div>
)
}

function render(){
ReactDOM.render(
<App/>,
document.querySelector('#root')
)
}

render();

你的疑惑为什么更新数据要 setState 而不是直接改?

1
2
3
4
5
6
7
8
9
10
11
12
add(){
//为什么是 setState
this.setState({
number:this.state.number + 1
})

/*
为什么不是这样写,这样更方便理解啊!
this.state.number += 1
render()
*/
}

setState 优点

  • setState可以对更新进行优化,有的时候你自己render很容易 重复调用,这样页面就卡了,减少更新的损耗

    1
    2
    3
    4
    5
    6
    this.state.number +=1
    render()
    this.state.name = 'aa'
    render()
    this.state.age = 19
    render()
  • setState会将 大批量的更新合并为一次更新

Box2的 点击 加二 或者 减二

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
// 这个肯定没问题
add(){
this.setState({
number:this.state.number + 2
})
}

// 我能不能分两次 setState呢?
add(){
this.setState({
number:this.state.number + 1
})

this.setState({
number:this.state.number + 1
})
}

// 答案是不行的
假设初始的时候 number = 0
第一次 setState
this.setState({
number:this.state.number + 1
})
但是第二次 setState 时候 this.state.number到底是0还是1呢?

你会说 不是已经 setState 过了吗?

注意!由于 setState 会合并,它有可能并不会马上执行(异步更新)
注意!由于 setState 会合并,它有可能并不会马上执行(异步更新)
注意!由于 setState 会合并,它有可能并不会马上执行(异步更新)

如何解决 add函数内两次 setState呢? —— 函数回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
add(){
this.setState((state)=>{
return {number:state.number+1}
})

this.setState((state)=>{
return {number:state.number+1}
})
}

// 但是明显你不会这么智障的写代码

add(){
this.setState({
number:this.state.number + 2
})
}

总结

至此我们知道我们有个 应用App 它内部有两个盒子 Box1和 Box2,然后你就去看Box1的实现,又继续去看Box2的实现,此时代码非常的模块化

而且 Box1中 点击 add就会 number+=1 然后自己调用 render,而且render只会更新 Box1 里的div 而且只会更新你改的number

Box1的 render过程每次都会去跟上一次对比 发现 span里的number变了 ,那么就找到这个位置只改 内的 {this.state.number}

寻找两次render结果不同之处的过程 叫做—— DOM diff

  • 如果面试官问你 dom diff的细节,就要看给多少工资 你给10K以下,你就呸,我会这个还跟你来面试

代码仓库

React入门001之React诞生

简单的 add / reduce 需求

  • 实现 页面显示 一个数字
  • 点击add +1
  • 点击reduce -1

代码如下:

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
<!DOCTYPE html>
<html lang="en">
<body>
<div>
<span id="result">0</span>
<button id="add">+</button>
<button id="minus">-</button>
</div>
</body>
<script>
let result = document.querySelector('#result');
let add = document.querySelector('#add');
let minus = document.querySelector('#minus');

add.addEventListener('click',function(){
let number = parseInt(result.innerText,10);
number += 1;
result.innerText = number;
})

minus.addEventListener('click',function(){
let number = parseInt(result.innerText,10);
number -= 1;
result.innerText = number;
})
</script>
</html>

如上代码无论如何优化,本质没有改变,过程如下

  • 获取节点元素的值,js中修改元素的值
  • 获取节点元素的值,js中回填修改后的值

如果是你呢?

  • 要么把获取值(num = xx.innerText)干掉 (不去获取了,直接更新dom)
  • 要么把回填值(xx.innerText = xxx)干掉 (不现实,这样就js无法通知页面了)

React诞生

React的思路

  • 在一开始页面就什么都没有
  • 先声明一个变量叫 number=0
  • js里生成 一个 0(虚拟的span) 并把它同步到页面中 0(真实的span)
  • number+=1
  • 此时重新生成一个 1(虚拟的span) 并把它同步到页面中 1(真实的span)

此时永远不需要去页面获取 number了,同时页面里显示的数字永远是我number的最新值

实现我们刚刚的需求

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="root">
</div>
<button id="add">+</button>

<script src="https://cdn.bootcss.com/react/16.7.0-alpha.2/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<script>
let number = 0 ;

let span = React.createElement('span',{className:'red'},number);
ReactDOM.render(span,document.querySelector('#root'))

let add = document.querySelector('#add');

add.addEventListener('click',function(){
number += 1;

let span = React.createElement('span',{className:'red'},number);
ReactDOM.render(span,document.querySelector('#root'))
})
</script>
</body>
</html>

现在我们的span没有任何的获取元素,只有从虚拟的span到 页面真实的span

优化一下

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 lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="root">
</div>
<button id="add">+</button>

<script src="https://cdn.bootcss.com/react/16.7.0-alpha.2/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<script>
let number = 0 ;

render()

let add = document.querySelector('#add');

add.addEventListener('click',function(){
number += 1;
render()
})

function render(){
let span = React.createElement('span',{className:'red'},number);
ReactDOM.render(span,document.querySelector('#root'))
}
</script>
</body>
</html>

再也不用 querySelector后 取它爸爸取它儿子了

再次优化,按钮也在render里

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="root">
</div>

<script src="https://cdn.bootcss.com/react/16.7.0-alpha.2/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<script>
let number = 0 ;
let onClickButton = ()=>{
number += 1;
render();
}
let onClickButton2 = ()=>{
number -= 1;
render();
}
render()

function render(){
let span = React.createElement('span',{className:'red'},number);
let button = React.createElement('button',{onClick:onClickButton},'+');
let button2 = React.createElement('button',{onClick:onClickButton2},'-');
let div = React.createElement('div',{className:'container'},span,button,button2);
ReactDOM.render(div,document.querySelector('#root'))
}
</script>
</body>
</html>

千万不要以为 render的时候是重新造了一个 dom

  • react没那么傻
  • 它是局部刷新, 不会整个更新

    点击add后 虚拟的 1 会和 页面里0 比较后发现只变了 中间部分的文案 其他没变

JSX的发明

第一次优化

1
2
3
4
5
6
7
8
9
10
11
12
function render(){
let h = React.createElement;
let span = h('span',{className:'red'},number);
let button = h('button',{onClick:onClickButton},'+');
let button2 = h('button',{onClick:onClickButton2},'-');
let div = h('div',{className:'container'},
span,
button,
button2
);
ReactDOM.render(div,document.querySelector('#root'))
}

第二次优化,移除临时变量,直接用函数嵌套

1
2
3
4
5
6
7
8
9
function render(){
let h = React.createElement;
let div = h('div',{className:'container'},
h('span',{className:'red'},number),
h('button',{onClick:onClickButton},'+'),
h('button',{onClick:onClickButton2},'-')
);
ReactDOM.render(div,document.querySelector('#root'))
}

第三次优化(最重要的优化)

此时的解构已经蕴含了 html解构

1
2
3
4
5
6
7
8
9
10
11
h('div',{className:'container'},
h('span',{className:'red'},number),
h('button',{onClick:onClickButton},'+'),
h('button',{onClick:onClickButton2},'-')
);

<div class="container">
<span class="red">number</span>
<button onClick=onClickButton>+</button>
<button onClick=onClickButton2>-</button>
</div>

React开发者惊讶的发现自己代码和html没什么区别

  • 想能不能以这种形式写代码,然后用程序翻译成上面的代码呢?

JSX语法

用html的形式写js,终于这种形式被babel采纳了(招安)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 所有变量的地方====> { 变量 }
<div class="container">
<span class="red"> { number } </span>
<button onClick= { onClickButton } >+</button>
<button onClick= { onClickButton2 } >-</button>
</div>

// 以上代码被翻译为
React.createElement(
"div",
{ class:"container" },
React.createElement("span",{ class:"red"},number),
React.createElement("button",{ onClick: onClickButton},"+"),
React.createElement("button",{ onClick: onClickButton2},"-")
)

但是,我们这样写是运行不了的,有两种方法可以支持我们这样写

  • webpack
  • 用jsbin
1
2
3
4
5
6
7
8
9
10
11
// 注意 render2是无法运行的  必须webpack 编译 或者 用jsbin 添加jsx模式
function render2(){
ReactDOM.render(
<div class="container">
<span class="red"> { number } </span>
<button onClick= { onClickButton } >+</button>
<button onClick= { onClickButton2 } >-</button>
</div>,
document.querySelector('#root')
)
}

很多人疑惑为什么 onClickButton 不加()呢?

  • 加()代表这个 函数的返回值传递给 onClick
1
onClick= { onClickButton }

再次回到之前那张图

  • 我们以前不管用原生js还是 jquery都是 取对应元素节点 获取节点的值,然后对值进行一些逻辑处理,然后回填页面

React的思路

  • 页面你先给我空着
  • 所有的东西都通过对象表示,然后把对象放到页面里,你想修改就直接改值,然后更新下这个对象
  • 就是 改值 render / 改值 render / 改值 render

由于 dom API太难用了

  • React不择手段的做到:不去访问dom,只用你更新数据,构造出一个虚拟dom(就是不是真实的dom)

总结

  • 虚拟dom就是一个对象,没有什么特别之处(如果你不知道你就console.log打印啊)
  • JSX:就是用html的形式写 JS(js的扩展)
  • 虚拟dom:非真实的dom,表示dom节点的对象

代码仓库

Py008-11-03部署实例之发布网站

部署web发布环境

我们先前已经安装了 nginx/mysql/python/django

但是仍然不能算完成

  • 因为nginx只能处理 静态文件

处理python的数据要使用其他模块

  • wsgi
  • uwsgi

安装 UWSGI

  • UWSGI是服务器和服务器端应用程序通信协议,规定了如何把请求转发给应用程序和返回
  • uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http等协议。Nginx中HttpUwsgiModule的作用是与uWSGI服务器进⾏交换。
  • nginx 和 uWSGI交互就必须使⽤同一个协议,而上⾯说了uwsgi支持fastcgi,uwsgi,http协议,这些都是nginx⽀持的协议,只要大家沟通好使用哪个协议,就可以正常运行了。
1
pip3 install uwsgi

编辑uwsgi配置⽂文件!

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
# 新建目录
mkdir /etc/uwsgi

vi /etc/uwsgi/uwsgi.ini
# 内容如下
[uwsgi]
uid = root
gid = root
socket = 127.0.0.1:9090
master = true //启动主进程
vhost = true //多站模式
no-site = true //多站模式时不设置⼊口模块和⽂文件
workers = 2 //⼦进程数
reload-mercy = 10 //平滑的重启
vacuum = true //退出、重启时清理文件
max-requests = 1000 //开启10000个进程后, ⾃动respawn下
limit-as = 512 // 将进程的总内存量控制在512M
buffer-size = 30000
pidfile = /var/run/uwsgi9090.pid //pid⽂文件,⽤于下面的脚本启动、停止该进程
daemonize = /var/log/uwsgi9090.log


# 移除注释
[uwsgi]
uid = root
gid = root
socket = 127.0.0.1:9090
master = true
vhost = true
no-site = true
workers = 2
reload-mercy = 10
vacuum = true
max-requests = 1000
limit-as = 512
buffer-size = 30000
pidfile = /var/run/uwsgi9090.pid
daemonize = /var/log/uwsgi9090.log

# 启动uwsgi
uwsgi --ini /etc/uwsgi/uwsgi.int

# 验证是否启动
netstat -ntpl

# 如何关闭 uwsgi
# 获取进程号
cat /var/run/uwsgi9090.pid
# 进程号
10111
# 杀掉进程
kill -9 10111

管理 uwsgi服务

一个脚本

1
vi /etc/init.d/uwsgi

内容如下:

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
59
60
61
62
#!/bin/sh
DESC="uwsgi daemon"
NAME=uwsgi
DAEMON=/usr/local/bin/uwsgi
CONFIGFILE=/etc/uwsgi/$NAME.ini
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
FIFOFILE=/tmp/uwsgififo
set -e
[ -x "$DAEMON" ] || exit 0
do_start() {
if [ ! -f $PIDFILE ];then
$DAEMON $CONFIGFILE || echo -n "uwsgi running"
else
echo "The PID is exist..."
fi
}
do_stop() {
if [ -f $PIDFILE ];then
$DAEMON --stop $PIDFILE || echo -n "uwsgi not running"
rm -f $PIDFILE
echo "$DAEMON STOPED."
else
echo "The $PIDFILE doesn't found"
fi
}
do_reload() {
if [ -p $FIFOFILE ];then
echo w > $FIFOFILE
else
$DAEMON --touch-workers-reload $PIDFILE || echo -n "uwsgi
can't reload"
fi
}

do_status() {
ps aux|grep $DAEMON
}

case "$1" in
status)
echo -en "Status $NAME: \n"
do_status
;;
start)
echo -en "Starting $NAME: \n"
do_start
;;
stop)
echo -en "Stopping $NAME: \n"
do_stop
;;
reload|graceful)
echo -en "Reloading $NAME: \n"
do_reload
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|reload}" >&2
exit 3
;;
esac
exit 0

分配执行权限

1
2
3
4
5
6
7
8
9
10
11
12
13
chmod 755 /etc/init.d/uwsgi 

# 查看状态
/etc/init.d/uwsgi status
# 启动
/etc/init.d/uwsgi start
# 关闭
/etc/init.d/uwsgi stop

# 关闭的时候可能会报错
因为 PIDFILE=/var/run/$NAME.pid 里指定的文件
我们配置的时候改了 pidfile = /var/run/uwsgi9090.pid
PIDFILE=/var/run/${NAME}9090.pid

发布我们的网站

修改nginx

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
vi /usr/local/nginx/conf/nginx.conf

# 将 server 里的内容删掉 最后如下
server {
listen 80;
server_name localhost;
}

# 将server修改如下

server {
listen 80;
server_name localhost;
#access_log logs/abc.log main;
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:9090;
uwsgi_param UWSGI_SCRIPT myweb.wsgi;
uwsgi_param UWSGI_CHDIR /usr/local/nginx/html/myweb;
index index.html index.htm;
client_max_body_size 35m;
#uwsgi_cache_valid 1m;
#uwsgi_temp_file_write_size 64k;
#uwsgi_busy_buffers_size 64k;
#uwsgi_buffers 8 64k;
#uwsgi_buffer_size 64k;
#uwsgi_read_timeout 300;
#uwsgi_send_timeout 300;
#uwsgi_connect_timeout 300;
}
}

# 注释部分为优化方案
# include uwsgi_params; 包含 uwsgi的参数
# 设置端口 uwsgi_pass 127.0.0.1:9090;
# 设置目录 uwsgi_param UWSGI_SCRIPT myweb.wsgi;
# 设置目录(注意不能在root下 没权限) uwsgi_param UWSGI_CHDIR /usr/local/nginx/html/myweb;

移动我们之前创建的 django项目 到 nginx下

1
2
3
4
5
6
7
8
9
mv myweb  /usr/local/nginx/html/

# 先关闭nginx
/etc/init.d/uwsgi stop
# 启动 nginx
/etc/init.d/uwsgi start

# 启动nginx
/usr/local/nginx/sbin/nginx

此时 访问 我的ip结果返回 Internal Server Error 报错了

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
# 切换到项目文件目录里
cd /usr/local/nginx/html

# 查看日志
tailf /var/log/uwsgi9090.log

# 浏览器里刷新 地址看看日志显示什么
No module named 'django'

# 意思是 你没有指定python对应的环境
vi /etc/uwsgi/uwsgi.ini

# 查看 python包环境
ls /usr/local/lib/python3.7/site-packages
发现没有django

# 因为我们安装在虚拟环境了 就是 虚拟环境的 web
切换到web目录 我的是在 /usr/local/src/web
cd lib/python3.7

ls /usr/local/src/web/lib/python3.7/site-packages
里面有我们安装的django


#####################################
# 修改我们的uwsgi配置文件
# 添加一行
pythonpath = /usr/local/src/web/lib/python3.7/site-packages


######################################
# 关闭uwsgi
/etc/init.d/uwsgi stop
# 启动
/etc/init.d/uwsgi start

# 然后刷新网页 访问你的服务器ip
这个是假的IP
50.77.161.112

### 成功

Py008-11-02部署实例之安装python

安装python

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
# 远程登录服务器后 

cd /usr/local/src

# python3.7.1
wget https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tar.xz
# 解压
tar xf Python-3.7.1.tar.xz
# 进入解压文件目录
cd Python-3.7.1

# 安装依赖
yum -y install gcc-* openssl-* libffi-devel sqlite-devel

# configure
./configure --enable-optimizations --with-openssl=/usr/bin/openssl

# make
make -j4

make install

# 默认安装路径为
/usr/local/lib/python3.7

# 验证python
python3

安装pip3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pip3 install --upgrade pip
# 此时会警告一个 ssl问题
# pip is configured with locations that require TLS/SSL, however the
# ssl module in Python is not available.

# 解决方案
# 去python源码目录
vi Modules/Setup
# 把下边这段话的#去掉 211 SSL=/usr/local/ssl
212 _ssl _ssl.c \
213 -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \
214 -L$(SSL)/lib -lssl -lcrypto

# 再次make
make -j4
make install

# 再次升级pip
pip3 install --upgrade pip

pip安装软件

1
2
3
4
# 安装
pip3 install ipython
# 卸载
pip3 uninstall ipython

安装python虚拟环境

  • virtualenv 是⼀一个创建隔绝的Python环境的⼯工具。virtualenv创建⼀一个包含所有必要 的可执⾏行行⽂文件的⽂文件夹,⽤用来使⽤用Python⼯工程所需的包。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pip3 install virtualenv

# virtualenv 使用
# 当前目录 创建一个基于python3的虚拟环境 web
virtualenv -p python3 web
# 如何生效这个 环境呢
source web/bin/activate

# 此时如果你想在 web环境里安装 django
pip3 install django
# 创建一个django项目
django-admin.py startproject myweb
# 进入目录
cd myweb
# 启动项目
python3 manage.py runserver localhost:8080

# 默认你是访问不到的 要设置 allowhost
vi myweb/settings.py
# 修改如下
ALLOWED_HOSTS = ['*']
# 重新启动django
python3 manage.py runserver localhost:8080

退出 virtualenv 虚拟环境

1
deactivate

Py008-11-01部署实例

部署环境

  • Centos7.5
  • Nginx
  • python
  • Django
  • uwsgi
  • mysql

前置条件

  • Centos7.5
  • 防火墙:关闭
  • Selinux:关闭
  • 有一个服务器IP

部署流程

  1. 安装nginx
  2. 安装python
  3. 安装mysql
  4. 部署发布平台
  5. 测试

安装nginx

参考之前的文章安装

nginx安装

安装mysql

一个关系型数据库,后来卖给oracle 目前分为商业版和社区版

目前有两大版本 mysql5/mysql8

目前建议使用5.7最新版本就行。官方提供RPM和源码包两种方式

mysql安装步骤

  1. 安装依赖包
  2. 升级cmake工具
  3. 升级boost库文件
  4. 安装mysql
  5. 启动测试
  • 安装依赖
1
2
3
4
yum -y install ncurses-devel gcc-* bzip2-*

安装好之后看看你本地有木有 cmake
yum search cmake
  • cmake安装
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
需要先升级 cmake 
1.获取软件

https://cmake.org/download/
复制下载链接
https://github.com/Kitware/CMake/releases/download/v3.13.2/cmake-3.13.2.tar.gz

wget https://github.com/Kitware/CMake/releases/download/v3.13.2/cmake-3.13.2.tar.gz

# 我用的是3.6.0
wget https://github.com/Kitware/CMake/archive/v3.6.0-rc1.tar.gz

2.安装软件

tar xf cmake-3.6.0-rc1.tar

cd cmake-3.6.0-rc1

./configure

# 还差一些依赖
yum -y install ncurses-devel gcc-* bzip2-* bison

# 编译
make -j4
# 安装
make install

# 验证安装
cmake --version
  • boost安装

https://www.boost.org/

我下载的1.59版本

1
2
3
4
5
6
7
8
9
https://nchc.dl.sourceforge.net/project/boost/boost/1.59.0/boost_1_59_0.tar.bz2


cd ~
wget https://nchc.dl.sourceforge.net/project/boost/boost/1.59.0/boost_1_59_0.tar.bz2

tar xf boost_1_59_0.tar.bz2

mv boost_1_59_0 /usr/local/boost

  • mysql安装

https://www.oracle.com/index.html

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
访问 https://www.oracle.com/index.html 去页脚的 software downloads 
获得网址 https://www.oracle.com/downloads/
选择 database ->mysql ---> https://www.mysql.com/downloads/
访问 https://www.mysql.com/downloads/ 拉到最后 选择社区版本 Community (GPL) Downloads »

选第一个 MySQL Community Server (GPL)

此时有8.0 和 5.7 我们使用 5.7
选择 MySQL Community Server 5.7 »

Select Operating System:
- 如果你想选 rpm的 系统选择 红帽
- 选择源码包安装就选择 source code

我们使用 源码包安装 选择 source code

Select OS Version:我们选择通用版
Generic Linux (Architecture Independent)
然后下载列表里选择
Compressed TAR Archive
然后下载

cd ~
wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.24.tar.gz
tar xf mysql-5.7.24.tar.gz

cd mysql-5.7.24

使用cmake安装
cmake . -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DMYSQL_DATADIR=/usr/local/mysql/data/ -DMYSQL_UNIX_ADDR=/usr/local/mysql/mysql.sock -DDOWNLOAD_BOOST=0 -DWITH_INNODBBASE_STORAGE_ENGINE=1 -DENABLE_LOCAL_INFILE=1 -DEXTRA_CHARSETS=all -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DMYSQL_USER=mysql -DWITH_DEBUG=0 -DWITH_EMBEDED_SERVER=0 -DDOWNLOAD_BOOST=1 -DWITH_BOOST=/usr/local/boost

# -DMYSQL_USER=mysql 这个会有问题去掉

cmake . -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DMYSQL_DATADIR=/usr/local/mysql/data/ -DMYSQL_UNIX_ADDR=/usr/local/mysql/mysql.sock -DDOWNLOAD_BOOST=0 -DWITH_INNODBBASE_STORAGE_ENGINE=1 -DENABLE_LOCAL_INFILE=1 -DEXTRA_CHARSETS=all -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DWITH_DEBUG=0 -DWITH_EMBEDED_SERVER=0 -DDOWNLOAD_BOOST=1 -DWITH_BOOST=/usr/local/boost

# 注意如果你想要重新配置
就在这个目录里 mysql-5.7.24/删除 CMackCache.txt
rm -rf CMakeCache.txt

# 然后重新配置

cmake . -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DMYSQL_DATADIR=/usr/local/mysql/data/ -DMYSQL_UNIX_ADDR=/usr/local/mysql/mysql.sock -DDOWNLOAD_BOOST=0 -DWITH_INNODBBASE_STORAGE_ENGINE=1 -DENABLE_LOCAL_INFILE=1 -DEXTRA_CHARSETS=all -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DWITH_DEBUG=0 -DWITH_EMBEDED_SERVER=0 -DDOWNLOAD_BOOST=1 -DWITH_BOOST=/usr/local/boost

# 安装
make

# 如果你用的服务器是单核的很可能会报错 什么到45%的时候 报错
Building CXX object sql/CMakeFiles/sql.dir/geometry_rtree.cc.o
c++: 编译器内部错误:已杀死(程序 cc1plus)
Please submit a full bug report,

解决方案 https://blog.csdn.net/razertang/article/details/45694567/


make install

源码安装特容易失败

1
2
3
4
5
6
7
8
9
# yum 安装
https://blog.csdn.net/z13615480737/article/details/78906598


# 修改密码
mysql_secure_installation

# 登录数据库
mysql -uroot -p

Py008-10-08url重写

url重写

  • rewrite模块(ngx_http_rewrite_module)
  • Rewrite功能是Nginx服务器提供的⼀个重要功能。⼏乎是所有的web产品必备技能,⽤于实现URL重写。URL重写是⾮常有用的功能,比如它可以在 我们在改变网站结构后,不需要客户端修改原来的书签,也不需要其他⽹站修改对我们⽹站的友情链接,还可以在一定程度上提⾼网站的安全性,能够让我们的网站显得更专业。
  • Nginx服务器Rewrite功能的实现是依赖于PCRE(Perl Compatible Regular Expression。Perl兼容的正则表达式)的支持,所以在编译安装Nginx之前, 需要安装PCRE库。

www.360buy.com 就是京东的url重写

应用场景

  • 域名变更(京东)
  • 用户跳转(从某个链接跳到另一个链接)
  • 伪静态场景(便于CDN缓存动态页面数据)

重写原理

你找我,我不给你数据,我告诉你哪里有数据

张三找李四借钱,李四说王五有钱去找王五

URL rewrite实现

url模块指令

1
2
3
4
5
1) set 设置变量
2) if 负责判断
3) return 返回返回值或 url
4) break 终止后续的rewtite规则
5) rewrite 重定向URL

set指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# set指令 ⾃自定义变量量 
# Syntax:语法
set $variable value;
Default:

# Context:作用域 可以放在 server/location/if里
server, location, if


# 将http://www.baidu.com 重写为 http://www.baidu.com/xxx

location / {
set $name hjx;
rewrite ^(.*)$ http://www.baidu.com/$name;
}

if指令

  • nginx有一些内置变量 自行百度
  • 匹配规则
    1
    2
    3
    4
    5
    6
    7
    8
    # 模糊匹配
    ~ 匹配
    !~ 不匹配
    ~* 匹配而且不区分大小写

    # 精确匹配
    =
    !=
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if指令 负责判断 Syntax:
if (condition) { ... } Default:


Context: server, location


location / {
root html;
index index.html index.htm;
# 日志里 $http_user_agent是用来代表客户端访问的浏览器(它其实是nginx的内置变量)
# 这里针对 google访问 返回403
if ($http_user_agent ~* 'Chrome') {

return 403;
#return http://www.jd.com;
}
}

return指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
return 指令 定义返回数据 Syntax: return code [text]; return code URL;
return URL;
Default: —
Context: server, location, if


----------------------------- 如果chrome浏览器访问就返回 京东
location / {
root html;
index index.html index.htm;
if ($http_user_agent ~* 'Chrome') {
# return 403;
# return指令 既可以返回状态码 还可以返回url
return http://www.jd.com; }
}

break指令

1
2
3
4
5
6
7
8
9
10
# 本来是匹配chrome浏览器访问就返回 京东 但是有 break 就导致提前结束 后续的url重写就失效了
location / {
root html;
index index.html index.htm;
if ($http_user_agent ~* 'Chrome') {
break;
# return 403;
# return指令 既可以返回状态码 还可以返回url
return http://www.jd.com; }
}

rewrite 语法

1
2
3
4
5
6
7
8
rewrite  <regex>  <replacement> [flag];
关键字 正则 替代内容 flag标记

flag:
last # 本条规则匹配完成后,继续向下匹配新的location URL规则
break # 本条规则匹配完成即终止,不再匹配后面的任何规则
redirect # 返回302临时重定向,浏览器地址会显示跳转后URL地址
permanent #返回301永久重定向,浏览器器地址栏会显示跳转后的URL地址

案例

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
域名跳转
www.ayitula.com 重写为 www.jd.com

server {
listen 80;
# 修改本地host 里添加www.ayitula.com的解析
server_name www.ayitula.com;
location / {
# 临时重定向301
rewrite ^/$ http://www.jd.com permanent ;
# 永久重定向302
rewrite ^/$ http://www.jd.com redirect ;
# 碰到break不往后处理了 直接返回 www.jd.com 也是302
rewrite ^/$ http://www.jd.com break ;
}
}

注意:
重定向就是将⽹页自动转向重定向
# 对新域名推广
301永久性重定向:新网址完全继承旧网址,旧网址的排名等完全清零
301重定向是⽹页更改地址后对搜索引擎友好的最好⽅法,只要不是暂时搬移的情况,都建议使用301来做转址。

# 对老域名推广
302临时性重定向:对旧⽹址没有影响,但新网址不会有排名
搜索引擎会抓取新的内容而保留旧的⽹址

last指令

1
如果是chrome浏览器器 就将 http://192.168.10.42/$URI 重写为 http://192.168.10.42/chrome/$URI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
如果是chrome浏览器器 就将 http://192.168.10.42/$URI 重写为 http://192.168.10.42/chrome/$URI
location / {
if ($http_user_agent ~* 'chrome'){
rewrite ^(.*)$ /chrome/$1 last;
}
# 对重写为 http://192.168.10.42/chrome/$URI 匹配
location /chrome {
root html ;
index index.html;
}
}

根据⽤用户浏览器器重写访问⽬目录
#^ 以什什么开头 ^a
#$ 以什什么结尾 c$
#. 除了了回⻋车以外的任意⼀一个字符
#* 前⾯面的字符可以出现多次或者不不出现 #更更多内容看正则表达式 re

Py008-10-07限速

限速

  • 限速该特性可以限制某个用户在⼀个给定时间段内能够产生的HTTP请求数。请求可以简单到就是⼀一个对于主页的GET请求或者一个登陆表格的POST请求。
  • 限速也可以⽤用于安全⽬的上,⽐如暴力密码破解攻击。通过限制进来的请求速率,并且(结合⽇志)标记出⽬目标URLs来帮助防范DDoS攻击。⼀般地说,限流是用在 保护上游应⽤服务器不被在同一时刻的大量⽤户请求湮没。

经典案例 12306

并非12306的开发人员low,而是因为环境太特殊。

1
2
3
4
5
买票就是在浏览器里刷新。每刷新一次就会产生大量链接给服务器,服务器就要开很多的子进程来处理请求。
频繁的刷服务器肯定受不了。

服务器收到海量的刷新。
承受不住就挂了。

阮一峰的博客被攻击过——DDOS

1
2
比如:你在肯德基,一个肯德基店铺最多接受50来人。
但是这一地点突然来了 几千个人。堵得水泄不通。导致KFC没法工作

应用场景

  • DDOS防御
  • 下载场景保护IO (多个客户端打开迅雷下载一个文件)

nginx限速原理

漏桶原理

1
2
3
4
水(请求)从上方倒入⽔桶,从⽔桶下方流出(被处理);
来不及流出的水存在⽔桶中(缓冲),以固定速率流出;
⽔桶满后⽔溢出(丢弃)。
这个算法的核⼼是:缓存请求、匀速处理、多余的请求直接丢弃。

实现方式

1
2
3
 Nginx官⽅版本限制IP的连接和并发分别有两个模块:
limit_req_zone 用来限制单位时间内的请求数,即速率限制。
limit_req_conn ⽤来限制同一时间连接数,即并发限制
  • limit_req_zone 参数配置
  • Syntax: limit_req zone=name [burst=number] [nodelay];
  • Default: —
  • Context: http, server, location

限速案例一(请求限速)

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
#基于IP对下载速率做限制 限制每秒处理1次请求,对突发超过5个以后的请求放⼊缓存区 
http {
# 设置缓存区那个 桶
limit_req_zone $binary_remote_addr zone=hjx:10m rate=1r/s;
server {
# 对abc目录进行限速
location /abc/ {
# 使用缓存区 最大请求为5个,超过5个返回503
limit_req zone=hjx burst=5 nodelay;
}
}
}

limit_req_zone $binary_remote_addr zone=baism:10m rate=1r/s;
第⼀个参数:$binary_remote_addr 表示通过remote_addr这个标识来做限制,“binary_”的⽬的是缩写内存占⽤量,是限制同⼀一客户端ip地址。

第二个参数:zone=baism:10m表示生成⼀一个⼤⼩为10M,名字为one的内存区域,⽤来存储访问的频次信息。

第三个参数:rate=1r/s表示允许相同标识的客户端的访问频次,这里限制的是每秒1次,还可以有⽐如30r/m的。
limit_req zone=baism burst=5 nodelay;

第⼀个参数:zone=baism 设置使⽤哪个配置区域来做限制,与上面limit_req_zone 里的name对应。

第二个参数:burst=5,重点说明一下这个配置,burst爆发的意思,这个配置的意思是设置一个⼤小为5的缓冲区当有⼤大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内。

第三个参数:nodelay,如果设置,超过访问频次⽽且缓冲区也满了的时候就会直接返回503,如果没有设置,则所有请求会等待排队。

限速案例二(下载限速)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#基于IP做连接限制 限制同⼀IP并发为1 下载速度为100K 
limit_conn_zone $binary_remote_addr zone=addr:10m;

server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location /abc {
limit_conn addr 1;
limit_rate 100k;
}
}

准备一个大文件

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
# 造一个300m的文件
dd if=/dev/zero of=/usr/local/nginx/html/abc/bigfile bs=1M count=300

# 切换到临时目录下载这个文件
wget http://45.77.47.0/abc/bigfile
# 下载就很快,而且还能多台机器同时下载

# 修改配置 nginx.conf

limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location /abc {
# 限制同时连接的数量
limit_conn addr 1;
# 限制下载速率
limit_rate 100k;
# 下载100M的时候限速(这个参数不太重要,因为不是按比例只能按大小)
limit_rate_after 100m;
}
}