├── .gitignore ├── todoMvc ├── todoMvc-1step │ ├── src │ │ ├── styles │ │ │ └── main.styl │ │ ├── entry.js │ │ └── components │ │ │ └── app.js │ ├── index.html │ ├── webpack.config.js │ ├── package.json │ ├── README.md │ └── out │ │ └── bundle.js ├── todoMvc-2step │ ├── src │ │ ├── styles │ │ │ └── main.styl │ │ ├── entry.js │ │ ├── components │ │ │ └── app.js │ │ └── js │ │ │ └── localDb.js │ ├── index.html │ ├── webpack.config.js │ ├── package.json │ └── README.md ├── todoMvc-3step │ ├── src │ │ ├── styles │ │ │ └── main.styl │ │ ├── components │ │ │ ├── TodoFooter.js │ │ │ ├── TodoItem.js │ │ │ ├── TodoHeader.js │ │ │ ├── TodoMain.js │ │ │ └── app.js │ │ ├── entry.js │ │ └── js │ │ │ └── localDb.js │ ├── index.html │ ├── webpack.config.js │ ├── package.json │ └── README.md ├── todoMvc-4step │ ├── .babelrc │ ├── src │ │ ├── entry.js │ │ ├── components │ │ │ ├── TodoMain.js │ │ │ ├── TodoFooter.js │ │ │ ├── TodoItem.js │ │ │ ├── TodoHeader.js │ │ │ └── app.js │ │ ├── styles │ │ │ └── main.styl │ │ └── js │ │ │ └── localDb.js │ ├── index.html │ ├── webpack.config.js │ ├── package.json │ └── README.md ├── todoMvc-5step │ ├── .babelrc │ ├── src │ │ ├── entry.js │ │ ├── components │ │ │ ├── TodoFooter.js │ │ │ ├── TodoMain.js │ │ │ ├── TodoItem.js │ │ │ ├── Login.js │ │ │ ├── TodoHeader.js │ │ │ └── app.js │ │ ├── styles │ │ │ └── main.styl │ │ └── js │ │ │ └── localDb.js │ ├── index.html │ ├── webpack.config.js │ ├── package.json │ └── README.md └── README.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | .DS_Store/ -------------------------------------------------------------------------------- /todoMvc/todoMvc-1step/src/styles/main.styl: -------------------------------------------------------------------------------- 1 | .todo-title 2 | color red -------------------------------------------------------------------------------- /todoMvc/todoMvc-2step/src/styles/main.styl: -------------------------------------------------------------------------------- 1 | .todo-title 2 | color green -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-demo 2 | ####react 学习仓库 3 | 4 | 欢迎 star,fork,提交issues 5 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-3step/src/styles/main.styl: -------------------------------------------------------------------------------- 1 | .todo-title 2 | color red 3 | font-family "Weibei TC" -------------------------------------------------------------------------------- /todoMvc/todoMvc-1step/src/entry.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | require('./components/app') 3 | require('./styles/main.styl') -------------------------------------------------------------------------------- /todoMvc/todoMvc-3step/src/components/TodoFooter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 17/02/2017. 3 | */ 4 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-4step/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [["import", {"libraryName": "antd", "style": "css"}]] 3 | } 4 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-5step/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [["import", {"libraryName": "antd", "style": "css"}]] 3 | } 4 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-1step/src/components/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 16/02/2017. 3 | */ 4 | alert('success') -------------------------------------------------------------------------------- /todoMvc/todoMvc-2step/src/entry.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // require('./components/app') 3 | // require('./styles/main.styl') 4 | 5 | import './components/app' //es6写法 6 | import './styles/main.styl' //es6写法 -------------------------------------------------------------------------------- /todoMvc/todoMvc-3step/src/entry.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // require('./components/app') 3 | // require('./styles/main.styl') 4 | 5 | import './components/app' //es6写法 6 | import './styles/main.styl' //es6写法 -------------------------------------------------------------------------------- /todoMvc/todoMvc-4step/src/entry.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // require('./components/app') 3 | // require('./styles/main.styl') 4 | 5 | import './components/app' //es6写法 6 | import './styles/main.styl' //es6写法 -------------------------------------------------------------------------------- /todoMvc/todoMvc-5step/src/entry.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // require('./components/app') 3 | // require('./styles/main.styl') 4 | 5 | import './components/app' //es6写法 6 | import './styles/main.styl' //es6写法 -------------------------------------------------------------------------------- /todoMvc/todoMvc-4step/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | react 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-5step/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | react 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-1step/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | react 6 | 7 | 8 |
9 |

React-Todos

10 |
11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-2step/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | react 6 | 7 | 8 |
9 |

React-Todos

10 |
11 |
12 |
13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-3step/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | react 6 | 7 | 8 |
9 |

React-Todos

