使用create-react-app创建react应用
react脚手架
-
xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目
- 包含了所有需要的配置(语法检查、jsx编译、devServer…)
- 下载好了所有相关的依赖
- 可以直接运行一个简单效果
-
react提供了一个用于创建react项目的脚手架库: create-react-app
-
项目的整体技术架构为: react + webpack + es6 + eslint
-
使用脚手架开发的项目的特点: 模块化, 组件化, 工程化
创建并启动项目
第一步,全局安装: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插件可以帮我们快速生成代码。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
安装之后我们输入rcc
可以帮我们快速生成类式组件,输入rfc
可以帮我们快速生成函数式组件。
它的其他快速生成代码的方式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
组件的组合使用-TODOList
功能界面的组件化编码流程
- 拆分组件: 拆分界面,抽取组件
- 实现静态组件: 使用组件实现静态页面效果
- 实现动态组件
- 动态显示初始化数据
- 数据类型
- 数据名称
- 保存在哪个组件?
- 交互(从绑定事件监听开始)
- 动态显示初始化数据
实现TODOList案例_静态组件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
组件拆分图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
案例源代码:
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>
)
}
}
案例相关知识点:
- 拆分组件、实现静态组件,注意:
className
、style
的写法 - 动态初始化列表,如何确定将数据放在哪个组件的state中?
- ——某个组件使用:放在其自身的state中
- ——某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
- 关于父子之间通信:
- 【父组件】给【子组件】传递数据:通过
props
传递 - 【子组件】给【父组件】传递数据:通过
props
传递,要求父提前给子传递一个函数
- 【父组件】给【子组件】传递数据:通过
- 注意:
defaultChecked
和checked
的区别:defaultValue
和value
- 状态在哪里,操作状态的方法就放在哪里