73react_todolist项目1
admin
2023-07-08 13:04:25
0

 

 

目录

todolist项目:... 1

antd... 2

阶段1... 2

服务层实现:... 2

视图层实现,输入框处理:... 3

阶段2,列表显示、更改todo的完成状态:... 6

阶段3,项目目录调整:... 11

阶段4,数据过滤:... 12

 

 

 

todolist项目:

todo,待办;

 

人机交互,实质就是增删改查;

前端MVC框架、后端MVC框架;

 

注:

开发环境用:npm + webpack + babel

部署是另一套东西;

 

国际化:

中、英两版;

复杂的网页只作字符串转换并不合适;

antd中的国际化;

 

瀑布式开发设计(传统,需求调研,各种设计,时间周期长);

敏捷开发(快速迭代);

 

异步提交;部分刷新;事件通知机制;重绘DOM树;

 

 

antd

ant design,蚂蚁金服开源的React UI库;

 

https://ant.design/index-cn

https://ant.design/docs/react/introduce-cn

 

用户提交,需要表单控件,对于React来说也是一个组件,但这些组件需要用户看到,为了美观引入antd

 

$ npm i antd --save

……

+ antd@2.13.14

added 68 packages and updated 1 package in 44.219s

 

 

需求分析:

分层:

视图层,负责显示数据,每一个React组件一个xxx.js文件;

服务层,负责业务数据处理逻辑,命名为xxxService.js文件;

Model层,负责数据,用Local Storage代替;

文件均在项目根下的./src/里;

 

注:

服务层要与视图层划分清楚,这比较麻烦;

 

 

阶段1

服务层实现:

./src/service.js

JS不支持静态变量,但支持静态方法;

但在babel下可用static定义静态变量;

import store from 'store';

 

export default class TodoService {   //与组件无关,是类,大驼峰

    static NAMESPACE = 'todo::';   //前缀,查看https://www.zhihu.com/ApplicationLocal Storage,相同类型的用同一个前缀,易于区分;看业务情况分段,一般最多2段即可,否则代码复杂

    todos = [];   //用数组存储待办事宜,是在内存中,刷新网页后会清空,所以必须存在Local Storage

 

    create(title) {   //提供create方法,创建todo,把数据存储到Local Storage上(Local Storage当作是后台数据库,持久化用),同时插入到todos数组中;titlebrowser端来,是用户提交的输入,在视图层提供用户提交的文本框

        console.log('service');

        const todo = {

            key: TodoService.NAMESPACE + (new Date()).valueOf(),   //毫秒时间戳

            title: title,

            completed: false

        };

        this.todos.push(todo);

        store.set(todo.key, todo);

        return todo;

    }

}

注:

前端的Model层相对简单,不用ORM工具,而是用browserLocal Storage模拟的存储;

后端的Model层要用ORM框架;

 

 

视图层实现,输入框处理:

,输入框;

https://ant.design/components/input-cn/

 

输入框,能够让用户提交信息,使用接收回车键即提交信息(另,也可点一按键即提交,这是锦上添花),即键盘按下就是一个事件(与处理鼠标点击一样),在中;

输入框的“回车”和“按钮”是一个事件,增加按钮是锦上添花;

 

import { Input } from 'antd';

ReactDOM.render(, mountNode);

addonBefore,前缀标签;

addonAfter,后置标签;

placeholder,占位词;

onPressEnter,表示光标在输入框中,按下了回车键触发;

size="small",小输入框,large大输入框;

 

./src/Create.js

import React from 'react';

import { Input,Card } from 'antd';   //组合使用InputCard组件

 

// import { Input } from 'antd';

import 'antd/lib/input/style';   //样式导入

// ReactDOM.render(, mountNode);

 

// import { Card } from 'antd';

import 'antd/lib/card/style';

// ReactDOM.render(

//  

//     title="Card title"

//     extra={More}

//     style={{ width: 300 }}

//   >

//    

Card content

//    

Card content

//    

Card content

//   ,

