React应用(基于React脚手架)

109 阅读6分钟

React应用(基于React脚手架)

目录

[TOC]

React应用(基于React脚手架)

react脚手架
  1. xxx脚手架:用来帮助程序员快速创建一个基于xxx库的模板项目

    1. 包含了所有需要的配置(语法检查、jsx编译、devServer
    2. 下载好了所有相关的依赖
    3. 可以直接运行一个简单效果
  2. react提供了一个用于创建react项目的脚手架库: create-react-app

  3. 项目的整体技术架构为:  react + webpack + es6 + eslint

  4. 使用脚手架开发的项目的特点:模块化,组件化,工程化

创建项目并启动

命令

  • npm start运行项目

  • npm run build 打包

  • npm test检测

  • npm run eject展示webpack配置文件(不可以回退)

  • 第一步 ,全局安装:npm i -g create-react-app

  • 第二步 ,切换到想创项目的目录,使用命令:create-react-app hello-react

  • 第三步 ,进入项目文件夹:cd hello-react

  • 第四步 ,启动项目:npm start

react脚手架项目结构

html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <!-- %PUBLIC_URL% 代表 public 文件夹的路径-->
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /><!-- 开启理想视口,用于移动端页面适配 -->
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <!-- 用于适配浏览器也签+地址栏颜色(仅支持安卓手机浏览器) -->
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <!-- 用于指定网页添加到主屏后的图标 -->
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <title>React App</title>
  </head>
  <body>
    <!-- 若浏览器不支持js则展示该内容 -->
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <!-- 容器 -->
    <div id="root"></div>
 
  </body>
</html>

index.js

// 核心库
import React from 'react';
// 操作dom
import ReactDOM from 'react-dom';
// 样式
import './index.css';
// app组件
import App from './App';
//记录页面性能
import reportWebVitals from './reportWebVitals';
 
ReactDOM.render(
  // React.StrictMode 检测语法
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
 
reportWebVitals();

app.js

  • //注意 Component 此处不是解构赋值 react内部 采用了多种暴露方式 默认爆露react
  • // export default react   export Component
export 跟export default 的区别
  1. .export与export default均可用于导出常量、函数、文件、模块等
  2. .在一个文件或模块中,export、import可以有多个,export default仅有一个
  3. 通过export方式导出,在导入时要加{ },export default则不需要
  4. 输出单个值,使用export default
  5. 输出多个值,使用export
  6. export default与普通的export不要同时使用
//创建外壳组件
//注意 Component 此处不是解构赋值 react内部 采用了多种暴露方式 默认爆露react 
// export default react   export Component
import React,{Component} from "react";
// 创建并暴露app
export default class App extends Component{
    render(){
        return(
            <div>
                hello react
            </div>
        )
    }
}
 
 
样式模块化
  • 给css文件添加module关键字 index.module.css
  • import hello from ./index.module.css 方式引入
  • 使用 className={hello.title}

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

1.拆分组件:拆分界面,抽取组件

2.实现静态组件:使用组件实现静态页面效果

3.实现动态组件

3.1动态显示初始化数据

3.1.1数据类型

3.1.2数据名称

3.1.2保存在哪个组件?

3.2交互(从绑定事件监听开始)

组件的组合使用-TodoList案例

效果

功能 : 组件化实现此功能

1. 显示所有 todo 列表

2. 输入文本 , 点击按钮显示到列表的首位 , 并清除输入的文本

组件拆分
主页面app.jsx
  • 状态在哪里,操作状态的方法就在哪里

  • 获取原数据 处理数据 更改状态

  • { ...item, done }  复制一个todos对象修改选中状态

  • 1.【父组件】给【子组件】传递数据:通过props传递

  • 2.【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数

import React, { Component } from 'react'
import './App.css'
import Header from './components/Header/index'
import List from './components/List'
import Footer from './components/Footer'
export default class App extends Component {
	//状态在哪里,操作状态的方法就在哪里
	render() {
		const { todos } = this.state
		return (
			<div className="todo-container">
				<div className="todo-wrap">
					<Header addTodo={this.addTodo} />
					<List todos={todos} updateTodos={this.updateTodos} deleteTodo={this.deleteTodo} />
					<Footer todos={todos} checkAllTodo={this.checkAllTodo} clearDone={this.clearDone} />
				</div>
			</div>
		)
	}
	//初始化状态
	state = {
		todos: [
			{ id: "001", name: '吃饭', done: true },
			{ id: "002", name: '睡觉', done: true },
			{ id: "003", name: '打代码', done: false },
		]
	}
	//addTod 用于添加一个 todos ,接收的参数是一个对象
	addTodo = (data) => {
		//获取原数据
		const { todos } = this.state
		//追加一个 todos
		const newTodos = [...todos, data]
		//更新状态
		this.setState({ todos: newTodos })
	}
 
	//用于更新一个 todo 对象
	updateTodos = (id, done) => {
		//获取原来状态
		const { todos } = this.state
		//处理数据
		const newTodos = todos.map((item) => {
			//复制一个todos对象修改选中状态
			if (item.id == id) return { ...item, done }
			else return item
		})
		//更新状态
		this.setState({ todos: newTodos })
	}
	//用于删除一个 todo 对象
	deleteTodo = (id) => {
		//获取原来状态
		const { todos } = this.state
		//删除指定id的todo
		const newTodos = todos.filter((item) => {
			return item.id != id
		})
		//更新状态
		this.setState({ todos: newTodos })
	}
 
	//用于全选
	checkAllTodo = (done) => {
		//获取原来状态
		const { todos } = this.state
		//处理数据
		const newTodos = todos.map((item) => {
			//复制一个todos对象修改选中状态
			return { ...item, done }
		})
		//更新状态
		this.setState({ todos: newTodos })
	}
	//用于所有已完成的
	clearDone = () => {
		//获取原来状态
		const { todos } = this.state
		//删除指定id的todo
		const newTodos = todos.filter((item) => {
			return item.done = false
		})
		//更新状态
		this.setState({ todos: newTodos })
	}
}
Header 组件

import { nanoid } from 'nanoid'生成唯一id的插件

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { nanoid } from 'nanoid'
import './index.css'
export default class Header extends Component {
    render() {
        return (
            <div className="todo-header">
                <input onKeyUp={this.handleUp} type="text" placeholder="请输入你的任务名称,按回车键确认" />
            </div>
        )
    }
    //,对接收的props进行类型,必要性的限制
    static propTypes = {
        addTodo: PropTypes.func.isRequired
    }
 
    //键盘事件的回调
    handleUp = (e) => {
        //解构赋值获取 keyCode, target 
        const { keyCode, target } = e
        //判断是否是回车
        if (keyCode !== 13) return
        //添加的todo名字不能为空
        if (target.value.trim() === '') {
            alert("输入不能为空")
            return
        }
        // 准备好一个todo对象 nanoid生成唯一值插件
        const obj = { id: nanoid(), name: target.value, done: false }
        //将 对象传递给app
        this.props.addTodo(obj)
 
    }
}
Item组件

切记事件方法必须传入一个函数,如果传值应该写高阶函数,避免直接调用

defaultChecked、defaultValue 只在初始渲染时由状态控制,之后更新不再跟状态有关系,而checked、value在全过程中都受状态控制

import React, { Component } from 'react'
import './index.css'
export default class Item extends Component {
    render() {
        const { id, name, done } = this.props
        const { mouse } = this.state
        return (
            // 切记事件方法必须传入一个函数,如果传值应该写高阶函数,避免直接调用
            <li onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)} style={{ backgroundColor: mouse ? "#ddd" : "white" }} >
                <label>
                    {/* defaultChecked、defaultValue 只在初始渲染时由状态控制,之后更新不再跟状态有关系,而checked、value在全过程中都受状态控制 */}
                    <input type="checkbox" checked={done} onChange={this.handleChange(id)} />
                    <span>{name}</span>
                </label>
                <button className="btn btn-danger" style={{ display: mouse ? "block" : 'none' }} onClick={() => this.handleDelete(id)}>删除</button>
            </li >
        )
    }
    state = { mouse: false }//标识鼠标移入,移除
 
    // 鼠标移入,移除回调
    handleMouse = (flag) => {
        return () => {
            this.setState({ mouse: flag })
        }
    }
    //勾选取消勾选的回调
    handleChange = (id) => {
        return (e) => {
 
            this.props.updateTodos(id, e.target.checked);
        }
    }
    // 删除一个todo的回调
    handleDelete(id) {
        if (window.confirm('确定删除吗')) { this.props.deleteTodo(id) }
 
    }
}
List组件

