React 应用(基于 React 脚手架)

一、使用create-react-app创建react应用

(一)react脚手架

  1. xxx 脚手架: 用来帮助程序员快速创建一个基于 xxx 库的模板项目
    • 包含了所有需要的配置(语法检查、jsx 编译、devServer…)
    • 下载好了所有相关的依赖
    • 可以直接运行一个简单效果
  2. react 提供了一个用于创建react项目的脚手架库: create-react-app
  3. 项目的整体技术架构为: react + webpack + es6 + eslint
  4. 使用脚手架开发的项目的特点: 模块化, 组件化, 工程化

(二)创建项目并启动

  • 第一步,全局安装:npm i -g create-react-app
  • 第二步,切换到想创项目的目录,使用命令:create-react-app hello-react
  • 第三步,进入项目文件夹:cd hello-react
  • 第四步,启动项目:npm start

(三)react脚手架项目结构

image.png
image.png

image.png
image.png

image.png

(四)功能界面的组件化编码流程(通用)

    1. 拆分组件: 拆分界面,抽取组件
    2. 实现静态组件: 使用组件实现静态页面效果
    3. 实现动态组件
        动态显示初始化数据
            数据类型
            数据名称
            保存在哪个组件?
        交互(从绑定事件监听开始)

二、组件的组合使用-TodoList

(一)案例效果

screenshots.gif

(二)目录结构

image.png

(三)拆分组件

image.png

(四)代码实现

App组件

  • App.jsx
import React, {
    
     Component } from 'react'
import Header from './components/Header'
import List from './components/List'
import Footer from './components/Footer'
import './App.css'

export default class App extends Component {
    
    
	//状态在哪里,操作状态的方法就在哪里

	//初始化状态
	state = {
    
    todos:[
		{
    
    id:'001',name:'吃饭',done:true},
		{
    
    id:'002',name:'睡觉',done:true},
		{
    
    id:'003',name:'打代码',done:false},
		{
    
    id:'004',name:'逛街',done:false}
	]}

	//addTodo用于添加一个todo,接收的参数是todo对象
	addTodo = (todoObj)=>{
    
    
		//获取原todos
		const {
    
    todos} = this.state
		//追加一个todo
		const newTodos = [todoObj,...todos]
		//更新状态
		this.setState({
    
    todos:newTodos})
	}

	//updateTodo用于更新一个todo对象
	updateTodo = (id,done)=>{
    
    
		//获取状态中的todos
		const {
    
    todos} = this.state
		//匹配处理数据
		const newTodos = todos.map((todoObj)=>{
    
    
			if(todoObj.id === id) return {
    
    ...todoObj,done}
			else return todoObj
		})
		this.setState({
    
    todos:newTodos})
	}

	//deleteTodo用于删除一个todo对象
	deleteTodo = (id)=>{
    
    
		//获取原来的todos
		const {
    
    todos} = this.state
		//删除指定id的todo对象
		const newTodos = todos.filter((todoObj)=>{
    
    
			return todoObj.id !== id
		})
		//更新状态
		this.setState({
    
    todos:newTodos})
	}

	//checkAllTodo用于全选
	checkAllTodo = (done)=>{
    
    
		//获取原来的todos
		const {
    
    todos} = this.state
		//加工数据
		const newTodos = todos.map((todoObj)=>{
    
    
			return {
    
    ...todoObj,done}
		})
		//更新状态
		this.setState({
    
    todos:newTodos})
	}

	//clearAllDone用于清除所有已完成的
	clearAllDone = ()=>{
    
    
		//获取原来的todos
		const {
    
    todos} = this.state
		//过滤数据
		const newTodos = todos.filter((todoObj)=>{
    
    
			return !todoObj.done
		})
		//更新状态
		this.setState({
    
    todos:newTodos})
	}

	render() {
    
    
		const {
    
    todos} = this.state
		return (
			<div className="todo-container">
				<div className="todo-wrap">
					<Header addTodo={
    
    this.addTodo}/>
					<List todos={
    
    todos} updateTodo={
    
    this.updateTodo} deleteTodo={
    
    this.deleteTodo}/>
					<Footer todos={
    
    todos} checkAllTodo={
    
    this.checkAllTodo} clearAllDone={
    
    this.clearAllDone}/>
				</div>
			</div>
		)
	}
}
  • App.css
    /*base*/
    body {
    
    
      background: #fff;
    }

    .btn {
    
    
      display: inline-block;
      padding: 4px 12px;
      margin-bottom: 0;
      font-size: 14px;
      line-height: 20px;
      text-align: center;
      vertical-align: middle;
      cursor: pointer;
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
      border-radius: 4px;
    }

    .btn-danger {
    
    
      color: #fff;
      background-color: #da4f49;
      border: 1px solid #bd362f;
    }

    .btn-danger:hover {
    
    
      color: #fff;
      background-color: #bd362f;
    }

    .btn:focus {
    
    
      outline: none;
    }

    .todo-container {
    
    
      margin: 20px 0;
      width: 600px;
      margin: 0 auto;
    }
    .todo-container .todo-wrap {
    
    
      padding: 10px;
      border: 1px solid #ddd;
      border-radius: 5px;
    }

Header组件

  • index.jsx
import React, {
    
     Component } from 'react'
import PropTypes from 'prop-types'
import {
    
    nanoid} from 'nanoid'
import './index.css'

export default class Header extends Component {
    
    

	//对接收的props进行:类型、必要性的限制
	static propTypes = {
    
    
		addTodo:PropTypes.func.isRequired
	}