//   mountNode);

 

// export default class Create extends React.Component {   //一定要导入React否则有异常

//     render() {

//         return (

//        

//            

//        

//         );

//     }

// }

 

export default props =>   //缺省导出无状态组件,这样写就是让不在这里面定义业务的方法

    <Card title='请输入待办事宜' style={{ width: 300 }}>

        <Input placeholder="Basic usage" onPressEnter={props.onCreate}/>   //等价于onPressEnter={(event) => {props.onCreate(event)}};测试时可用onPressEnter={(...args) => console.log('args is ', args)}看打印出什么,就能决定参数怎么写

       {/* {props.onCreate(event)}}/> */}   //onPressEnter本质就是要关联一个单个参数的函数就行了,相当于绑定一个函数对象;onPressEnter实现的是一个函数定义,这个函数定义是一个参数,这个参数就是event对象,该箭头函数本质就是调用props.onCreate(title)函数,这个箭头函数只是作转发,完全可省略,用上面那种写法

    Card>

注:


 

 

./src/index.js

import React from 'react';

import ReactDom from 'react-dom';

 

import Create from './Create';

 

import TodoService from './service';

 

class Root extends React.Component {

    constructor(props) {

        super(props);

        this.service = new TodoService();   //将业务的TodoService实例化,作为Root实例的属性

    }

 

    // handleCreate(...args) {   //测试用,看打印的信息,可知参数如何写;MDNhtml帮助,W3CSchool中的帮助

    //     console.log('Root handlerCreate');

    //     console.log(this);

    //     console.log('args is ', args);

    // }

    handleCreate(event) {   //该函数处理Input中获取到的用户的输入,及拿到数据后调用业务层service.js中相应的方法处理

        console.log('Root handleCreate');

        console.log(this);

        console.log('event is ', event.target, event.target.value);

        this.service.create(event.target.value);   //数据持久化,拿到用户输入的数据,之后的处理(不应该在Create.js组件里写类似这样的方法,数据交给index.jsRoot组件处理,这就需要组件间数据共享,使用props),应调用service.jsTodoService类的create方法,构造器中有实例化该类

    }

 

    render() {

        return (

            <div>

                <Create onCreate={this.handleCreate.bind(this)}/>   //自定义组件属性onCreate,其它名字也可,约定这样写,用于注入数据处理函数到Create组件的props中;此处要绑定Rootthis,否则handleCreate中打印的是Create元素的this

            div>

        )

    }

}

 

ReactDom.render(<Root />, document.getElementById('root'));

 

注:

73react_todolist项目1

 

73react_todolist项目1

若手动将Local Storage中的数据清掉,数组中仍有,这两处不会同步;

若再次刷新网页,内存数组中的会清掉;

 

 

 

阶段2,列表显示、更改todo的完成状态:

增加的todo待办事宜,得显示出来;

 

CheckBox,多选框,用来选中(完成)和取消(未完成);

onChange,选中|取消时触发回调函数;

checked,是否选中,如在Todo.js

https://ant.design/components/checkbox-cn/

 

Grid栅格系统:

布局方案,ant desighbootstrap很像,都使用一套栅格系统,使用24栅格,即每一个内部都能切分为24份;

栅格卡片:

https://ant.design/components/card-cn/

 

例:

m.values()m.keys()是迭代器(惰性求值),而array中的forEach()是立即返回;

m = new Map();

 

m.set(1,'a');

m.set(2,'b');

m.set(3,'c');

console.log(m);

 

let t = m.forEach((value,key) => console.log(key, '==', value));   //forEach方法遍历后,没有返回值

console.log(t);

t = [...m.values()].map(item => item + 1);   //map方法遍历后,有返回值

console.log(t);

 

k = [...m.keys()].map(item => item + 1);

console.log(k);

输出:

Map { 1 => 'a', 2 => 'b', 3 => 'c' }

1 '==' 'a'

2 '==' 'b'

3 '==' 'c'

undefined

[ 'a1', 'b1', 'c1' ]