import PropTypes from 'prop-types'  对接收的props进行类型,必要性的限制

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import './index.css'
import Item from '../Item'
export default class List extends Component {
    render() {
        const { todos, updateTodos, deleteTodo } = this.props
        return (
            <ul className="todo-main">
                {todos.map((todos) => {
                    return <Item key={todos.id} {...todos} updateTodos={updateTodos} deleteTodo={deleteTodo} />
                })}
            </ul>
        )
    }
    //,对接收的props进行类型,必要性的限制
    static propTypes = {
        todos: PropTypes.array.isRequired,
        updateTodos: PropTypes.func.isRequired,
        deleteTodo: PropTypes.func.isRequired
    }
}

Footer组件

reduce计算值 参数:回调函数(函数上一次的返回值,第一次为reduce参数2,每一项),初始值

import React, { Component } from 'react'
import './index.css'
export default class Footer extends Component {
    render() {
        const { todos } = this.props
        //总数
        const total = todos.length
        // 计算已完成的个数
        //reduce计算值 参数:回调函数(函数上一次的返回值,第一次为reduce参数2,每一项),初始值
        const doneCount = todos.reduce((pre, current) => {
            return pre + (current.done ? 1 : 0)
        }, 0)
 
        return (
            <div className="todo-footer">
                <label>
                    {/*defaultChecked只执行一次  */}
                    <input type="checkbox" onChange={this.handleCheckAll} checked={doneCount === total && total != 0 ? true : false} />
                </label>
                <span>
                    <span>已完成{doneCount}</span> / 全部{total}
                </span>
                <button className="btn btn-danger" onClick={this.handleClearAllDone}> 清除已完成任务</button>
            </div>
        )
    }
    //全选的回调
    handleCheckAll = (e) => {
        this.props.checkAllTodo(e.target.checked)
    }
    // 清除所有已完成的回调
    handleClearAllDone = () => {
        this.props.clearDone()
    }
}
总结

1.拆分组件、实现静态组件,注意:className、style的写法

2.动态初始化列表,如何确定将数据放在哪个组件的state中?

——某个组件使用:放在其自身的state中

——某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)

3.关于父子之间通信:

1.【父组件】给【子组件】传递数据:通过props传递

2.【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数

4.注意defaultChecked 和 checked的区别,类似的还有:defaultValue 和 value

5.状态在哪里,操作状态的方法就在哪里