一、 使用create-react-app创建react应用
1.1. react脚手架
- xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目
- 包含了所有需要的配置(语法检查、jsx编译、devServer…)
- 下载好了所有相关的依赖
- 可以直接运行一个简单效果
- react提供了一个用于创建react项目的脚手架库: create-react-app
- 项目的整体技术架构为: react + webpack + es6 + eslint
- 使用脚手架开发的项目的特点: 模块化, 组件化, 工程化
1.2. 创建项目并启动
第一步,全局安装:npm i -g create-react-app
第二步,切换到想创项目的目录,使用命令:create-react-app hello-react
第三步,进入项目文件夹:cd hello-react
第四步,启动项目:npm start
1.3. react脚手架项目结构
public ---- 静态资源文件夹
favicon.icon ------ 网站页签图标
index.html -------- 主页面
logo192.png ------- logo图
logo512.png ------- logo图
manifest.json ----- 应用加壳的配置文件
robots.txt -------- 爬虫协议文件
src ---- 源码文件夹
App.css -------- App组件的样式
App.js --------- App组件
App.test.js ---- 用于给App做测试
index.css ------ 样式
index.js ------- 入口文件
logo.svg ------- logo图
reportWebVitals.js --- 页面性能分析文件(需要web-vitals库的支持)
setupTests.js ---- 组件单元测试的文件(需要jest-dom库的支持)
1.4. 功能界面的组件化编码流程(通用)
1. 拆分组件: 拆分界面,抽取组件
2. 实现静态组件: 使用组件实现静态页面效果
3. 实现动态组件
3.1 动态显示初始化数据
3.1.1 数据类型
3.1.2 数据名称
3.1.2 保存在哪个组件
3.2 交互(从绑定事件监听开始)
二、案例
1. TodoList分析
1.拆分组件、实现静态组件,注意:className、style的写法
2.动态初始化列表,如何确定将数据放在哪个组件的state中?
——某个组件使用:放在其自身的state中
——某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
3.关于父子之间通信:
1.【父组件】给【子组件】传递数据:通过props传递
2.【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
4.注意defaultChecked 和 checked的区别,类似的还有:defaultValue 和 value, checked必须要写onChange方法配合使用
5.状态在哪里,操作状态的方法就在哪里
![](https://img2020.cnblogs.com/blog/1217233/202104/1217233-20210421202110564-1180896923.png)
2. 代码
(1)App.jsx
1 import React, { Component } from 'react' 2 import Header from './components/Header' 3 import List from './components/List' 4 import Footer from './components/Footer' 5 import './App.css' 6 7 export default class App extends Component { 8 //状态在哪里,操作状态的方法就在哪里 9 10 //初始化状态 11 state = {todos:[ 12 {id:'001',name:'吃饭',done:true}, 13 {id:'002',name:'睡觉',done:true}, 14 {id:'003',name:'打代码',done:false}, 15 {id:'004',name:'逛街',done:false} 16 ]} 17 18 //addTodo用于添加一个todo,接收的参数是todo对象 19 addTodo = (todoObj)=>{ 20 //获取原todos 21 const {todos} = this.state 22 //追加一个todo 23 const newTodos = [todoObj,...todos] 24 //更新状态 25 this.setState({todos:newTodos}) 26 } 27 28 //updateTodo用于更新一个todo对象 29 updateTodo = (id,done)=>{ 30 //获取状态中的todos 31 const {todos} = this.state 32 //匹配处理数据 33 const newTodos = todos.map((todoObj)=>{ 34 if(todoObj.id === id) return {...todoObj,done} 35 else return todoObj 36 }) 37 this.setState({todos:newTodos}) 38 } 39 40 //deleteTodo用于删除一个todo对象 41 deleteTodo = (id)=>{ 42 //获取原来的todos 43 const {todos} = this.state 44 //删除指定id的todo对象 45 const newTodos = todos.filter((todoObj)=>{ 46 return todoObj.id !== id 47 }) 48 //更新状态 49 this.setState({todos:newTodos}) 50 } 51 52 //checkAllTodo用于全选 53 checkAllTodo = (done)=>{ 54 //获取原来的todos 55 const {todos} = this.state 56 //加工数据 57 const newTodos = todos.map((todoObj)=>{ 58 return {...todoObj,done} 59 }) 60 //更新状态 61 this.setState({todos:newTodos}) 62 } 63 64 //clearAllDone用于清除所有已完成的 65 clearAllDone = ()=>{ 66 //获取原来的todos 67 const {todos} = this.state 68 //过滤数据 69 const newTodos = todos.filter((todoObj)=>{ 70 return !todoObj.done 71 }) 72 //更新状态 73 this.setState({todos:newTodos}) 74 } 75 76 render() { 77 const {todos} = this.state 78 return ( 79 <div className="todo-container"> 80 <div className="todo-wrap"> 81 <Header addTodo={this.addTodo}/> 82 <List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo}/> 83 <Footer todos={todos} checkAllTodo={this.checkAllTodo} clearAllDone={this.clearAllDone}/> 84 </div> 85 </div> 86 ) 87 } 88 }
(2) Header.jsx
1 import React, { Component } from 'react' 2 import PropTypes from 'prop-types' 3 import {nanoid} from 'nanoid' 4 import './index.css' 5 6 export default class Header extends Component { 7 8 //对接收的props进行:类型、必要性的限制 9 static propTypes = { 10 addTodo:PropTypes.func.isRequired 11 } 12 13 //键盘事件的回调 14 handleKeyUp = (event)=>{ 15 //解构赋值获取keyCode,target 16 const {keyCode,target} = event 17 //判断是否是回车按键 18 if(keyCode !== 13) return 19 //添加的todo名字不能为空 20 if(target.value.trim() === ''){ 21 alert('输入不能为空') 22 return 23 } 24 //准备好一个todo对象 25 const todoObj = {id:nanoid(),name:target.value,done:false} 26 //将todoObj传递给App 27 this.props.addTodo(todoObj) 28 //清空输入 29 target.value = '' 30 } 31 32 render() { 33 return ( 34 <div className="todo-header"> 35 <input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认"/> 36 </div> 37 ) 38 } 39 }
(3) List.jsx
1 import React, { Component } from 'react' 2 import PropTypes from 'prop-types' 3 import Item from '../Item' 4 import './index.css' 5 6 export default class List extends Component { 7 8 //对接收的props进行:类型、必要性的限制 9 static propTypes = { 10 todos:PropTypes.array.isRequired, 11 updateTodo:PropTypes.func.isRequired, 12 deleteTodo:PropTypes.func.isRequired, 13 } 14 15 render() { 16 const {todos,updateTodo,deleteTodo} = this.props 17 return ( 18 <ul className="todo-main"> 19 { 20 todos.map( todo =>{ 21 return <Item key={todo.id} {...todo} updateTodo={updateTodo} deleteTodo={deleteTodo}/> 22 }) 23 } 24 </ul> 25 ) 26 } 27 }
(4) Item.jsx
1 import React, { Component } from 'react' 2 import './index.css' 3 4 export default class Item extends Component { 5 6 state = {mouse:false} //标识鼠标移入、移出 7 8 //鼠标移入、移出的回调 9 handleMouse = (flag)=>{ 10 return ()=>{ 11 this.setState({mouse:flag}) 12 } 13 } 14 15 //勾选、取消勾选某一个todo的回调 16 handleCheck = (id)=>{ 17 return (event)=>{ 18 this.props.updateTodo(id,event.target.checked) 19 } 20 } 21 22 //删除一个todo的回调 23 handleDelete = (id)=>{ 24 if(window.confirm('确定删除吗?')){ 25 this.props.deleteTodo(id) 26 } 27 } 28 29 30 render() { 31 const {id,name,done} = this.props 32 const {mouse} = this.state 33 return ( 34 <li style={{backgroundColor:mouse ? '#ddd' : 'white'}} onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)}> 35 <label> 36 <input type="checkbox" checked={done} onChange={this.handleCheck(id)}/> 37 <span>{name}</span> 38 </label> 39 <button onClick={()=> this.handleDelete(id) } className="btn btn-danger" style={{display:mouse?'block':'none'}}>删除</button> 40 </li> 41 ) 42 } 43 }
3. 效果