[ 2, 3, 4 ]

 

 

 

./src/Todo.js,构建todo组件:

import React from 'react';

import { Checkbox, Card, Col, Row } from 'antd';

 

import 'antd/lib/checkbox/style';

import 'antd/lib/Card/style';

import 'antd/lib/Col/style';

import 'antd/lib/Row/style';

 

// import { Checkbox } from 'antd';

// function onChange(e) {

//   console.log(`checked = ${e.target.checked}`);

// }

// ReactDOM.render(

//   Checkbox,

//   mountNode);

 

// import { Card, Col, Row } from 'antd';

// ReactDOM.render(

//  

//    

//      

//         Card content

//      

//      

//         Card content

//      

//      

//         Card content

//      

//    

//  

,

//   mountNode);

 

export default props => (

    <Card style={{ width: 300 }}>

        <Row>

            <Col span={4}>

                <Checkbox checked={props.todo.completed} onChange={event => props.onChange(props.todo.key, event.target.checked)} />   //若没有checked属性,刷新页面后,完成状态的待办事宜不会选中,但Local Storage中的completedtrue

            Col>

            <Col span={20}>

                {props.todo.title}

            Col>

        Row>

    Card>

)

 

 

./src/index.js

import React from 'react';

import ReactDom from 'react-dom';

 

import Create from './Create';

 

import TodoService from './service';

 

import Todo from './Todo';

 

class Root extends React.Component {

    constructor(props) {

        super(props);

        this.service = new TodoService();

        this.state = ({todos: this.service.todos})   //若无此句,新增title后,Rootstate没变化,页面不会刷新导致看不到新增的,而在Local Storage中是有的

    }

 

    // 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.target, event.target.value);

        this.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.service.setTodoState(key, checked);

        this.setState({todos: this.service.todos});

    }

 

    render() {

        return (

            <div>

                <Create onCreate={this.handleCreate.bind(this)}/>

                <br />

                {/* {this.service.todos.map(

                    item => )

                } */}   //迭代所有todos元素,返回一个个React组件;如下图,要求迭代的元素有唯一的key,此处加上key={item.key}

                {

                    [...this.service.todos.values()].map(

                        item => <Todo key={item.key} todo={item} onChange={this.handleCheckedChange.bind(this)} />

                    )

                }

            div>

        );

    }

}

 

 

ReactDom.render(<Root />, document.getElementById('root'));

 

73react_todolist项目1

index.js组件中,加key={item.key}

 

./service.js

import store from 'store';

 

export default class TodoService {

    constructor() {

        // super();

        this.load();   //刷新页面后,从Local Storage装载数据,否则刷新页面后没有数据

    }

 

    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 = [];

    todos = new Map();   //遍历数组找到key匹配的,为高效使用ES6提供的Map类型

 

    create(title) {

        // console.log('service');

        const todo = {   //todo3个属性,keytitlecompletedcompleted表示完成状态

            key: TodoService.NAMESPACE + (new Date()).valueOf(),

            title: title,

            completed: false

        };

 

        // this.todos.push(todo);

        this.todos.set(todo.key, todo);   //存储todo

        store.set(todo.key, todo);   //持久化todo

        return todo;

    }

 

    setTodoState(key, checked) {

        let todo = this.todos.get(key);

        if (todo) {

            todo.completed = checked;

            store.set(key, todo);   //同步Local Storage

        }

    }

}

 

注:


 

 

 

阶段3,项目目录调整:

./src/component/{Create.js,Todo.js,TodoApp.js,Filter.js}   #渲染的事情归TodoApp负责,并管理所有的状态;Create负责显示文本框,接收用户的输入;Todo负责每一条待办事宜的显示;Filter负责状态的切换

./src/service/service.js   #负责业务的处理,为了简单,把数据处理也放在这里

./src/index.js

 

./src/index.js

import React from 'react';

import ReactDom from 'react-dom';

 

import TodoApp from './component/TodoApp';

 

