目录
todolist项目:... 1
阶段5,状态的控制和改变:... 1
阶段6,getter、setter:... 5
阶段7,过滤数据方法调整:... 9
阶段8,@computed的使用:... 13
阶段9,完成状态,Symbol类型:... 14
阶段10,axios,整合前后端:... 17
Redux和Mobx,社区提供的状态管理库;
Redux,代码优秀,使用严格的函数式编程思想,学习曲线陡峭,小项目使用的优势不明显;
Mobx,优秀稳定的库,简单方便(内部实现复杂),适合中小项目使用,使用面向对象的方式,易学习和接受,现使用非常广泛;
https://mobx.js.org/
http://cn.mobx.js.org/
mobx实现了观察者模式(订阅+广播模式),观察者观察某个目标,obserable目标对象发生了变化,会通知自己内部注册了的observer观察者;
./src/index.js
import React from 'react';
import ReactDom from 'react-dom';
import TodoApp from './component/TodoApp';
import TodoService from './service/service';
const service = new TodoService(); //service实例通过props属性传递到组件中
ReactDom.render(<TodoApp service={service}/>, document.getElementById('root'));
./src/service/service.js
解决复选框改变,列表不刷新问题:
1、手动修改todos,如下例,相当于change过;
2、用一常量,如@observable changed=false(或changed=时间戳+随机数),但这个变量要在render()中用到,哪怕写{this.props.service.changed}都行反正要用到,否则不会刷新;
import store from 'store';
import {observable} from 'mobx';
export default class TodoService {
constructor() {
// super();
this.load();
}
load() {
store.each((value,key) => {
if (key.startsWith(TodoService.NAMESPACE))
// this.todos.push(value);
this.todos.set(key, value);
});
console.log(this.todos);
}
static NAMESPACE = 'todo::';
// todos = [];
@observable //观察目标
todos = new Map();
create(title) {
// console.log('service');
const todo = {
key: TodoService.NAMESPACE + (new Date()).valueOf(),
title: title,
completed: false
};
// this.todos.push(todo);
this.todos.set(todo.key, todo);
store.set(todo.key, todo);
return todo;
}
setTodoState(key, checked) {
let todo = this.todos.get(key);
if (todo) {
todo.completed = checked;
store.set(key, todo);
}
let temp = this.todos; //改变观察目标,解决Checkbox复选框改变列表不刷新问题
this.todos = {}; //同this.todos = () => {}
this.todos = temp;
}
}
./src/component/TodoApp.js
this.props.service.create(event.target.value),index.js已定义,此处用props属性访问;
之前的this.state=({todos: this.service.todos})注释,当前使用mobx控制状态;
import React from 'react';
import Create from './Create';
// import TodoService from '../service/service';
import Todo from './Todo';
import Filter from './Filter';
import {observer} from 'mobx-react'; //注意此处是mobx-react
@observer
export default class TodoApp extends React.Component {
constructor(props) {
super(props);
// this.service = new TodoService();
// this.state = ({todos: this.service.todos, filter: 'uncompleted'});
this.state = ({filter: 'uncompleted'});
}
// handleCreate(...args) {
// console.log('Root handlerCreate');
// console.log(this);
// console.log('args is ', args);
// }
handleCreate(event) {
// console.log('Root handleCreate');
// console.log(this);
// console.log('event is ', event, event.target, event.target.value);
this.props.service.create(event.target.value);
// this.setState({todos: this.service.todos});
}
handleCheckedChange(key, checked) { //handleCheckedChange(event),event.target.checked=false|true
console.log('handleCheckedChange', key, checked);
this.props.service.setTodoState(key, checked);
// this.setState({todos: this.service.todos});
}
handleFilterChange(value) {
// console.log('~~~~~~~', args);
// console.log(this);
console.log(value);
this.setState({filter: value});;
}
render() {
return (
<div>
<Create onCreate={this.handleCreate.bind(this)}/>
<Filter onChange={this.handleFilterChange.bind(this)}/>
<br />
{/* {this.service.todos.map(
item => <Todo todo={item} key={item.key} onChange={this.handleCheckedChange.bind(this)}/>)
} */}
{/* {
[...this.service.todos.values()].map(
item => <Todo key={item.key} todo={item} onChange={this.handleCheckedChange.bind(this)} />
)
} */}
{
[...this.props.service.todos.values()].filter(
item => {
let fs = this.state.filter;
if(fs === 'all') {
return true;
} else if(fs === 'completed') {
// if(item.completed === true)
// return true;
// else
// return false;
return item.completed === true;
} else if(fs === 'uncompleted') {
// if(item.completed === false)
// return true;
// else
// return false;
return item.completed === false;
}
}
).map(
item => <Todo key={item.key} todo={item} onChange={this.handleCheckedChange.bind(this)} />
)
}
</div>
);
}
}
./src/component/{Create.js,Todo.js,Filter.js}不动;
类似py的property装饰器;
./src/service/service.js
import store from 'store';
import {observable} from 'mobx';
export default class TodoService {
constructor() {
// super();
this.load();
}
load() {
store.each((value,key) => {
if (key.startsWith(TodoService.NAMESPACE))
// this.todos.push(value);
this._todos.set(key, value);
});
// console.log(this.todos);
}
static NAMESPACE = 'todo::';
// todos = [];
@observable
_todos = new Map();
get todos() { //getter
return this._todos;
}
create(title) {
// console.log('service');
const todo = {
key: TodoService.NAMESPACE + (new Date()).valueOf(),
title: title,
completed: false
};
// this.todos.push(todo);
this._todos.set(todo.key, todo);
store.set(todo.key, todo);
let temp = this._todos; //类似setter;这三句放到此处,解决新创建的待办事宜的刷新问题
this._todos = {};
this._todos = temp;
return todo;
}
setTodoState(key, checked) {
let todo = this._todos.get(key);
if (todo) {
todo.completed = checked;
store.set(key, todo);
}
let temp = this._todos; //setter
this._todos = {}; //this.todos = () => {}
this._todos = temp;
}
}
./src/component/TodoApp.js
import React from 'react';
import Create from './Create';
// import TodoService from '../service/service';
import Todo from './Todo';
import Filter from './Filter';
import {observer} from 'mobx-react';
@observer
export default class TodoApp extends React.Component {
constructor(props) {
super(props);
// this.service = new TodoService();
// this.state = ({todos: this.service.todos, filter: 'uncompleted'});
this.state = ({filter: 'uncompleted'});
}
// handleCreate(...args) {
// console.log('Root handlerCreate');
// console.log(this);
// console.log('args is ', args);
// }
handleCreate(event) {
// console.log('Root handleCreate');
// console.log(this);
// console.log('event is ', event, event.target, event.target.value);
this.props.service.create(event.target.value);
// this.setState({todos: this.service.todos});
}
handleCheckedChange(key, checked) { //handleCheckedChange(event),event.target.checked=false|true
// console.log('handleCheckedChange', key, checked);
this.props.service.setTodoState(key, checked);
// this.setState({todos: this.service.todos});
}
handleFilterChange(value) {
// console.log('~~~~~~~', args);
// console.log(this);
// console.log(value);
this.setState({filter: value});;
}
render() {
return (
<div>
<Create onCreate={this.handleCreate.bind(this)}/>
<Filter onChange={this.handleFilterChange.bind(this)}/>
<br />
{/* {this.service.todos.map(
item => <Todo todo={item} key={item.key} onChange={this.handleCheckedChange.bind(this)}/>)
} */}
{/* {
[...this.service.todos.values()].map(
item => <Todo key={item.key} todo={item} onChange={this.handleCheckedChange.bind(this)} />
)
} */}
{
[...this.props.service.todos.values()].filter(
item => {
let fs = this.state.filter;
if(fs === 'all') {
return true;
} else if(fs === 'completed') {
// if(item.completed === true)
// return true;
// else
// return false;
return item.completed === true;
} else if(fs === 'uncompleted') {
// if(item.completed === false)
// return true;
// else
// return false;
return item.completed === false;
}
}
)
.map(
item => <Todo key={item.key} todo={item} onChange={this.handleCheckedChange.bind(this)} />
)
}
</div>
);
}
}
将过滤数据的filter,移到service.js中;
./src/service/service.js
import store from 'store';
import {observable} from 'mobx';
export default class TodoService {
constructor() {
// super();
this.load();
}
load() {
store.each((value,key) => {
if (key.startsWith(TodoService.NAMESPACE))
// this.todos.push(value);
this._todos.set(key, value);
});
// console.log(this.todos);
}
static NAMESPACE = 'todo::';
// todos = [];
@observable
_todos = new Map();
@observable //添加观察目标对象
filter = 'uncompleted';
get todos() { //getter,_todos变量
// return this._todos;
return [...this._todos.values()].filter(
item => {
let fs = this.filter;
if(fs === 'all') {
return true;
} else if(fs === 'completed') {
// if(item.completed === true)
// return true;
// else
// return false;
return item.completed === true;
} else if(fs === 'uncompleted') {
// if(item.completed === false)
// return true;
// else
// return false;
return item.completed === false;
}
}
);
}
create(title) {
// console.log('service');
const todo = {
key: TodoService.NAMESPACE + (new Date()).valueOf(),
title: title,
completed: false
};
// this.todos.push(todo);
this._todos.set(todo.key, todo);
store.set(todo.key, todo);
let temp = this._todos;
this._todos = {};
this._todos = temp;
return todo;
}
setTodoState(key, checked) {
let todo = this._todos.get(key);
if (todo) {
todo.completed = checked;
store.set(key, todo);
}
let temp = this._todos;
this._todos = {}; //this.todos = () => {}
this._todos = temp;
}
setTodoFilter(value) {
this.filter = value;
}
}
./src/component/TodoApp.js
import React from 'react';
import Create from './Create';
// import TodoService from '../service/service';
import Todo from './Todo';
import Filter from './Filter';
import {observer} from 'mobx-react';
@observer
export default class TodoApp extends React.Component {
constructor(props) {
super(props);
// this.service = new TodoService();
// this.state = ({todos: this.service.todos, filter: 'uncompleted'});
// this.state = ({filter: 'uncompleted'}); //filter使用mobx管理
}
// handleCreate(...args) {
// console.log('Root handlerCreate');
// console.log(this);
// console.log('args is ', args);
// }
handleCreate(event) {
// console.log('Root handleCreate');
// console.log(this);
// console.log('event is ', event, event.target, event.target.value);
this.props.service.create(event.target.value);
// this.setState({todos: this.service.todos});
}
handleCheckedChange(key, checked) { //handleCheckedChange(event),event.target.checked=false|true
// console.log('handleCheckedChange', key, checked);
this.props.service.setTodoState(key, checked);
// this.setState({todos: this.service.todos});
}
handleFilterChange(value) {
// console.log('~~~~~~~', args);
// console.log(this);
// console.log(value);
// this.setState({filter: value});;
this.props.service.setTodoFilter(value);
}
render() {
return (
<div>
<Create onCreate={this.handleCreate.bind(this)}/>
<Filter onChange={this.handleFilterChange.bind(this)}/>
<br />
{/* {this.service.todos.map(
item => <Todo todo={item} key={item.key} onChange={this.handleCheckedChange.bind(this)}/>)
} */}
{/* {
[...this.service.todos.values()].map(
item => <Todo key={item.key} todo={item} onChange={this.handleCheckedChange.bind(this)} />
)
} */}
{
this.props.service.todos.map(
item => <Todo key={item.key} todo={item} onChange={this.handleCheckedChange.bind(this)} />
)
}
</div>
);
}
}
mobx提供了@computed装饰器,可用在任意类的属性的getter上,它所依赖的值发生了变化就重新计算,否则直接返回上次计算的结果;
使用@computed装饰get todos();
./src/service/service.js
import {observable, computed} from 'mobx';
export default class TodoService {
……
@computed //程序员感知不到变化,观察对象_todos和filter任意一个变化都会重新计算
get todos() {
// return this._todos;
return [...this._todos.values()].filter(
item => {
let fs = this.filter;
if(fs === 'all') {
return true;
} else if(fs === 'completed') {
// if(item.completed === true)
// return true;
// else
// return false;
return item.completed === true;
} else if(fs === 'uncompleted') {
// if(item.completed === false)
// return true;
// else
// return false;
return item.completed === false;
}
}
);
}
……
}
Symbol类型,是JS中的基本类型;
是ES6新增的主数据类型,是一种特殊的、不可变的数据类型;
Symbol([description]),description是可选的字符串;
不可使用new关键字,生成的不是对象,直接用函数调用方式使用;
Symbol每次返回一个独一无二的值,即便2次的描述一样,描述只是为了使人阅读方便,便于区分不同的Symbol值;
例:
let sym0 = Symbol();
let sym1 = Symbol();
let sym2 = Symbol('symbol2');
let sym3 = Symbol('symbol2');
console.log(sym0 === sym1);
console.log(sym1 === sym2);
输出:
false
false
./src/service/service.js
import store from 'store';
import {observable, computed} from 'mobx';
const ALL = Symbol('all');
const COMPLETED = Symbol('completed');
const UNCOMPLETED = Symbol('uncompleted');
export default class TodoService {
constructor() {
// super();
this.load();
}
load() {
store.each((value,key) => {
if (key.startsWith(TodoService.NAMESPACE))
// this.todos.push(value);
this._todos.set(key, value);
});
// console.log(this.todos);
}
static NAMESPACE = 'todo::';
static TODOSTATES = {
// all: 'all',
all: ALL,
// completed: 'completed',
completed: COMPLETED,
// uncompleted: 'uncompleted'
uncompleted: UNCOMPLETED
}
// todos = [];
@observable
_todos = new Map();
@observable
// filter = 'uncompleted';
filter = TodoService.TODOSTATES.uncompleted;
@computed
get todos() {
// return this._todos;
return [...this._todos.values()].filter(
item => {
let fs = this.filter;
// if(fs === 'all') {
if(fs === TodoService.TODOSTATES.all) { //只能用类.属性这种方式,不可以fs === Symbol('all'),这样相当于重新调用Symbol()是个新值;===常用,严格相等;==要做隐式转换,不用
return true;
// } else if(fs === 'completed') {
} else if(fs === TodoService.TODOSTATES.completed) {
// if(item.completed === true)
// return true;
// else
// return false;
return item.completed === true;
// } else if(fs === 'uncompleted') {
} else if(fs === TodoService.TODOSTATES.uncompleted) {
// if(item.completed === false)
// return true;
// else
// return false;
return item.completed === false;
}
}
);
}
create(title) {
// console.log('service');
const todo = {
key: TodoService.NAMESPACE + (new Date()).valueOf(),
title: title,
completed: false
};
// this.todos.push(todo);
this._todos.set(todo.key, todo);
store.set(todo.key, todo);
let temp = this._todos;
this._todos = {};
this._todos = temp;
return todo;
}
setTodoState(key, checked) {
let todo = this._todos.get(key);
if (todo) {
todo.completed = checked;
store.set(key, todo);
}
let temp = this._todos;
this._todos = {}; //this.todos = () => {}
this._todos = temp;
}
setTodoFilter(value) {
// this.filter = value;
this.filter = TodoService.TODOSTATES[value];
}
}
注:
./src/component/Filter.js
<Select style={{ width: 120 }} defaultValue="uncompleted" onChange={value => props.onChange(value)}>
antd在defaultValue={TodoService.TODOSTATE.uncompleted},不支持传递类.属性这种,只能用字符串;
axios是一个异步HTTP库,可用在浏览器或nodejs中;
例:
axios.get('/user?ID=12345')
.then(function (response) { //返回200 OK的处理,response是返回的数据(区别于服务器的响应),具体看MIME类型,可以是picture、下载内容等
console.log(response);
})
.catch(function (error) { //异常的处理
console.log(error);
});
注:
前端要操作DB,通过ajax或jquery(早期用)-->web服务器;
url + method --> restful;
post #add
put #modify
store.set(todo.key, todo) #当前是存储在浏览器本地的Local Storage,实际是要存储在数据库上,这块需改造
例:
webpack.config.dev.js #开发的win主机上更改
devServer: {
compress: true,
port: 3000,
publicPath: '/assets/',
hot: true,
inline: true,
historyApiFallback: true,
stats: {
chunks: false
},
proxy: {
'/api': {
target: 'http://192.168.23.134:80', //nginx主机配置
changeOrigin: true
}
}
}
$ npm run build #在项目根下打包
]# pwd #nginx主机上操作
/ane/packages
]# tar xf tengine-1.2.3.tar.gz
]# cd tengine-1.2.3/
]# yum -y install gcc openssl-devel pcre-devel
]# ./configure
]# make && make install
]# cd /usr/local/nginx/
]# vim conf/nginx.conf
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location /api/ {
proxy_pass http://192.168.7.144:8080; #win主机上运行的py开发的后台app
}
location / {
root html;
index index.html index.htm;
}
]# tree ./ #将打好包的index.html和app-56350ea8.js放到nignx上,app-*.js要放到assets/下
./
├── 50x.html
├── assets
│ └── app-56350ea8.js
└── index.html
]# sbin/nginx #sbin/nginx -s reload,动态加载配置
]# netstat -tnulp | grep nginx
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 2280/nginx: master
http://192.168.23.134/
注:
nginx配置中先注释location /api/ {……}这段,在create待办事宜时报404;
nginx配置中有location /api/ {……}这段,在create待办事宜时报502,因为后端还没开启py开发的后台app;
win主机的pycharm中运行:
from aiohttp import web, log
import json
import logging
async def indexhandle(request: web.Request):
return web.Response(text='welcome to pyserver', status=200)
async def handle(request: web.Request):
print(request.match_info)
print(request.query_string)
return web.Response(text=request.match_info.get('id', '0000'), status=200)
async def todopost(request: web.Request): #协程函数
print(request.method)
print(request.match_info)
print(request.query_string)
print(request.json())
js = await request.json() #协程中用,同yield from
print(js, type(js))
text = dict(await request.post())
print(text, type(text))
js.update(text)
res = json.dumps(js)
print(res)
return web.Response(text=res, status=201) #201状态码表示Created
app = web.Application()
app.router.add_get('/', indexhandle)
app.router.add_get('/{id}', handle)
app.router.add_post('/api/todo', todopost)
app.logger.setLevel(level=logging.NOTSET)
web.run_app(app, host='0.0.0.0', port=8080)
输出:
======== Running on http://0.0.0.0:8080 ========
(Press CTRL+C to quit)
POST
<MatchInfo {}: <ResourceRoute [POST] <PlainResource /api/todo> -> <function todopost at 0x0000000003997A60>>
<coroutine object BaseRequest.json at 0x0000000003970F68>
{'completed': False, 'key': 'todo::1543038271618', 'title': 'test'} <class 'dict'>
{} <class 'dict'>
{"completed": false, "key": "todo::1543038271618", "title": "test"}
http://192.168.23.134
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。