React应用(基于React脚手架)

本文介绍了基于React脚手架创建项目并启动的过程,详细解析了项目结构和`export`与`export default`的区别。文章还探讨了样式模块化、组件化的编码流程,并通过TodoList案例展示了组件的组合使用,包括主页面、Header、Item和List组件的实现。总结了组件拆分、状态管理和父子组件通信的关键点。
摘要由CSDN通过智能技术生成

目录

React应用(基于React脚手架)

 react脚手架

 创建项目并启动

 react脚手架项目结构

export 跟export default 的区别

 样式模块化

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

组件的组合使用-TodoList案例

效果

组件拆分

主页面app.jsx

 Header 组件

 Item组件

List组件

总结


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.状态在哪里,操作状态的方法就在哪里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值