	//键盘事件的回调
	handleKeyUp = (event)=>{
    
    
		//解构赋值获取keyCode,target
		const {
    
    keyCode,target} = event
		//判断是否是回车按键
		if(keyCode !== 13) return
		//添加的todo名字不能为空
		if(target.value.trim() === ''){
    
    
			alert('输入不能为空')
			return
		}
		//准备好一个todo对象
		const todoObj = {
    
    id:nanoid(),name:target.value,done:false}
		//将todoObj传递给App
		this.props.addTodo(todoObj)
		//清空输入
		target.value = ''
	}

	render() {
    
    
		return (
			<div className="todo-header">
				<input onKeyUp={
    
    this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认"/>
			</div>
		)
	}
}
  • index.css
/*header*/
.todo-header input {
    
    
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}

.todo-header input:focus {
    
    
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}

List组件

  • index.jsx
import React, {
    
     Component } from 'react'
import PropTypes from 'prop-types'
import Item from '../Item'
import './index.css'

export default class List extends Component {
    
    

	//对接收的props进行:类型、必要性的限制
	static propTypes = {
    
    
		todos:PropTypes.array.isRequired,
		updateTodo:PropTypes.func.isRequired,
		deleteTodo:PropTypes.func.isRequired,
	}

	render() {
    
    
		const {
    
    todos,updateTodo,deleteTodo} = this.props
		return (
			<ul className="todo-main">
				{
    
    
					todos.map( todo =>{
    
    
						return <Item key={
    
    todo.id} {
    
    ...todo} updateTodo={
    
    updateTodo} deleteTodo={
    
    deleteTodo}/>
					})
				}
			</ul>
		)
	}
}
  • index.css
/*main*/
.todo-main {
    
    
  margin-left: 0px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}

.todo-empty {
    
    
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}

Item组件

  • index.jsx
import React, {
    
     Component } from 'react'
import './index.css'

export default class Item extends Component {
    
    

	state = {
    
    mouse:false} //标识鼠标移入、移出

	//鼠标移入、移出的回调
	handleMouse = (flag)=>{
    
    
		return ()=>{
    
    
			this.setState({
    
    mouse:flag})
		}
	}

	//勾选、取消勾选某一个todo的回调
	handleCheck = (id)=>{
    
    
		return (event)=>{
    
    
			this.props.updateTodo(id,event.target.checked)
		}
	}

	//删除一个todo的回调
	handleDelete = (id)=>{
    
    
		if(window.confirm('确定删除吗?')){
    
    
			this.props.deleteTodo(id)
		}
	}


	render() {
    
    
		const {
    
    id,name,done} = this.props
		const {
    
    mouse} = this.state
		return (
			<li style={
    
    {
    
    backgroundColor:mouse ? '#ddd' : 'white'}} onMouseEnter={
    
    this.handleMouse(true)} onMouseLeave={
    
    this.handleMouse(false)}>
				<label>
					<input type="checkbox" checked={
    
    done} onChange={
    
    this.handleCheck(id)}/>
					<span>{
    
    name}</span>
				</label>
				<button onClick={
    
    ()=> this.handleDelete(id) } className="btn btn-danger" style={
    
    {
    
    display:mouse?'block':'none'}}>删除</button>
			</li>
		)
	}
}
  • index.css
/*item*/
li {
    
    
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
}

li label {
    
    
  cursor: pointer;
}

li label li input {
    
    
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
}

li button {
    
    
  float: right;
  display: none;
  margin-top: 3px;
}

li:before {
    
    
  content: initial;
}

li:last-child {
    
    
  border-bottom: none;
}

Footer组件

  • index.jsx
import React, {
    
     Component } from 'react'
import './index.css'

export default class Footer extends Component {
    
    

	//全选checkbox的回调
	handleCheckAll = (event)=>{
    
    
		this.props.checkAllTodo(event.target.checked)
	}

	//清除已完成任务的回调
	handleClearAllDone = ()=>{
    
    
		this.props.clearAllDone()
	}

	render() {
    
    
		const {
    
    todos} = this.props
		//已完成的个数
		const doneCount = todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0),0)
		//总数
		const total = todos.length
		return (
			<div className="todo-footer">
				<label>
					<input type="checkbox" onChange={
    
    this.handleCheckAll} checked={
    
    doneCount === total && total !== 0 ? true : false}/>
				</label>
				<span>
					<span>已完成{
    
    doneCount}</span> / 全部{
    
    total}
				</span>
				<button onClick={
    
    this.handleClearAllDone} className="btn btn-danger">清除已完成任务</button>
			</div>
		)
	}
}

  • index.css
/*footer*/
.todo-footer {
    
    
  height: 40px;
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}

.todo-footer label {
    
    
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}

.todo-footer label input {
    
    
  position: relative;
  top: -1px;
  vertical-align: middle;
  margin-right: 5px;
}

.todo-footer button {
    
    
  float: right;
  margin-top: 5px;
}

(五)todoList案例相关知识点

  1.拆分组件、实现静态组件,注意:className、style的写法
  2.动态初始化列表,如何确定将数据放在哪个组件的state中?
     ——某个组件使用:放在其自身的state中
     ——某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
  3.关于父子之间通信:
    1.【父组件】给【子组件】传递数据:通过props传递
    2.【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
  4.注意defaultChecked 和 checked的区别,类似的还有:defaultValue 和 value
  5.状态在哪里,操作状态的方法就在哪里

猜你喜欢

转载自blog.csdn.net/m0_58190023/article/details/129768757
今日推荐