74react_todolist2

 

 

目錄

todolist項目:... 1

階段5,狀態的控制和改變:... 1

階段6gettersetter... 5

階段7,過濾數據方法調整:... 9

階段8@computed的使用:... 13

階段9,完成狀態,Symbol類型:... 14

階段10axios,整合前後端:... 17

 

 

 

todolist項目:

 

階段5,狀態的控制和改變:

ReduxMobx,社區提供的狀態管理庫;

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}不動;

 

 

 

階段6gettersetter

類似pyproperty裝飾器;

 

./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>

        );

    }

}

 

 

 

階段7,過濾數據方法調整:

將過濾數據的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>

        );

    }

}

 

 

 

階段8@computed的使用:

mobx提供了@computed裝飾器,可用在任意類的屬性的getter上,它所依賴的值發生了變化就重新計算,否則直接返回上次計算的結果;

使用@computed裝飾get todos()

 

./src/service/service.js

import {observable, computed} from 'mobx';

export default class TodoService {

……

    @computed   //程序員感知不到變化,觀察對象_todosfilter任意一個變化都會重新計算

    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;

                }

            }

        );

    }

……

}

 

 

 

階段9,完成狀態,Symbol類型:

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)}>

antddefaultValue={TodoService.TODOSTATE.uncompleted},不支持傳遞類.屬性這種,只能用字符串;

 

 

 

階段10axios,整合前後端:

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,通過ajaxjquery(早期用)-->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.htmlapp-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/

1.jpg

 

2.jpg

注:

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

3.jpg

 

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章