React应用(基于React脚手架)

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

react脚手架

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

    1. 包含了所有需要的配置(语法检查、jsx编译、devServer…)
    2. 下载好了所有相关的依赖
    3. 可以直接运行一个简单效果
  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脚手架项目解构

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库的支持)

脚手架public>index.js文件

<!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="red" />
    <meta
      name="description"
      content="Web site created using create-react-app"
		/>
		<!-- 用于指定网页添加到手机主屏幕后的图标 -->
		<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
		<!-- 应用加壳时的配置文件 -->
		<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React App</title>
  </head>
  <body>
		<!-- 若llq不支持js则展示标签中的内容 -->
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

使用React脚手架实现简单地Hello World案例

在React脚手架帮我们生成的文件中有很多是我们现在用不到的,我们现阶段能用到的只有上面我标红的三个文件,下面我们来实现以下Hello World案例

首先从index.html文件说起,在inde.html文件中我们只需要保留以下内容

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
     <title>首页</title>
 </head>
 <body>
     <div id="root"></div>
 </body>
 </html>

就是如此简单,因为其他的一些功能我们暂时不需要

下面是程序的入口index.js文件

//1.首先要引入React核心库
import React from 'react'

//2.引入ReactDOM
import ReactDOM from 'react-dom'

//3.在index.js中引入根组件
import App from './App'

//4.渲染App到页面
ReactDOM.render(<App/>,document.getElementById('root'))

这就是程序的入口文件,我们现在所需要的的功能不是那么复杂,所以可以写的稍微简单一点

然后就是所有组件的根组件App.js

//以下是每一个组件的惯用写法

//1.是首先引入react核心库,采用的是即引入全部核心库,又半引入了库中的一些类
import React,{Component} from 'react'

//引入其他组件
import Hello from './components/Helllo'
import Welcome from './components/Welcome'

//2.创建并暴露App组件
export default class App extends Component {
    render () {
        return (
            <div>
                <Welcome/>
                <Hello/>
            </div>
        )
    }
}

在根组件里面尽量不要写页面,也不要写任何的业务逻辑,这些也免得效果和业务逻辑我们要将其分化到各个组件。

下面我们来说说组件,在React脚手架中,我们需要将我们写的组件全部存放在src目录下的components文件夹中,在这里面每一个单独的文件夹中,如果有index.jsx文件,index.css文件,以及其他的一些js文件的话那么就可以说这是一个组件了。其中index.jsx文件主要负责将虚拟DOM节点转换为真实DOM节点(只要在components文件夹下的文件夹中有此文件就可以说其是组件),index.css文件主要用于存放样式,其他的js文件用于处理一些业务逻辑。

Hello组件下index.jsx文件中的代码

import React,{Component} from 'react'

//引入CSS样式
import './index.css'

export default class Hello extends Component {
    render () {
        return (
            <div className='he'>
                <h2>Hello World</h2>
            </div>
        )
    }
}

index.css样式中的代码

.he {
    background-color: aqua;
}

模块化样式

我们在使用组件时如果不同的组件中的两个元素使用了相同的类名,虽然在不同的组件中,但是最终都会在App.js这个根组件上进行挂载,这就会造成写在下面的组件类样式会覆盖掉上面的组件类样式。

解决方案:

模块化组件样式:

将样式文件命名为index.module.css

index.jsx文件中引入如下:

import React,{Component} from 'react'

//引入模块化CSS样式
import hello from './index.module.css'

export default class Hello extends Component {
    render () {
        return (
            <div className={hello.title}>{/**在jsx中类名的写法*/}
                <h2>Hello World</h2>
            </div>
        )
    }
}

插件的安装

在vscode中安装React插件可以帮我们快速生成代码。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AVX3tNEp-1647827961701)(../images/image-20220312162732669.png)]

安装之后我们输入rcc可以帮我们快速生成类式组件,输入rfc可以帮我们快速生成函数式组件。

它的其他快速生成代码的方式:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UAOEfjIR-1647827961702)(../images/image-20220312163406943.png)]

组件的组合使用-TODOList

功能界面的组件化编码流程

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

实现TODOList案例_静态组件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6mTn00PP-1647827961703)(../images/image-20220312165125334.png)]

组件拆分图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D5h3Oby6-1647827961704)(../images/image-20220316152806844.png)]

案例源代码:

App.jsx根组件中的代码

import React, { Component } from 'react'
import FOOTER from './components/FOOTER'

import HEADER from './components/HEADER'
import MAIN from './components/MAIN'

import './index.css'

export default class App extends Component {
    state= {
        lists : [
            {id:'001',name:'吃饭',done:true},
            {id:'002',name:'睡觉',done:true},
            {id:'003',name:'打代码',done:true},
        ]
    }

    //新增
    addList = (listObj) => {
        const {lists} = this.state;
        const newLists = [listObj,...lists]
        
        this.setState({lists:newLists})
    }

    //修改
    changeList = (id,done) => {
        const {lists} = this.state
        const newLists = lists.map((list) => {
            if (list.id === id) {
                list.done = done;
                return list
            } else {
                return list
            }
        })
        this.setState({lists:newLists})
    }