ReactDom.render(<TodoApp />, document.getElementById('root'));

 

./src/component/TodoApp.js

import React from 'react';

 

import Create from './Create';

 

import TodoService from '../service/service';

 

import Todo from './Todo';

 

export default class Root extends React.Component {

    ……

}

 

 

 

阶段4,数据过滤:

过滤什么状态的待办事宜,有3种,未完成、完成、全部;

 

使用antdselect

https://ant.design/components/select-cn/

 

./src/component/Filter.js

import React from 'react';

 

import {Select, Card, Row, Col} from 'antd';

 

import 'antd/lib/select/style';

import 'antd/lib/card/style';

import 'antd/lib/row/style';

import 'antd/lib/col/style';

 

// import { Select } from 'antd';

// const Option = Select.Option;

// function handleChange(value) {

//   console.log(`selected ${value}`);

// }

// ReactDOM.render(

//  

//    

//    

//  

,

//   mountNode);

 

const Option = Select.Option;

 

export default props => (

    <Card style={{ width: 300 }}>

        <Row>

            <Col span="4">Col>

            <Col span="20">

                <Select style={{ width: 120 }} defaultValue="uncompleted" onChange={value => props.onChange(value)}>

                    <Option value="all">所有Option>

                    <Option value="completed">完成Option>

                    <Option value="uncompleted">未完成Option>

                Select>

            Col>

        Row>

    Card>

)

 

 

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

 

export default class Root extends React.Component {

    constructor(props) {

        super(props);

        this.service = new TodoService();

        this.state = ({todos: this.service.todos, filter: 'uncompleted'})

    }   //state的属性中加入filter

 

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

                } */}

                {/* {

                    [...this.service.todos.values()].map(

                        item =>

                    )

                } */}

                {

                    [...this.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)} />

                    )

                }   //迭代待办事宜时,加入数组的filter函数过滤

            div>

        );

    }

}

 

 

 


上一篇:mac如何安装jupyter

下一篇:74react_todolist2

相关内容

热门资讯

美前副总统:共和党失去了方向,... 2026年是美国的中期选举年,共和党选情不利,可能在年底的选举中遭遇挫败。美国前副总统彭斯5月31日...
南枝原来去过中国?《给阿嬷的情... 《给阿嬷的情书》票房口碑双丰收,目前票房已突破13亿。凤凰卫视最新一期《问答神州》专访了该片导演蓝鸿...
法国海军扣押一艘俄“影子舰队”... 近日,法国海军在大西洋海域扣押了一艘据称从俄罗斯摩尔曼斯克出发的油轮,引发俄方强烈不满。俄新社6月1...
凤凰晚报丨面粉染头模仿黄仁勋,... 今日人物【面粉染头模仿黄仁勋,农村青年走红后称遭“法务”警告】“先赔偿5000元肖像侵权使用费,再删...
亲特朗普极右派候选人领跑哥伦比... 【文/观察者网 熊超然】当地时间5月31日,哥伦比亚总统选举拉开帷幕,首轮投票计票工作已完成逾99%...
2026年度网络举报系列宣传活... 5月28日至29日,以“每一件举报,都是共治的力量——豫你e行 同心护网”为主题的2026年度网络举...
中原首例帝企鹅DNA性别鉴定!... 近日,郑州海昌海洋公园正式对外公布中原首对人工繁育的帝企鹅萌宝的DNA性别鉴定报告。这是中原首个将 ...
我国科学家为细胞信号“导航”开... 新华社济南5月31日电(记者张力元)人体细胞犹如一座精密的通信城市,每天都有大量“指令”穿梭传递,调...
极端大风突袭哈尔滨!过山车停摆... 极目新闻记者 詹钘5月31日,受强对流天气影响,哈尔滨国际会展中心体育场相关设施受到损坏,原计划当晚...
三原电缆取得电缆接头连接用防护... 国家知识产权局信息显示,上海三原电缆附件有限公司取得一项名为“一种电缆接头连接用防护结构”的专利,授...