10 |
11 |
12 |
13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-3step/src/components/TodoItem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 17/02/2017. 3 | */ 4 | import React from 'react' 5 | 6 | class TodoItem extends React.Component { 7 | render() { 8 | let className = this.props.isDone?'task-done':'' 9 | return ( 10 |
  • 11 | 15 |
  • 16 | ) 17 | } 18 | } 19 | 20 | export default TodoItem -------------------------------------------------------------------------------- /todoMvc/todoMvc-5step/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: [ 3 | "./src/entry.js" //入口文件 4 | ], 5 | output: { 6 | path: './out/', 7 | filename: "bundle.js" //输出文件 8 | }, 9 | module: { 10 | loaders: [ 11 | { test: /\.js[x]?$/, loader: "babel-loader?presets[]=es2015&presets[]=react", include: /src/},//支持es6 12 | { test: /\.css$/, loader: "style-loader!css-loader"}, 13 | { test: /\.styl$/, loader: "style-loader!css-loader!stylus-loader"},//支持stylus 14 | { test: /\.(png|jpg)$/, loader: 'url?limit=8192'} 15 | ] 16 | } 17 | }; -------------------------------------------------------------------------------- /todoMvc/todoMvc-1step/webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 16/02/2017. 3 | */ 4 | module.exports = { 5 | entry: [ 6 | "./src/entry.js" 7 | ], 8 | output: { 9 | path: './out/', 10 | filename: "bundle.js" 11 | }, 12 | module: { 13 | loaders: [ 14 | { test: /\.js[x]?$/, loader: "babel-loader?presets[]=es2015&presets[]=react", include: /src/}, 15 | { test: /\.css$/, loader: "style!css"}, 16 | { test: /\.styl$/, loader: "style-loader!css-loader!stylus-loader"}, 17 | { test: /\.(png|jpg)$/, loader: 'url?limit=8192'} 18 | ] 19 | } 20 | }; -------------------------------------------------------------------------------- /todoMvc/todoMvc-2step/webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 16/02/2017. 3 | */ 4 | module.exports = { 5 | entry: [ 6 | "./src/entry.js" //入口文件 7 | ], 8 | output: { 9 | path: './out/', 10 | filename: "bundle.js" //输出文件 11 | }, 12 | module: { 13 | loaders: [ 14 | { test: /\.js[x]?$/, loader: "babel-loader?presets[]=es2015&presets[]=react", include: /src/},//支持es6 15 | { test: /\.css$/, loader: "style!css"}, 16 | { test: /\.styl$/, loader: "style-loader!css-loader!stylus-loader"},//支持stylus 17 | { test: /\.(png|jpg)$/, loader: 'url?limit=8192'} 18 | ] 19 | } 20 | }; -------------------------------------------------------------------------------- /todoMvc/todoMvc-3step/webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 16/02/2017. 3 | */ 4 | module.exports = { 5 | entry: [ 6 | "./src/entry.js" //入口文件 7 | ], 8 | output: { 9 | path: './out/', 10 | filename: "bundle.js" //输出文件 11 | }, 12 | module: { 13 | loaders: [ 14 | { test: /\.js[x]?$/, loader: "babel-loader?presets[]=es2015&presets[]=react", include: /src/},//支持es6 15 | { test: /\.css$/, loader: "style!css"}, 16 | { test: /\.styl$/, loader: "style-loader!css-loader!stylus-loader"},//支持stylus 17 | { test: /\.(png|jpg)$/, loader: 'url?limit=8192'} 18 | ] 19 | } 20 | }; -------------------------------------------------------------------------------- /todoMvc/todoMvc-4step/webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 16/02/2017. 3 | */ 4 | module.exports = { 5 | entry: [ 6 | "./src/entry.js" //入口文件 7 | ], 8 | output: { 9 | path: './out/', 10 | filename: "bundle.js" //输出文件 11 | }, 12 | module: { 13 | loaders: [ 14 | { test: /\.js[x]?$/, loader: "babel-loader?presets[]=es2015&presets[]=react", include: /src/},//支持es6 15 | { test: /\.css$/, loader: "style-loader!css-loader"}, 16 | { test: /\.styl$/, loader: "style-loader!css-loader!stylus-loader"},//支持stylus 17 | { test: /\.(png|jpg)$/, loader: 'url?limit=8192'} 18 | ] 19 | } 20 | }; -------------------------------------------------------------------------------- /todoMvc/todoMvc-3step/src/components/TodoHeader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 17/02/2017. 3 | */ 4 | import React from 'react' 5 | 6 | class TodoHeader extends React.Component { 7 | // 绑定键盘回车事件,添加新任务 8 | handlerKeyUp(e) { 9 | if(e.keyCode == 13) { 10 | let value = e.target.value; 11 | if(!value) return false; 12 | let newTodoItem = { 13 | text: value, 14 | isDone: false 15 | }; 16 | e.target.value = ''; 17 | this.props.addTodo(newTodoItem) 18 | } 19 | } 20 | render(){ 21 | return ( 22 |
    23 | 24 |
    25 | ) 26 | } 27 | } 28 | export default TodoHeader -------------------------------------------------------------------------------- /todoMvc/todoMvc-3step/src/components/TodoMain.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 17/02/2017. 3 | */ 4 | import React from 'react' 5 | import TodoItem from './TodoItem' 6 | 7 | class TodoMain extends React.Component { 8 | render(){ 9 | if(this.props.todos.length == 0) { 10 | return ( 11 |
    恭喜您,目前没有待办任务
    12 | ) 13 | } else { 14 | return ( 15 | 23 | ) 24 | } 25 | } 26 | } 27 | export default TodoMain -------------------------------------------------------------------------------- /todoMvc/todoMvc-3step/src/components/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 16/02/2017. 3 | */ 4 | import React from 'react' 5 | import ReactDOM from 'react-dom' 6 | import TodoHeader from './TodoHeader' 7 | import TodoMain from './TodoMain' 8 | 9 | class App extends React.Component { //定义组件,继承父类 10 | constructor() { 11 | super() 12 | this.state = { 13 | todos: [] 14 | } 15 | } 16 | addTodo(item) { 17 | this.state.todos.push(item) 18 | this.setState({todos: this.state.todos}); //设置状态 19 | } 20 | render(){ 21 | return ( 22 |
    23 | 24 | 25 |
    26 | ) 27 | } 28 | } 29 | 30 | 31 | ReactDOM.render(,document.getElementById('app')) -------------------------------------------------------------------------------- /todoMvc/todoMvc-4step/src/components/TodoMain.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 17/02/2017. 3 | */ 4 | import React from 'react' 5 | import TodoItem from './TodoItem' 6 | 7 | class TodoMain extends React.Component { 8 | render(){ 9 | if(this.props.todos.length == 0) { 10 | return ( 11 |
    恭喜您,目前没有待办任务
    12 | ) 13 | } else { 14 | return ( 15 | 23 | ) 24 | } 25 | } 26 | } 27 | export default TodoMain -------------------------------------------------------------------------------- /todoMvc/todoMvc-4step/src/components/TodoFooter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 17/02/2017. 3 | */ 4 | import React from 'react' 5 | import { Button,Checkbox } from 'antd' 6 | 7 | class TodoFooter extends React.Component{ 8 | deleteAll(){ 9 | this.props.clearDone() 10 | } 11 | changeAll(e){ 12 | this.props.changeTodoState(null,e.target.checked,true) 13 | } 14 | render(){ 15 | let minus = this.props.todoCount - this.props.todoDoneCount 16 | return ( 17 |
    18 | 21 | 还剩{minus}未完成 22 | 23 |
    24 | ) 25 | } 26 | } 27 | 28 | export default TodoFooter -------------------------------------------------------------------------------- /todoMvc/todoMvc-4step/src/styles/main.styl: -------------------------------------------------------------------------------- 1 | #app 2 | width 60% 3 | padding 20px 4 | margin 0 auto 5 | .todo-empty 6 | text-align center 7 | padding 10px 0 8 | font-size 16px 9 | .todo-title 10 | color red 11 | font-family "Weibei TC" 12 | text-align center 13 | .todo-main 14 | li 15 | padding 10px 0 16 | height 50px 17 | .time 18 | display inline-block 19 | vertical-align top 20 | width 31% 21 | min-width 160px 22 | font-size 16px 23 | .task 24 | display inline-block 25 | vertical-align top 26 | width 56% 27 | font-size 16px 28 | &.task-done 29 | text-decoration line-through 30 | .ant-btn 31 | vertical-align top 32 | display none 33 | .todo-footer 34 | display flex 35 | justify-content space-between 36 | font-size 14px -------------------------------------------------------------------------------- /todoMvc/todoMvc-5step/src/components/TodoFooter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 17/02/2017. 3 | */ 4 | import React from 'react' 5 | import { Button,Checkbox } from 'antd' 6 | 7 | class TodoFooter extends React.Component{ 8 | deleteAll(){ 9 | this.props.clearDone() 10 | } 11 | changeAll(e){ 12 | this.props.changeTodoState(null,e.target.checked,true) 13 | } 14 | render(){ 15 | let minus = this.props.todoCount - this.props.todoDoneCount 16 | return ( 17 |
    18 | 21 | 还剩{minus}未完成 22 | 23 |
    24 | ) 25 | } 26 | } 27 | 28 | export default TodoFooter -------------------------------------------------------------------------------- /todoMvc/todoMvc-2step/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tomvc", 3 | "version": "1.0.0", 4 | "description": "react", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "react-demo" 11 | ], 12 | "author": "zxw", 13 | "license": "ISC", 14 | "dependencies": { 15 | "normalize.css": "^5.0.0", 16 | "react": "^15.4.2", 17 | "react-dom": "^15.4.2" 18 | }, 19 | "devDependencies": { 20 | "babel-core": "^6.22.1", 21 | "babel-loader": "^6.2.10", 22 | "babel-preset-es2015": "^6.18.0", 23 | "babel-preset-react": "^6.16.0", 24 | "css-loader": "^0.14.5", 25 | "file-loader": "^0.8.4", 26 | "jsx-loader": "^0.13.2", 27 | "node-libs-browser": "^0.5.2", 28 | "style-loader": "^0.12.3", 29 | "stylus-loader": "^1.0.2", 30 | "url-loader": "^0.5.6", 31 | "webpack": "^2.2.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-3step/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tomvc", 3 | "version": "1.0.0", 4 | "description": "react", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "react-demo" 11 | ], 12 | "author": "zxw", 13 | "license": "ISC", 14 | "dependencies": { 15 | "normalize.css": "^5.0.0", 16 | "react": "^15.4.2", 17 | "react-dom": "^15.4.2" 18 | }, 19 | "devDependencies": { 20 | "babel-core": "^6.22.1", 21 | "babel-loader": "^6.2.10", 22 | "babel-preset-es2015": "^6.18.0", 23 | "babel-preset-react": "^6.16.0", 24 | "css-loader": "^0.14.5", 25 | "file-loader": "^0.8.4", 26 | "jsx-loader": "^0.13.2", 27 | "node-libs-browser": "^0.5.2", 28 | "style-loader": "^0.12.3", 29 | "stylus-loader": "^1.0.2", 30 | "url-loader": "^0.5.6", 31 | "webpack": "^2.2.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-5step/src/components/TodoMain.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 17/02/2017. 3 | */ 4 | import React from 'react' 5 | import TodoItem from './TodoItem' 6 | 7 | class TodoMain extends React.Component { 8 | componentDidUpdate() { 9 | this.props.saveOrUpdateTodos() 10 | } 11 | render(){ 12 | if(this.props.todos.length == 0) { 13 | return ( 14 |
    恭喜您,目前没有待办任务!
    15 | ) 16 | } else { 17 | return ( 18 | 26 | ) 27 | } 28 | } 29 | } 30 | export default TodoMain -------------------------------------------------------------------------------- /todoMvc/todoMvc-1step/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tomvc", 3 | "version": "1.0.0", 4 | "description": "react", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "react-demo" 11 | ], 12 | "author": "zxw", 13 | "license": "ISC", 14 | "dependencies": { 15 | "normalize.css": "^5.0.0", 16 | "react": "^15.4.2", 17 | "react-dom": "^15.4.2" 18 | }, 19 | "devDependencies": { 20 | "babel-core": "^6.22.1", 21 | "babel-loader": "^6.2.10", 22 | "babel-preset-es2015": "^6.18.0", 23 | "babel-preset-react": "^6.16.0", 24 | "css-loader": "^0.14.5", 25 | "file-loader": "^0.8.4", 26 | "jsx-loader": "^0.13.2", 27 | "node-libs-browser": "^0.5.2", 28 | "style-loader": "^0.12.3", 29 | "stylus": "^0.54.5", 30 | "stylus-loader": "^1.0.2", 31 | "url-loader": "^0.5.6", 32 | "webpack": "^2.2.1" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-4step/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tomvc", 3 | "version": "1.0.0", 4 | "description": "react", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "react-demo" 11 | ], 12 | "author": "zxw", 13 | "license": "ISC", 14 | "dependencies": { 15 | "antd": "^2.7.1", 16 | "normalize.css": "^5.0.0", 17 | "react": "^15.4.2", 18 | "react-dom": "^15.4.2" 19 | }, 20 | "devDependencies": { 21 | "babel-core": "^6.22.1", 22 | "babel-loader": "^6.2.10", 23 | "babel-plugin-import": "^1.1.1", 24 | "babel-preset-es2015": "^6.18.0", 25 | "babel-preset-react": "^6.16.0", 26 | "css-loader": "^0.14.5", 27 | "file-loader": "^0.8.4", 28 | "jsx-loader": "^0.13.2", 29 | "node-libs-browser": "^0.5.2", 30 | "style-loader": "^0.12.3", 31 | "stylus-loader": "^1.0.2", 32 | "url-loader": "^0.5.6", 33 | "webpack": "^2.2.1" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-5step/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tomvc", 3 | "version": "1.0.0", 4 | "description": "react", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "webpack-dev-server", 9 | "build": "webpack" 10 | }, 11 | "keywords": [ 12 | "react-demo" 13 | ], 14 | "author": "zxw", 15 | "license": "ISC", 16 | "dependencies": { 17 | "antd": "^2.7.1", 18 | "leancloud-storage": "^2.1.2", 19 | "normalize.css": "^5.0.0", 20 | "react": "^15.4.2", 21 | "react-dom": "^15.4.2" 22 | }, 23 | "devDependencies": { 24 | "babel-core": "^6.22.1", 25 | "babel-loader": "^6.2.10", 26 | "babel-plugin-import": "^1.1.1", 27 | "babel-preset-es2015": "^6.18.0", 28 | "babel-preset-react": "^6.16.0", 29 | "css-loader": "^0.14.5", 30 | "file-loader": "^0.8.4", 31 | "html-webpack-plugin": "^2.28.0", 32 | "jsx-loader": "^0.13.2", 33 | "node-libs-browser": "^0.5.2", 34 | "style-loader": "^0.12.3", 35 | "stylus-loader": "^1.0.2", 36 | "url-loader": "^0.5.6", 37 | "webpack": "^2.2.1", 38 | "webpack-dev-server": "^2.4.1", 39 | "webpack-merge": "^3.0.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-4step/src/components/TodoItem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 17/02/2017. 3 | */ 4 | import React from 'react' 5 | import ReactDOM from 'react-dom' 6 | import { Button,Checkbox } from 'antd' 7 | 8 | class TodoItem extends React.Component { 9 | handlerChange() { 10 | let isDone = !this.props.isDone; 11 | this.props.changeTodoState(this.props.index, isDone); 12 | } 13 | handlerDelete() { 14 | this.props.deleteTodo(this.props.index) 15 | } 16 | handlerMouseIn () { 17 | ReactDOM.findDOMNode(this.refs.delButton).style.display = 'inline-block' 18 | } 19 | handlerMouseOut () { 20 | ReactDOM.findDOMNode(this.refs.delButton).style.display = 'none' 21 | } 22 | render() { 23 | let className = this.props.isDone?'task-done':'' 24 | return ( 25 |
  • 26 | 27 | {this.props.time} 28 | {this.props.text} 29 | 30 |
  • 31 | ) 32 | } 33 | } 34 | 35 | export default TodoItem -------------------------------------------------------------------------------- /todoMvc/README.md: -------------------------------------------------------------------------------- 1 | #### 此项目记录了本人从零开始学习用业余时间做经典的 React-todoMVC 2 | 3 | 每个step做一个关键知识点,循序渐进,希望可以帮助像我一样刚开始找不到头绪的同学,近期我会把详细教程也写出来 4 | 5 | ####文件目录 6 | 7 | 1. [todoMvc-1step](https://github.com/Zegendary/react-demo/tree/master/todoMvc/todoMvc-1step) [演示1](https://zegendary.github.io/react-demo/todoMvc/todoMvc-1step/) webpack+react项目构建 8 | 9 | 2. [todoMvc-2step](https://github.com/Zegendary/react-demo/tree/master/todoMvc/todoMvc-2step) [演示2](https://zegendary.github.io/react-demo/todoMvc/todoMvc-2step/) 实现双向绑定功能 10 | 11 | 3. [todoMvc-3step](https://github.com/Zegendary/react-demo/tree/master/todoMvc/todoMvc-3step) [演示3](https://zegendary.github.io/react-demo/todoMvc/todoMvc-3step/) 将项目组件化 12 | 13 | 4. [todoMvc-4step](https://github.com/Zegendary/react-demo/tree/master/todoMvc/todoMvc-4step) [演示4](https://zegendary.github.io/react-demo/todoMvc/todoMvc-4step/) 完成todoMvc所有功能,包括全选,清除全部已完成,并引入[Ant-Design](https://ant.design/index-cn)UI 14 | 15 | 5. [todoMvc-5step](https://github.com/Zegendary/react-demo/tree/master/todoMvc/todoMvc-5step) [演示5](https://zegendary.github.io/react-demo/todoMvc/todoMvc-5step/) 利用LeanCloud第三方储存,将账户,密码,已经todo内容存储到第三方服务器中 16 | 17 | 如何打开本项目下的demo? 18 | 19 | 1. `git clone git@github.com:Zegendary/react-demo.git` 20 | 21 | 2. `cd react-demo/todoMvc/你感兴趣的步骤` 22 | 23 | 3. `npm install` 24 | 25 | 4. `webpack -w` 26 | 27 | 5. `open index.html` -------------------------------------------------------------------------------- /todoMvc/todoMvc-5step/src/components/TodoItem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 17/02/2017. 3 | */ 4 | import React from 'react' 5 | import ReactDOM from 'react-dom' 6 | import { Button,Checkbox } from 'antd' 7 | 8 | class TodoItem extends React.Component { 9 | handlerChange() { 10 | let isDone = !this.props.isDone; 11 | this.props.changeTodoState(this.props.index, isDone); 12 | } 13 | handlerDelete() { 14 | this.props.deleteTodo(this.props.index) 15 | } 16 | handlerMouseIn () { 17 | ReactDOM.findDOMNode(this.refs.delButton).style.display = 'inline-block' 18 | } 19 | handlerMouseOut () { 20 | ReactDOM.findDOMNode(this.refs.delButton).style.display = 'none' 21 | } 22 | render() { 23 | let className = this.props.isDone?'task-done':'' 24 | return ( 25 |
  • 26 | 27 | 28 | {this.props.time} 29 | {this.props.text} 30 | 31 | 32 |
  • 33 | ) 34 | } 35 | } 36 | 37 | export default TodoItem -------------------------------------------------------------------------------- /todoMvc/todoMvc-5step/src/styles/main.styl: -------------------------------------------------------------------------------- 1 | #app 2 | width 60% 3 | padding 20px 4 | margin 0 auto 5 | .todo-empty 6 | text-align center 7 | padding 10px 0 8 | font-size 16px 9 | .todo-title 10 | color red 11 | font-family "Weibei TC" 12 | text-align center 13 | .user-info 14 | display flex 15 | justify-content space-between 16 | font-size 14px 17 | margin 10px 0 18 | .todo-main 19 | li 20 | padding 10px 0 21 | height 50px 22 | .time-task 23 | display inline-block 24 | vertical-align top 25 | width calc(100% - 75px) 26 | font-size 0 27 | .time 28 | display inline-block 29 | vertical-align top 30 | width 40% 31 | font-size 16px 32 | .task 33 | display inline-block 34 | vertical-align top 35 | width 60% 36 | font-size 16px 37 | &.task-done 38 | text-decoration line-through 39 | .ant-btn 40 | vertical-align top 41 | display none 42 | width 50px 43 | .todo-footer 44 | display flex 45 | justify-content space-between 46 | font-size 14px 47 | .form-wrapper 48 | width 250px 49 | margin 0 auto 50 | .radio-wrapper 51 | margin 10px 0 52 | width 100% 53 | text-align center 54 | .login-form-button 55 | width 100% -------------------------------------------------------------------------------- /todoMvc/todoMvc-2step/src/components/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 16/02/2017. 3 | */ 4 | import React from 'react' 5 | import ReactDOM from 'react-dom' 6 | 7 | class App extends React.Component { //定义组件,继承父类 8 | constructor() {//constructor 是和 class 一起用来创建和初始化对象的特殊方法。 9 | super()//在装载组件(mounting)之前调用会React组件的构造函数。当实现React.Component子类的构造函数时,应该在任何其他语句之前调用super(props) 10 | this.state = {//设置初始状态 11 | todos: [] 12 | } 13 | } 14 | // 绑定键盘回车事件,添加新任务 15 | handlerKeyUp(e) { 16 | if(e.keyCode == 13) { 17 | let value = e.target.value; 18 | if(!value) return false; 19 | let newTodoItem = { 20 | text: value, 21 | isDone: false 22 | }; 23 | e.target.value = ''; 24 | this.state.todos.push(newTodoItem) 25 | this.setState({todos: this.state.todos}); //修改状态值,每次修改以后,自动调用 this.render 方法,再次渲染组件。 26 | } 27 | } 28 | render(){ 29 | return ( 30 |
    31 | 32 | 39 |
    40 | ) 41 | } 42 | } 43 | 44 | 45 | ReactDOM.render(,document.getElementById('app')) -------------------------------------------------------------------------------- /todoMvc/todoMvc-5step/src/components/Login.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 20/02/2017. 3 | */ 4 | import React from 'react' 5 | import { Form, Icon, Input, Button } from 'antd'; 6 | 7 | const FormItem = Form.Item; 8 | 9 | const Login = Form.create()(React.createClass({ 10 | handleSubmit(e) { 11 | e.preventDefault(); 12 | this.props.form.validateFields((err, values) => { 13 | if (!err) { 14 | this.props.loginOrSignUp(values) 15 | } 16 | }); 17 | }, 18 | render() { 19 | const { getFieldDecorator } = this.props.form; 20 | let text = this.props.value == 1 ?'注册':'登陆' 21 | return ( 22 |
    23 | 24 | {getFieldDecorator('userName', { 25 | rules: [{ required: true, message: 'Please input your username!' }], 26 | })( 27 | } placeholder="Username" /> 28 | )} 29 | 30 | 31 | {getFieldDecorator('password', { 32 | rules: [{ required: true, message: 'Please input your Password!' }], 33 | })( 34 | } type="password" placeholder="Password" /> 35 | )} 36 | 37 | 38 | 41 | 42 |
    43 | ); 44 | }, 45 | })); 46 | export default Login -------------------------------------------------------------------------------- /todoMvc/todoMvc-4step/src/components/TodoHeader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 17/02/2017. 3 | */ 4 | import React from 'react' 5 | import ReactDOM from 'react-dom' 6 | import { Input } from 'antd' 7 | 8 | class TodoHeader extends React.Component { 9 | // 绑定键盘回车事件,添加新任务 10 | handlerKeyUp(e) { 11 | if(e.keyCode == 13) { 12 | let value = e.target.value; 13 | if(!value) return false; 14 | let date = new Date().Format("yyyy-MM-dd hh:mm") 15 | let newTodoItem = { 16 | text: value, 17 | isDone: false, 18 | time: date 19 | }; 20 | e.target.value = ''; 21 | this.props.addTodo(newTodoItem) 22 | } 23 | } 24 | componentWillMount() { 25 | //日期格式化 26 | Date.prototype.Format = function (fmt) { 27 | var o = { 28 | "M+": this.getMonth() + 1, //月份 29 | "d+": this.getDate(), //日 30 | "h+": this.getHours(), //小时 31 | "m+": this.getMinutes(), //分 32 | "s+": this.getSeconds(), //秒 33 | "q+": Math.floor((this.getMonth() + 3) / 3), //季度 34 | "S": this.getMilliseconds() //毫秒 35 | }; 36 | if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); 37 | for (var k in o) 38 | if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); 39 | return fmt; 40 | } 41 | } 42 | render(){ 43 | return ( 44 |
    45 |

    React-Todos

    46 | 47 |
    48 | ) 49 | } 50 | } 51 | export default TodoHeader -------------------------------------------------------------------------------- /todoMvc/todoMvc-4step/README.md: -------------------------------------------------------------------------------- 1 | [todoMvc-4step源码](https://github.com/Zegendary/react-demo/tree/master/todoMvc/todoMvc-4step) 2 | 3 | [todoMvc-4step演示](https://zegendary.github.io/react-demo/todoMvc/todoMvc-4step/) 4 | 5 | 这一章主要以【删除】键为例讲一下如何使用以 [React](http://facebook.github.io/react/) 封装了一套 [Ant Design](https://ant.design/index-cn) 的组件库: 6 | 7 | ![Ant Design](http://upload-images.jianshu.io/upload_images/1826203-f3afc4ccb39696a9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 8 | 9 | ### 1.安装 10 | 11 | 推荐使用 npm 的方式进行开发,不仅可在开发环境轻松调试,也可放心地在生产环境打包部署使用,享受整个生态圈和工具链带来的诸多好处。 12 | 可以通过 npm 直接安装到项目中,使用 `import` 或 `require` 进行引用。 13 | 14 | $ npm install antd --save 15 | 16 | ### 2.加载 17 | 18 | 可以通过以下的写法来按需加载组件。 19 | 20 | import Button from 'antd/lib/button'; 21 | import 'antd/lib/button/style'; // 或者 antd/lib/button/style/css 加载 css 文件 22 | 23 | 但我推荐使用更简便的写法: 24 | 首先需要安装[babel-plugin-import](https://github.com/ant-design/babel-plugin-import) 依赖 25 | 26 | $ npm install babel-plugin-import --save-dev 27 | 28 | 然后在我们的根目录下新建`.babelrc`: 29 | 30 | { 31 | "plugins": [["import", {"libraryName": "antd", "style": "css"}]] //import js and css modularly 32 | } 33 | 34 | 这时我们需要什么UI组件,即可如下这么写以达到按需加载`js`和`css`: 35 | 36 | import { Button } from 'antd'; 37 | 38 | ### 3.使用 39 | 40 | 由于`Antd`组件已经油`React`封装好了,用法和原生html标签没差: 41 | 42 | 43 | 44 | 剩余的样式我们就可以对着[antd components](https://ant.design/components/button-cn/)的demo来开发。 45 | 46 | [React+webpack从0到1开发一个todoMvc(一)](http://www.jianshu.com/p/aa02a10c5b69) 47 | [React+webpack从0到1开发一个todoMvc(二)](http://www.jianshu.com/p/ed01cf27b23d) 48 | [React+webpack从0到1开发一个todoMvc(三)](http://www.jianshu.com/p/80e54fc179e4)  49 | [React+webpack从0到1开发一个todoMvc(五)](http://www.jianshu.com/p/86b83192917d)  -------------------------------------------------------------------------------- /todoMvc/todoMvc-5step/src/components/TodoHeader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 17/02/2017. 3 | */ 4 | import React from 'react' 5 | import ReactDOM from 'react-dom' 6 | import { Input,Button } from 'antd' 7 | 8 | class TodoHeader extends React.Component { 9 | // 绑定键盘回车事件,添加新任务 10 | handlerKeyUp(e) { 11 | if(e.keyCode == 13) { 12 | let value = e.target.value; 13 | if(!value) return false; 14 | let date = new Date().Format("yyyy-MM-dd hh:mm") 15 | let newTodoItem = { 16 | text: value, 17 | isDone: false, 18 | time: date 19 | }; 20 | e.target.value = ''; 21 | this.props.addTodo(newTodoItem) 22 | } 23 | } 24 | loginOut(){ 25 | this.props.logout() 26 | } 27 | componentWillMount() { 28 | //日期格式化 29 | Date.prototype.Format = function (fmt) { 30 | var o = { 31 | "M+": this.getMonth() + 1, //月份 32 | "d+": this.getDate(), //日 33 | "h+": this.getHours(), //小时 34 | "m+": this.getMinutes(), //分 35 | "s+": this.getSeconds(), //秒 36 | "q+": Math.floor((this.getMonth() + 3) / 3), //季度 37 | "S": this.getMilliseconds() //毫秒 38 | }; 39 | if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); 40 | for (var k in o) 41 | if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); 42 | return fmt; 43 | } 44 | } 45 | render(){ 46 | return ( 47 |
    48 |

    React-Todos

    49 |

    50 | {"欢迎你,"+ this.props.currentUser.username} 51 | 52 |

    53 | 54 |
    55 | ) 56 | } 57 | } 58 | export default TodoHeader -------------------------------------------------------------------------------- /todoMvc/todoMvc-4step/src/components/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 16/02/2017. 3 | */ 4 | import React from 'react' 5 | import ReactDOM from 'react-dom' 6 | import TodoHeader from './TodoHeader' 7 | import TodoMain from './TodoMain' 8 | import TodoFooter from './TodoFooter' 9 | 10 | class App extends React.Component { //定义组件,继承父类 11 | constructor() { 12 | super() 13 | this.state = { 14 | todos: [], 15 | isAllChecked: false 16 | } 17 | } 18 | addTodo(item) { 19 | this.state.todos.push(item) 20 | this.setState({todos: this.state.todos}); //设置状态 21 | } 22 | // 改变任务状态,传递给TodoItem和Footer组件的方法 23 | changeTodoState(index, isDone, isChangeAll=false){ //初始化isChangeAll为false 24 | if(isChangeAll){ //全部操作 25 | this.setState({ 26 | todos: this.state.todos.map((todo) => { 27 | todo.isDone = isDone; 28 | return todo; 29 | }), 30 | isAllChecked: isDone 31 | }); 32 | }else{ //操作其中一个todo 33 | this.state.todos[index].isDone = isDone; 34 | this.allChecked(); 35 | } 36 | } 37 | // 判断是否所有任务的状态都完成,同步底部的全选框 38 | allChecked() { 39 | let isAllChecked = false; 40 | if (this.state.todos.every(todo => todo.isDone)) { 41 | isAllChecked = true; 42 | } 43 | this.setState({ //改变状态,组件重绘 44 | todos: this.state.todos, 45 | isAllChecked: isAllChecked 46 | }); 47 | } 48 | clearDone(){ 49 | let todos = this.state.todos.filter(todo => !todo.isDone) 50 | this.setState({ 51 | todos : todos 52 | }) 53 | } 54 | deleteTodo(index) { 55 | this.state.todos.splice(index,1) 56 | this.setState({todos: this.state.todos}) 57 | } 58 | render(){ 59 | let info = { 60 | isAllChecked: this.state.isAllChecked, 61 | todoCount: this.state.todos.length || 0, 62 | todoDoneCount: (this.state.todos && this.state.todos.filter((todo) => todo.isDone)).length || 0 63 | } 64 | return ( 65 |
    66 | 67 | 68 | 69 |
    70 | ) 71 | } 72 | } 73 | 74 | 75 | ReactDOM.render(,document.getElementById('app')) -------------------------------------------------------------------------------- /todoMvc/todoMvc-2step/src/js/localDb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by YikaJ on 15/6/10. 3 | */ 4 | 'use strict'; 5 | 6 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 7 | 8 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 9 | 10 | var LocalDb = (function () { 11 | function LocalDb() { 12 | var localDb = arguments[0] === undefined ? 'localDb' : arguments[0]; 13 | 14 | _classCallCheck(this, LocalDb); 15 | 16 | if (!window.localStorage) { 17 | throw new Error('Not supports localStorage'); 18 | } 19 | this.localDb = localDb; 20 | 21 | if (localStorage[localDb]) { 22 | this.db = JSON.parse(localStorage[localDb]); 23 | } else { 24 | this.db = {}; 25 | } 26 | } 27 | 28 | _createClass(LocalDb, [{ 29 | key: 'getDb', 30 | value: function getDb() { 31 | return this.db; 32 | } 33 | }, { 34 | key: 'set', 35 | value: function set(key, value) { 36 | if (key) { 37 | this.db[key] = value; 38 | 39 | return this._saveToLocalStorage(); 40 | } 41 | 42 | throw new Error('set参数key不能为空'); 43 | } 44 | }, { 45 | key: 'get', 46 | value: function get(key) { 47 | if (key) { 48 | var value = this.db[key]; 49 | if (typeof value === 'undefined') { 50 | console.warn(key + '的值不存在'); 51 | } 52 | return value; 53 | } 54 | 55 | throw new Error('get参数key不能为空'); 56 | } 57 | }, { 58 | key: 'clean', 59 | value: function clean() { 60 | this.db = {}; 61 | this._saveToLocalStorage(); 62 | } 63 | }, { 64 | key: '_saveToLocalStorage', 65 | value: function _saveToLocalStorage() { 66 | localStorage[this.localDb] = JSON.stringify(this.getDb()); 67 | } 68 | }]); 69 | 70 | return LocalDb; 71 | })(); 72 | 73 | module.exports = LocalDb; 74 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-3step/src/js/localDb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by YikaJ on 15/6/10. 3 | */ 4 | 'use strict'; 5 | 6 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 7 | 8 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 9 | 10 | var LocalDb = (function () { 11 | function LocalDb() { 12 | var localDb = arguments[0] === undefined ? 'localDb' : arguments[0]; 13 | 14 | _classCallCheck(this, LocalDb); 15 | 16 | if (!window.localStorage) { 17 | throw new Error('Not supports localStorage'); 18 | } 19 | this.localDb = localDb; 20 | 21 | if (localStorage[localDb]) { 22 | this.db = JSON.parse(localStorage[localDb]); 23 | } else { 24 | this.db = {}; 25 | } 26 | } 27 | 28 | _createClass(LocalDb, [{ 29 | key: 'getDb', 30 | value: function getDb() { 31 | return this.db; 32 | } 33 | }, { 34 | key: 'set', 35 | value: function set(key, value) { 36 | if (key) { 37 | this.db[key] = value; 38 | 39 | return this._saveToLocalStorage(); 40 | } 41 | 42 | throw new Error('set参数key不能为空'); 43 | } 44 | }, { 45 | key: 'get', 46 | value: function get(key) { 47 | if (key) { 48 | var value = this.db[key]; 49 | if (typeof value === 'undefined') { 50 | console.warn(key + '的值不存在'); 51 | } 52 | return value; 53 | } 54 | 55 | throw new Error('get参数key不能为空'); 56 | } 57 | }, { 58 | key: 'clean', 59 | value: function clean() { 60 | this.db = {}; 61 | this._saveToLocalStorage(); 62 | } 63 | }, { 64 | key: '_saveToLocalStorage', 65 | value: function _saveToLocalStorage() { 66 | localStorage[this.localDb] = JSON.stringify(this.getDb()); 67 | } 68 | }]); 69 | 70 | return LocalDb; 71 | })(); 72 | 73 | module.exports = LocalDb; 74 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-4step/src/js/localDb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by YikaJ on 15/6/10. 3 | */ 4 | 'use strict'; 5 | 6 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 7 | 8 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 9 | 10 | var LocalDb = (function () { 11 | function LocalDb() { 12 | var localDb = arguments[0] === undefined ? 'localDb' : arguments[0]; 13 | 14 | _classCallCheck(this, LocalDb); 15 | 16 | if (!window.localStorage) { 17 | throw new Error('Not supports localStorage'); 18 | } 19 | this.localDb = localDb; 20 | 21 | if (localStorage[localDb]) { 22 | this.db = JSON.parse(localStorage[localDb]); 23 | } else { 24 | this.db = {}; 25 | } 26 | } 27 | 28 | _createClass(LocalDb, [{ 29 | key: 'getDb', 30 | value: function getDb() { 31 | return this.db; 32 | } 33 | }, { 34 | key: 'set', 35 | value: function set(key, value) { 36 | if (key) { 37 | this.db[key] = value; 38 | 39 | return this._saveToLocalStorage(); 40 | } 41 | 42 | throw new Error('set参数key不能为空'); 43 | } 44 | }, { 45 | key: 'get', 46 | value: function get(key) { 47 | if (key) { 48 | var value = this.db[key]; 49 | if (typeof value === 'undefined') { 50 | console.warn(key + '的值不存在'); 51 | } 52 | return value; 53 | } 54 | 55 | throw new Error('get参数key不能为空'); 56 | } 57 | }, { 58 | key: 'clean', 59 | value: function clean() { 60 | this.db = {}; 61 | this._saveToLocalStorage(); 62 | } 63 | }, { 64 | key: '_saveToLocalStorage', 65 | value: function _saveToLocalStorage() { 66 | localStorage[this.localDb] = JSON.stringify(this.getDb()); 67 | } 68 | }]); 69 | 70 | return LocalDb; 71 | })(); 72 | 73 | module.exports = LocalDb; 74 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-5step/src/js/localDb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by YikaJ on 15/6/10. 3 | */ 4 | 'use strict'; 5 | 6 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 7 | 8 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 9 | 10 | var LocalDb = (function () { 11 | function LocalDb() { 12 | var localDb = arguments[0] === undefined ? 'localDb' : arguments[0]; 13 | 14 | _classCallCheck(this, LocalDb); 15 | 16 | if (!window.localStorage) { 17 | throw new Error('Not supports localStorage'); 18 | } 19 | this.localDb = localDb; 20 | 21 | if (localStorage[localDb]) { 22 | this.db = JSON.parse(localStorage[localDb]); 23 | } else { 24 | this.db = {}; 25 | } 26 | } 27 | 28 | _createClass(LocalDb, [{ 29 | key: 'getDb', 30 | value: function getDb() { 31 | return this.db; 32 | } 33 | }, { 34 | key: 'set', 35 | value: function set(key, value) { 36 | if (key) { 37 | this.db[key] = value; 38 | 39 | return this._saveToLocalStorage(); 40 | } 41 | 42 | throw new Error('set参数key不能为空'); 43 | } 44 | }, { 45 | key: 'get', 46 | value: function get(key) { 47 | if (key) { 48 | var value = this.db[key]; 49 | if (typeof value === 'undefined') { 50 | console.warn(key + '的值不存在'); 51 | } 52 | return value; 53 | } 54 | 55 | throw new Error('get参数key不能为空'); 56 | } 57 | }, { 58 | key: 'clean', 59 | value: function clean() { 60 | this.db = {}; 61 | this._saveToLocalStorage(); 62 | } 63 | }, { 64 | key: '_saveToLocalStorage', 65 | value: function _saveToLocalStorage() { 66 | localStorage[this.localDb] = JSON.stringify(this.getDb()); 67 | } 68 | }]); 69 | 70 | return LocalDb; 71 | })(); 72 | 73 | module.exports = LocalDb; 74 | -------------------------------------------------------------------------------- /todoMvc/todoMvc-1step/README.md: -------------------------------------------------------------------------------- 1 | ### 如何用webpack搭建react+stylus项目 2 | 学习必要条件:略懂`node.js`,略懂`ES6`,然后你的电脑**必须**安装有较新版本[node](https://nodejs.org/en/download/),没有的同学赶紧安装。 3 | 好了,废话不多说,直接开始。 4 | 5 | 第一部分源码:[todoMvc-1step](https://github.com/Zegendary/react-demo/tree/master/todoMvc/todoMvc-1step) 6 | #[webpack](https://webpack.github.io/docs/what-is-webpack.html)的配置 7 | 1. 介绍:[Webpack](https://github.com/webpack/webpack) 是当下最热门的前端资源模块化管理和打包工具。详细见官网 8 | 2. 安装: 9 | 10 | $ npm install webpack -g 11 | 12 | 此时 Webpack 已经安装到了全局环境下,可以通过命令行 webpack -h 试试。 13 | 14 | 但通常我们会将 Webpack 以及相关依赖以这种方式安装,如下: 15 | 16 | # 进入项目目录 17 | # 确定已经有 package.json,没有就通过 npm init 创建 18 | # 安装 webpack 依赖 19 | $ npm install webpack --save-dev 20 | # 安装react.js依赖(i是install的简写,-S是--save的简写) 21 | $ npm i react react-dom -S 22 | 23 | 剩余的依赖组件参照我源码中的package.json的依赖添加就好。最终,我们得到的package.json应该如下图: 24 | ![package.age](http://upload-images.jianshu.io/upload_images/1826203-43b19e9bb8aa4c8e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 25 | 确保红框中的内容一样即可。 26 | 27 | 3. 配置 28 | 29 | 现在我们已经安装好了依赖,下面我们需要先把项目的目录建好: 30 | 31 | . 32 | ├── node_modules # npm install 安装的东西都跑着里面来了 33 | ├── src 34 | ├── components 35 | ├── app.js # react组件 36 | ├── styles 37 | ├── main.styl # stylus文件(类似于sass) 38 | ├── entry.js # 入口js文件 39 | ├── index.html # 入口页面 40 | ├── package.json # 项目描述文件(内有相关依赖) 41 | └── webpack.config.js # webpack配置文件 42 | 43 | 然后我们在webpack.config.js中添加配置: 44 | 45 | module.exports = { 46 | entry: [ 47 | "./src/entry.js" 48 | ], 49 | output: { 50 | path: './out/', 51 | filename: "bundle.js" 52 | }, 53 | module: { 54 | loaders: [ 55 | { test: /\.js[x]?$/, loader: "babel-loader?presets[]=es2015&presets[]=react", include: /src/}, 56 | { test: /\.css$/, loader: "style!css"}, 57 | { test: /\.styl$/, loader: "style-loader!css-loader!stylus-loader"}, 58 | { test: /\.(png|jpg)$/, loader: 'url?limit=8192'} 59 | ] 60 | } 61 | } 62 | 63 | 配置文件将我们的入口文件`entry.js`打包输出到 `./out/bundle.js`,我们直接在页面`index.html`中引入`bundle.js`就好了 64 | 65 | 66 | 67 | 不懂得话可以参考webpack的文档:[webpack-usage](https://webpack.github.io/docs/usage.html) 和 [webpack-loader](https://webpack.github.io/docs/using-loaders.html) 68 | 69 | 关于`/src`目录下的文件内容可以直接到源码中查看。 70 | 然后就可以小试牛刀啦,在终端中输入: 71 | 72 | $ webpack 73 | 74 | 然后我们看到我们的目录下多了个`./out/bundle.js`文件,然后我们在浏览器打开目录下的index.html文件可以看到内容并alert('success') 75 | ![todoMvc-step1](http://upload-images.jianshu.io/upload_images/1826203-1ae2afe159ba5f1e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 76 | 77 | 那么恭喜你,第一步圆满完成! 78 | 79 | [React+webpack从0到1开发一个todoMvc(二)](http://www.jianshu.com/p/ed01cf27b23d) 80 | [React+webpack从0到1开发一个todoMvc(三)](http://www.jianshu.com/p/80e54fc179e4) 81 | [React+webpack从0到1开发一个todoMvc(四)](http://www.jianshu.com/p/4b3b2f3146e2) 82 | [React+webpack从0到1开发一个todoMvc(五)](http://www.jianshu.com/p/86b83192917d) -------------------------------------------------------------------------------- /todoMvc/todoMvc-2step/README.md: -------------------------------------------------------------------------------- 1 | #React如何双向绑定 2 | [todoMvc-2step源码](https://github.com/Zegendary/react-demo/tree/master/todoMvc/todoMvc-2step) 3 | [todoMvc-2step演示](https://zegendary.github.io/react-demo/todoMvc/todoMvc-2step/) 4 | 上一章主要说了下react+webpack的环境搭建,这一章主要讲一下如何双向绑定。对vue和angular略有了解的都知道,这两个框架都是支持双向绑定的,而react是单向绑定的,知乎有一篇关于单向绑定和双向绑定可以拓展一下:[单向数据绑定和双向数据绑定的优缺点,适合什么场景?](https://www.zhihu.com/question/49964363) 5 | 6 | ##下面分析如何具体实现 7 | 进入我们的`app.js`文件,在之前我们搭建环境的时候已经安装了react相关的依赖以及babel编译工具,所以我们可以直接在这里使用`ES6`、`JSX`语法。 8 | ####1.引入react核心内容 9 | 10 | import React from 'react' 11 | import ReactDOM from 'react-dom' 12 | 其中,react.js 是 React 的核心库,react-dom.js 是提供与 DOM 相关的功能。 13 | 14 | ####2.生成组件 15 | 先介绍react三个比较重要的知识点: 16 | 1)ReactDOM.render() 17 | ReactDOM.render 是 React 的最基本方法,用于将模板转为 HTML 语言,并插入指定的 DOM 节点。举个例子: 18 | 19 | ReactDOM.render( 20 |

    Hello, world!

    , 21 | document.getElementById('example') 22 | ); 23 | 上面代码将一个 h1 标题,插入 example 节点。 24 | 2)JSX 语法 25 | HTML 语言直接写在 JavaScript 语言之中,不加任何引号,这就是 [JSX 的语法](http://facebook.github.io/react/docs/displaying-data.html#jsx-syntax),它允许 HTML 与 JavaScript 的混写,上面的`

    Hello, world!

    `,就是使用了jsx语法。 26 | 3)组件 27 | React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件。React.createClass 方法就用于生成一个组件类。举个🌰: 28 | 29 | #es5写法 30 | var HelloMessage = React.createClass({ 31 | render: function() { 32 | return

    Hello React

    ; 33 | } 34 | }); 35 | #es6写法 36 | Class HelloMessage extends React.Component { 37 | render() { 38 | return

    Hello, React; 39 | } 40 | } 41 | 当然,这里的`HelloMessage`我们也可以当做HTML标签用`ReactDOM.render()`渲染出来。 42 | 43 | `app.js`: 44 | 45 | class App extends React.Component { //定义组件,继承父类 46 | constructor() {//constructor 是和 class 一起用来创建和初始化对象的特殊方法。 47 | super()//在装载组件(mounting)之前调用会React组件的构造函数。当实现React.Component子类的构造函数时,应该在任何其他语句之前调用super(props) 48 | this.state = {//设置初始状态 49 | todos: [] 50 | } 51 | } 52 | // 绑定键盘回车事件,添加新任务 53 | handlerKeyUp(e) { 54 | if(e.keyCode == 13) { 55 | let value = e.target.value; 56 | if(!value) return false; 57 | let newTodoItem = { 58 | text: value, 59 | isDone: false 60 | }; 61 | e.target.value = ''; 62 | this.state.todos.push(newTodoItem) 63 | this.setState({todos: this.state.todos}); //修改状态值,每次修改以后,自动调用 this.render 方法,再次渲染组件。 64 | } 65 | } 66 | render(){ 67 | return ( 68 |
    69 | 70 |
      71 | {this.state.todos.map((todo,index) => {{ 72 | return ( 73 |
    • {todo.text}
    • //Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity 74 | ) 75 | }})} 76 |
    77 |
    78 | ) 79 | } 80 | } 81 | ReactDOM.render(,document.getElementById('app')) 82 | 83 | ####3.测试 84 | 运行 85 | 86 | $ webpack 87 | 然后打开`index.html`,如果可以在input输入,按下回车可以在下方生成list![成功画面](http://upload-images.jianshu.io/upload_images/1826203-978e6e74113bb79d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 88 | 89 | 那么恭喜你,双向绑定功能圆满完成! 90 | 91 | [React+webpack从0到1开发一个todoMvc(一)](http://www.jianshu.com/p/aa02a10c5b69)  92 | [React+webpack从0到1开发一个todoMvc(三)](http://www.jianshu.com/p/80e54fc179e4)  93 | [React+webpack从0到1开发一个todoMvc(四)](http://www.jianshu.com/p/4b3b2f3146e2)  94 | [React+webpack从0到1开发一个todoMvc(五)](http://www.jianshu.com/p/86b83192917d) -------------------------------------------------------------------------------- /todoMvc/todoMvc-3step/README.md: -------------------------------------------------------------------------------- 1 | [todoMvc-3step源码](https://github.com/Zegendary/react-demo/tree/master/todoMvc/todoMvc-3step) 2 | 3 | [todoMvc-3step演示](https://zegendary.github.io/react-demo/todoMvc/todoMvc-3step/) 4 | 5 | 上一张主要介绍了下React如何进行双向绑定以及如何生成一个组件,我们第三步的目标就是需要把之前做的内容抽象出更细的组件,这样便于解耦,各个组件各司其职,互不干扰。 6 | 7 | 先看下抽象后`src/components`下的目录 8 | 9 | ![组件目录](http://upload-images.jianshu.io/upload_images/1826203-779efaebcebaadb8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 10 | 11 | 这是我们`app.js`修改过后的内容: 12 | 13 | import React from 'react' 14 | import ReactDOM from 'react-dom' 15 | import TodoHeader from './TodoHeader' // 引入TodoHeader组件 16 | import TodoMain from './TodoMain' // 引入TodoMain组件 17 | 18 | class App extends React.Component { // 定义组件,继承父类 19 | constructor() { 20 | super() 21 | this.state = { 22 | todos: [] 23 | } 24 | } 25 | addTodo(item) { // 新增了添加todo事项的方法 26 | this.state.todos.push(item) 27 | this.setState({todos: this.state.todos}); //设置状态 28 | } 29 | render(){ 30 | return ( 31 |
    32 | // 将原内容写在组件中并引入进行渲染 33 | // 把addTodo方法传递到TodoHeader组件中,bind(this)是为了把该React实例绑定到this上 34 | 35 | // 把 state.todos 传入到TodoMain 中 36 | 37 |
    38 | ) 39 | } 40 | } 41 | 42 | ReactDOM.render(,document.getElementById('app')) 43 | 44 | `TodoHeader`: 45 | 46 | import React from 'react' 47 | 48 | class TodoHeader extends React.Component { 49 | // 绑定键盘回车事件,添加新任务 50 | handlerKeyUp(e) { 51 | if(e.keyCode == 13) { // enter键的 keyCode 为13 52 | let value = e.target.value; 53 | if(!value) return false; 54 | let newTodoItem = { 55 | text: value, 56 | isDone: false 57 | }; 58 | e.target.value = ''; 59 | this.props.addTodo(newTodoItem) // 通过 this.props 来调用父组件传递过来的addTodo方法 60 | } 61 | } 62 | render(){ 63 | return ( 64 |
    65 | 66 |
    67 | ) 68 | } 69 | } 70 | export default TodoHeader // 将TodoHeader导出,否则父组件无法导入 71 | `TodoMain`修改后内容: 72 | 73 | import React from 'react' 74 | import TodoItem from './TodoItem' 75 | 76 | class TodoMain extends React.Component { 77 | render(){ 78 | if(this.props.todos.length == 0) { 79 | return ( 80 |
    恭喜您,目前没有待办任务
    81 | ) 82 | } else { 83 | return ( 84 |
      85 | { 86 | this.props.todos.map((todo,index) => { 87 | //{...this.props} 用来传递TodoMain的todos属性和delete、change方法。 88 | return 89 | }) 90 | } 91 |
    92 | ) 93 | } 94 | } 95 | } 96 | export default TodoMain 97 | `TodoItem`: 98 | 99 | import React from 'react' 100 | 101 | class TodoItem extends React.Component { 102 | render() { 103 | let className = this.props.isDone?'task-done':'' 104 | return ( 105 |
  • 106 | 110 |
  • 111 | ) 112 | } 113 | } 114 | 115 | export default TodoItem 116 | `TodoFoot`我们下章在做. 117 | 118 | 这一步时`webpack`先编译,然后打开index.html,如果页面像下图这样的[odoMvc-3step演示](https://zegendary.github.io/react-demo/todoMvc/todoMvc-3step/),那就说明成功了。 119 | 120 | ![第三部截图](http://upload-images.jianshu.io/upload_images/1826203-80312f98bd0efa9a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 121 | 122 | 做到这里应该对react组件组件化的有个大概的了解了。新手们基本可以对着源码按照这种思路继续做下去。以完善【删除】、【清除已完成】、【未完成数量】等功能了,由于代码类似,故不做赘述了,不太清楚的地方可以参考源码。 123 | 124 | [React+webpack从0到1开发一个todoMvc(一)](http://www.jianshu.com/p/aa02a10c5b69) 125 | [React+webpack从0到1开发一个todoMvc(二)](http://www.jianshu.com/p/ed01cf27b23d)  126 | [React+webpack从0到1开发一个todoMvc(四)](http://www.jianshu.com/p/4b3b2f3146e2)  127 | [React+webpack从0到1开发一个todoMvc(五)](http://www.jianshu.com/p/86b83192917d)  -------------------------------------------------------------------------------- /todoMvc/todoMvc-5step/README.md: -------------------------------------------------------------------------------- 1 | [todoMvc-5step源码](https://github.com/Zegendary/react-demo/tree/master/todoMvc/todoMvc-5step) 2 | [todoMvc-5step演示](https://zegendary.github.io/react-demo/todoMvc/todoMvc-5step/) 3 | 4 | 这一章主要将上一章已经成型的TodoMvc增加【注册】、【登陆】、【数据储存】的功能,这里我们把数据保存到[leancloud](https://leancloud.cn/)。 5 | 6 | ### 1.创建 LeanCloud 账户 7 | 你需要去 [https://leancloud.cn](https://leancloud.cn/) 创建一个账户。 8 | 创建成功后,你需要验证你的邮箱,否则无法创建应用。 9 | 10 | ### 2.创建TodoMVC应用 11 | 如下图操作: 12 | ![创建应用](http://upload-images.jianshu.io/upload_images/1826203-dbbd7861bc7d7143.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 13 | 创建成功后就放在那里,因为接下来我们要按照 LeanCloud 的「[JavaScript SDK 文档](https://leancloud.cn/docs/sdk_setup-js.html)」来开发登录、注册功能。 14 | 15 | ### 3.准备HTML页面 16 | 登陆和注册的页面同样也以组件的形式单独抽离出来,样式如图: 17 | 18 | ![登录注册](http://upload-images.jianshu.io/upload_images/1826203-6e7b77b53efa371c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 19 | 组件`Login.js`代码如下: 20 | 21 | import React from 'react' 22 | import { Form, Icon, Input, Button } from 'antd'; 23 | 24 | const FormItem = Form.Item; 25 | 26 | const Login = Form.create()(React.createClass({ 27 | handleSubmit(e) { // 提交操作 28 | e.preventDefault(); 29 | this.props.form.validateFields((err, values) => { 30 | if (!err) { 31 | this.props.loginOrSignUp(values) 32 | } 33 | }); 34 | }, 35 | render() { 36 | const { getFieldDecorator } = this.props.form; 37 | let text = this.props.value == 1 ?'注册':'登陆' // 判断“登陆”或者注册功能 38 | return ( 39 |
    // antdUI的表单 40 | 41 | {getFieldDecorator('userName', { 42 | rules: [{ required: true, message: 'Please input your username!' }], 43 | })( 44 | } placeholder="Username" /> 45 | )} 46 | 47 | 48 | {getFieldDecorator('password', { 49 | rules: [{ required: true, message: 'Please input your Password!' }], // 必须填写项 50 | })( 51 | } type="password" placeholder="Password" /> 52 | )} 53 | 54 | 55 | 58 | 59 |
    60 | ); 61 | }, 62 | })); 63 | export default Login 64 | 在`app.js`中做判断,如果**已登录**,则显示ToDo应用界面,否则显示登陆界面: 65 | 66 | render(){ 67 | if (!this.state.currentUser){ // 判断是否已经登录 68 | const RadioGroup = Radio.Group; 69 | return ( 70 |
    71 |

    React-Todos

    72 | 73 | 注册 74 | 登入 75 | 76 | 77 |
    78 | ) 79 | } else { 80 | let info = { 81 | isAllChecked: this.state.isAllChecked, 82 | todoCount: this.state.todos.length || 0, 83 | todoDoneCount: (this.state.todos && this.state.todos.filter((todo) => todo.isDone)).length || 0 84 | } 85 | return ( 86 |
    87 | 88 | 89 | 90 |
    91 | ) 92 | } 93 | } 94 | 95 | ### 4.注册&登陆 96 | 1.安装 LeanCloud SDK 97 | 98 | [https://leancloud.cn/docs/sdk_setup-js.html](https://leancloud.cn/docs/sdk_setup-js.html) 99 | 100 | $ npm install leancloud-storage --save 101 | 2.初始化 102 | 103 | [https://leancloud.cn/docs/sdk_setup-js.html#初始化](https://leancloud.cn/docs/sdk_setup-js.html#%E5%88%9D%E5%A7%8B%E5%8C%96) 104 | `app.js`: 105 | 106 | import AV from 'leancloud-storage' 107 | 108 | const appId = 'XXXXXXXXXXXXXXXXXXXXXX' //这里的appId就是刚才我们创建的应用的Id,每个人都不一样 109 | const appKey = 'XXXXXXXXXXXXXXXXXXX'; 110 | AV.init({ appId, appKey }); 111 | 3.写入注册登陆的方法 112 | 我们先要通读一下 LeanCloud [关于注册的文档](https://leancloud.cn/docs/leanstorage_guide-js.html#%E7%94%A8%E6%88%B7%E5%90%8D%E5%92%8C%E5%AF%86%E7%A0%81%E6%B3%A8%E5%86%8C),然后按照里面的demo去做修改。 113 | `app.js`: 114 | 115 | //登陆或者注册 116 | loginOrSignUp(values){ 117 | //判断是登陆还是注册 118 | if (this.state.value === 1){ 119 | let user = new AV.User(); 120 | user.setUsername(values.userName); 121 | user.setPassword(values.password); 122 | user.signUp().then((loginedUser) => { 123 | this.state.currentUser = this.getCurrentUser() 124 | this.setState({currentUser: this.state.currentUser}) 125 | }, function (error) { 126 | alert("注册失败") 127 | }) 128 | } else if (this.state.value === 2){ 129 | console.log("执行登陆") 130 | AV.User.logIn(values.userName, values.password).then((loginedUser) => { 131 | this.state.currentUser = this.getCurrentUser() 132 | this.setState({currentUser: this.state.currentUser}) 133 | this.fetchTodos() 134 | }, function (error) { 135 | alert("登陆失败") 136 | }); 137 | } 138 | } 139 | 140 | 下面还需要去做【登出】、【保存Todo】等功能。这里我就不贴出来代码了,可以直接去github上面去看我的[app.js源码](https://github.com/Zegendary/react-demo/blob/master/todoMvc/todoMvc-5step/src/components/app.js)。 141 | 至此,我们React+Webpack+Antd 的一个TodoMVC就开发完毕了。希望能帮助小伙伴。 142 | 143 | [React+webpack从0到1开发一个todoMvc(一)](http://www.jianshu.com/p/aa02a10c5b69) 144 | [React+webpack从0到1开发一个todoMvc(二)](http://www.jianshu.com/p/ed01cf27b23d) 145 | [React+webpack从0到1开发一个todoMvc(三)](http://www.jianshu.com/p/80e54fc179e4) 146 | [React+webpack从0到1开发一个todoMvc(四)](http://www.jianshu.com/p/4b3b2f3146e2)  -------------------------------------------------------------------------------- /todoMvc/todoMvc-5step/src/components/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zhangxinwang on 16/02/2017. 3 | */ 4 | import React from 'react' 5 | import ReactDOM from 'react-dom' 6 | 7 | import TodoHeader from './TodoHeader' 8 | import TodoMain from './TodoMain' 9 | import TodoFooter from './TodoFooter' 10 | import Login from './Login' 11 | import { Radio } from 'antd'; 12 | import AV from 'leancloud-storage' 13 | 14 | const appId = 'VU2Q5GFPX9GzaOOQ0ka4pIOa-gzGzoHsz'; 15 | const appKey = 'lE8CkfPRCgSBApNu3zmKUGht'; 16 | AV.init({ appId, appKey }); 17 | 18 | class App extends React.Component { //定义组件,继承父类 19 | constructor() { 20 | super() 21 | this.state = { 22 | todos: [], 23 | todoId: null, 24 | isAllChecked: false, 25 | currentUser: null, 26 | value: 1, 27 | } 28 | } 29 | //添加todoitem 30 | addTodo(item) { 31 | this.state.todos.push(item) 32 | this.setState({todos: this.state.todos}); //设置状态 33 | } 34 | // 改变任务状态,传递给TodoItem和Footer组件的方法 35 | changeTodoState(index, isDone, isChangeAll=false){ //初始化isChangeAll为false 36 | if(isChangeAll){ //全部操作 37 | this.setState({ 38 | todos: this.state.todos.map((todo) => { 39 | todo.isDone = isDone; 40 | return todo; 41 | }), 42 | isAllChecked: isDone 43 | }); 44 | }else{ //操作其中一个todo 45 | this.state.todos[index].isDone = isDone; 46 | this.allChecked(); 47 | } 48 | } 49 | // 判断是否所有任务的状态都完成,同步底部的全选框 50 | allChecked() { 51 | let isAllChecked = false; 52 | if (this.state.todos.every(todo => todo.isDone)) { 53 | isAllChecked = true; 54 | } 55 | this.setState({ //改变状态,组件重绘 56 | todos: this.state.todos, 57 | isAllChecked: isAllChecked 58 | }); 59 | } 60 | //清除已经完成事件 61 | clearDone(){ 62 | let todos = this.state.todos.filter(todo => !todo.isDone) 63 | this.setState({ 64 | todos : todos 65 | }) 66 | } 67 | //删除某个todoitem 68 | deleteTodo(index) { 69 | this.state.todos.splice(index,1) 70 | this.setState({todos: this.state.todos}) 71 | } 72 | //修改登录或者注册 73 | onChange(e) { 74 | this.setState({ 75 | value: e.target.value, 76 | }); 77 | } 78 | //登陆或者注册 79 | loginOrSignUp(values){ 80 | //判断是登陆还是注册 81 | if (this.state.value === 1){ 82 | let user = new AV.User(); 83 | user.setUsername(values.userName); 84 | user.setPassword(values.password); 85 | user.signUp().then((loginedUser) => { 86 | this.state.currentUser = this.getCurrentUser() 87 | this.setState({currentUser: this.state.currentUser}) 88 | }, function (error) { 89 | alert("注册失败") 90 | }) 91 | } else if (this.state.value === 2){ 92 | console.log("执行登陆") 93 | AV.User.logIn(values.userName, values.password).then((loginedUser) => { 94 | this.state.currentUser = this.getCurrentUser() 95 | this.setState({currentUser: this.state.currentUser}) 96 | this.fetchTodos() 97 | }, function (error) { 98 | alert("登陆失败") 99 | }); 100 | } 101 | } 102 | //获取当前用户 103 | getCurrentUser() { 104 | let current = AV.User.current() 105 | if (current){ 106 | let {id, createdAt, attributes: {username}} = current 107 | return {id, username, createdAt} 108 | } else{ 109 | return null 110 | } 111 | } 112 | //登出用户 113 | logout() { 114 | AV.User.logOut() 115 | this.state.currentUser = null 116 | window.location.reload() 117 | } 118 | //将todo储存到服务器上 119 | saveTodos() { 120 | let dataString = JSON.stringify(this.state.todos) 121 | var AVTodos = AV.Object.extend('AllTodos'); 122 | var avTodos = new AVTodos(); 123 | // 新建一个 ACL 实例 124 | var acl = new AV.ACL(); 125 | acl.setReadAccess(AV.User.current(),true) 126 | acl.setWriteAccess(AV.User.current(),true) 127 | 128 | avTodos.setACL(acl) // 设置访问控制 129 | 130 | avTodos.set('content', dataString); 131 | avTodos.save().then((todo) => { 132 | this.state.todoId = todo.id 133 | this.setState({todoId:this.state.todoId}) 134 | console.log('保存成功'); 135 | }, function (error) { 136 | // 异常处理 137 | console.log('保存失败'); 138 | }); 139 | } 140 | //更新todo到服务器上 141 | updateTodos() { 142 | let dataString = JSON.stringify(this.state.todos) 143 | let avTodos = AV.Object.createWithoutData('AllTodos', this.state.todoId) 144 | avTodos.set('content', dataString) 145 | avTodos.save().then(()=>{ 146 | console.log('更新成功') 147 | }) 148 | } 149 | //判断应该更新或储存list到leanCloud 150 | saveOrUpdateTodos() { 151 | if(this.state.todoId){ 152 | this.updateTodos() 153 | } else { 154 | this.saveTodos() 155 | } 156 | } 157 | componentWillMount() { 158 | this.state.currentUser = this.getCurrentUser() 159 | this.fetchTodos() 160 | this.setState({currentUser: this.state.currentUser}) 161 | } 162 | fetchTodos() { 163 | if(this.state.currentUser){ 164 | var query = new AV.Query('AllTodos'); 165 | query.find() 166 | .then((todoList) => { 167 | let avAllTodos = todoList[0]// 因为理论上 AllTodos 只有一个,所以我们取结果的第一项 168 | let id = avAllTodos.id 169 | this.state.todos = JSON.parse(avAllTodos.attributes.content) 170 | this.state.todoId = id 171 | this.setState({todos:this.state.todos,todoId:this.state.todoId}) 172 | }, function(error){ 173 | console.error(error) 174 | }) 175 | } 176 | } 177 | render(){ 178 | if (!this.state.currentUser){ 179 | const RadioGroup = Radio.Group; 180 | return ( 181 |
    182 |

    React-Todos

    183 | 184 | 注册 185 | 登入 186 | 187 | 188 |
    189 | ) 190 | } else { 191 | let info = { 192 | isAllChecked: this.state.isAllChecked, 193 | todoCount: this.state.todos.length || 0, 194 | todoDoneCount: (this.state.todos && this.state.todos.filter((todo) => todo.isDone)).length || 0 195 | } 196 | return ( 197 |
    198 | 199 | 200 | 201 |
    202 | ) 203 | } 204 | } 205 | } 206 | 207 | 208 | ReactDOM.render(,document.getElementById('app')) -------------------------------------------------------------------------------- /todoMvc/todoMvc-1step/out/bundle.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) 10 | /******/ return installedModules[moduleId].exports; 11 | 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ i: moduleId, 15 | /******/ l: false, 16 | /******/ exports: {} 17 | /******/ }; 18 | 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | 22 | /******/ // Flag the module as loaded 23 | /******/ module.l = true; 24 | 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | 29 | 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | 36 | /******/ // identity function for calling harmony imports with the correct context 37 | /******/ __webpack_require__.i = function(value) { return value; }; 38 | 39 | /******/ // define getter function for harmony exports 40 | /******/ __webpack_require__.d = function(exports, name, getter) { 41 | /******/ if(!__webpack_require__.o(exports, name)) { 42 | /******/ Object.defineProperty(exports, name, { 43 | /******/ configurable: false, 44 | /******/ enumerable: true, 45 | /******/ get: getter 46 | /******/ }); 47 | /******/ } 48 | /******/ }; 49 | 50 | /******/ // getDefaultExport function for compatibility with non-harmony modules 51 | /******/ __webpack_require__.n = function(module) { 52 | /******/ var getter = module && module.__esModule ? 53 | /******/ function getDefault() { return module['default']; } : 54 | /******/ function getModuleExports() { return module; }; 55 | /******/ __webpack_require__.d(getter, 'a', getter); 56 | /******/ return getter; 57 | /******/ }; 58 | 59 | /******/ // Object.prototype.hasOwnProperty.call 60 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 61 | 62 | /******/ // __webpack_public_path__ 63 | /******/ __webpack_require__.p = ""; 64 | 65 | /******/ // Load entry module and return exports 66 | /******/ return __webpack_require__(__webpack_require__.s = 6); 67 | /******/ }) 68 | /************************************************************************/ 69 | /******/ ([ 70 | /* 0 */ 71 | /***/ (function(module, exports, __webpack_require__) { 72 | 73 | "use strict"; 74 | 75 | 76 | __webpack_require__(1); 77 | __webpack_require__(5); 78 | 79 | /***/ }), 80 | /* 1 */ 81 | /***/ (function(module, exports, __webpack_require__) { 82 | 83 | "use strict"; 84 | 85 | 86 | /** 87 | * Created by zhangxinwang on 16/02/2017. 88 | */ 89 | alert('success'); 90 | 91 | /***/ }), 92 | /* 2 */ 93 | /***/ (function(module, exports, __webpack_require__) { 94 | 95 | exports = module.exports = __webpack_require__(3)(); 96 | exports.push([module.i, ".todo-title {\n color: #f00;\n}\n", ""]); 97 | 98 | /***/ }), 99 | /* 3 */ 100 | /***/ (function(module, exports) { 101 | 102 | /* 103 | MIT License http://www.opensource.org/licenses/mit-license.php 104 | Author Tobias Koppers @sokra 105 | */ 106 | // css base code, injected by the css-loader 107 | module.exports = function() { 108 | var list = []; 109 | 110 | // return the list of modules as css string 111 | list.toString = function toString() { 112 | var result = []; 113 | for(var i = 0; i < this.length; i++) { 114 | var item = this[i]; 115 | if(item[2]) { 116 | result.push("@media " + item[2] + "{" + item[1] + "}"); 117 | } else { 118 | result.push(item[1]); 119 | } 120 | } 121 | return result.join(""); 122 | }; 123 | 124 | // import a list of modules into the list 125 | list.i = function(modules, mediaQuery) { 126 | if(typeof modules === "string") 127 | modules = [[null, modules, ""]]; 128 | var alreadyImportedModules = {}; 129 | for(var i = 0; i < this.length; i++) { 130 | var id = this[i][0]; 131 | if(typeof id === "number") 132 | alreadyImportedModules[id] = true; 133 | } 134 | for(i = 0; i < modules.length; i++) { 135 | var item = modules[i]; 136 | // skip already imported module 137 | // this implementation is not 100% perfect for weird media query combinations 138 | // when a module is imported multiple times with different media queries. 139 | // I hope this will never occur (Hey this way we have smaller bundles) 140 | if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) { 141 | if(mediaQuery && !item[2]) { 142 | item[2] = mediaQuery; 143 | } else if(mediaQuery) { 144 | item[2] = "(" + item[2] + ") and (" + mediaQuery + ")"; 145 | } 146 | list.push(item); 147 | } 148 | } 149 | }; 150 | return list; 151 | }; 152 | 153 | 154 | /***/ }), 155 | /* 4 */ 156 | /***/ (function(module, exports) { 157 | 158 | /* 159 | MIT License http://www.opensource.org/licenses/mit-license.php 160 | Author Tobias Koppers @sokra 161 | */ 162 | var stylesInDom = {}, 163 | memoize = function(fn) { 164 | var memo; 165 | return function () { 166 | if (typeof memo === "undefined") memo = fn.apply(this, arguments); 167 | return memo; 168 | }; 169 | }, 170 | isOldIE = memoize(function() { 171 | return /msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase()); 172 | }), 173 | getHeadElement = memoize(function () { 174 | return document.head || document.getElementsByTagName("head")[0]; 175 | }), 176 | singletonElement = null, 177 | singletonCounter = 0; 178 | 179 | module.exports = function(list, options) { 180 | if(typeof DEBUG !== "undefined" && DEBUG) { 181 | if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment"); 182 | } 183 | 184 | options = options || {}; 185 | // Force single-tag solution on IE6-9, which has a hard limit on the # of