    //删除
    deleteList = (id) => {
        const {lists} = this.state;
        
        const newLists = lists.filter((list) => {
            return list.id !== id;
        })

        this.setState({lists:newLists})
    }

    //清除已完成的任务
    clearDone () {
        const {lists} = this.state;

        const newLists = lists.filter((list) => {
            return list.done === false;
        })

        this.setState({lists:newLists})
    }

    //全选或全不选
    choosenAll = (done) => {
        console.log(this.state);
        const {lists} = this.state;

        const newLists = lists.map((list) => {
            list.done = done;
            return list;
        })

        this.setState({lists:newLists})
    }

    render() {
        return (
            <div className="todo-container">
            <div className="todo-wrap">
                <HEADER choosenAll={this.choosenAll} addList={this.addList}/>
                <MAIN lists={this.state.lists} updateList={this.changeList} deleteList={this.deleteList}/>
                <FOOTER choosenAll={this.choosenAll} lists={this.state.lists} clearDone={this.clearDone.bind(this)}/>
            </div>
        </div>
        )
    }
}

HEADER组件中的代码

import React, { Component } from 'react'

import { nanoid } from 'nanoid'

import './index.css'

export default class HEADER extends Component {

  addObj = (event) => {
    const {target,keyCode} = event

    if (keyCode === 13) {
      if (target.value.trim() === '') {
        alert('请在输入框中输入内容')
        return
      }
      const listObj = {
        id:nanoid(),
        name:target.value,  
        done:false
      }
      this.props.addList(listObj)
      target.value = ''
    }
    // const {value,}
  }

  render() {
    return (
      <div className="todo-header">
        <input onKeyUp={this.addObj} type="text" placeholder="请输入你的任务名称,按回车键确认"/>
      </div>
    )
  }
}

MAIN中间展示区域外部组件的代码

import React, { Component } from 'react'
import PropsTypes from 'prop-types'

import ITEM from '../ITEM'
import './index.css'

export default class MAIN extends Component {
    static PropsTypes = {
        lists:PropsTypes.array.isRequired,
        updateList:PropsTypes.func.isRequired,
        deleteList:PropsTypes.func.isRequired
    }

    render() {
        const {lists,updateList,deleteList} = this.props
        return (
        <ul className="todo-main">
            {
                lists.map(list => {
                    return (
                        <ITEM key={list.id} {...list} updateList={updateList} deleteList={deleteList}/>
                    )
                })
            }
            
        </ul>
        )
    }
}

ITEM详情组件中的代码

import React, { Component } from 'react'

import './index.css'

export default class ITEM extends Component {
    state = {
        mouse:false
    }

    //鼠标移入移出时的操作
    mouseMove = (flag) => {
        return () => {
            this.setState({mouse:flag})
        }
    }

    //更改是否已完成
    changeDone = (id) => {
        return (event) => {
            console.log(id,event.target.checked);
            // console.log(this.props);
            this.props.updateList(id,event.target.checked)
        }
    }

    //删除
    deleteList (id) {
        console.log();
        if (window.confirm('是否删除该项')) {
            this.props.deleteList(id)
        }
    }

    render() {
        const {id,name,done} = this.props;
        const {mouse} = this.state;
        return (
            <li style={{background: mouse ? '#ddd' : 'white'}} onMouseEnter={this.mouseMove(true)} onMouseLeave={this.mouseMove(false)}>
                <label>
                <input type="checkbox" checked={done} onChange={this.changeDone(id)}/>
                <span>{name}</span>
                </label>
                <button onClick={() => {this.deleteList(id)}} className="btn btn-danger" style={{display: mouse ? 'block' : 'none'}}>删除</button>
            </li>
        )
    }
}

FOOTER底部组件的代码

import React, { Component } from 'react'

import './index.css'

export default class FOOTER extends Component {

  //清空所有已完成的任务
  clearDone () {
    // console.log(this.props);
    this.props.clearDone();
  }

  //全选或全不选
  choosenAll = (event) => {
    this.props.choosenAll(event.target.checked)
  }


  render() {
    const {lists} = this.props

    let sum = lists.length;

    let count = lists.reduce((pre,list) => pre + (list.done ? 1 : 0),0)

    return (
      <div className="todo-footer">
        <label>
          <input type="checkbox" onChange={this.choosenAll} checked={sum === count & sum !== 0 ? true : false}/>
        </label>
        <span>
          <span>已完成{count}</span> / 全部{sum}
        </span>
        <button onClick={this.clearDone.bind(this)} className="btn btn-danger">清除已完成任务</button>
      </div>
    )
  }
}

案例相关知识点:

  1. 拆分组件、实现静态组件,注意:classNamestyle的写法
  2. 动态初始化列表,如何确定将数据放在哪个组件的state中?
    1. ——某个组件使用:放在其自身的state中
    2. ——某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
  3. 关于父子之间通信:
    1. 【父组件】给【子组件】传递数据:通过props传递
    2. 【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
  4. 注意:defaultCheckedchecked的区别:defaultValuevalue
  5. 状态在哪里,操作状态的方法就放在哪里
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值