React 应用
尚硅谷 2021 版 React 技术全家桶全套完整版(零基础入门到精通/男神天禹老师亲授)
React 脚手架
- xxx脚手架:用来帮助程序员快速创建一个基于 xxx 库的模板项目
- 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
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<!-- 开启理想视口,用于做移动端网页的适配 -->
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- 用于配置浏览器页签+地址栏的颜色(仅支持安卓手机浏览器) -->
<meta name="theme-color" content="#000000" />
<!-- 用于指定网页添加到手机主屏幕后的图标 -->
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo.png" />
<!-- 应用加壳的配置文件(手机分辨率多少用什么图片) -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!-- 如浏览器不支持JS则展示标签中的内容 -->
<noscript>You need to enable JavaScript to run this app.</noscript>
%PUBLIC_URL%
代表 public 文件夹的路径reportWebVitals
记录页面性能,其使用web-vitals
库setupTests
组件单元测试,其使用@testing-library/jest-dom
安装插件 ES7 React/Redux/GraphQL/React-Native snippets
样式模块化
-
使用预编译语言比如:Less、Scss
-
把 css 名改为
index.module.css
import xx from './index.module.css'
<h2 className={xx.xx}>
组件化编码流程
-
拆分组件:拆分页面,抽取组件
-
实现静态组件,使用组件实现静态页面效果
-
实现动态组件
动态显示初始化数据(数据类型、数据名称、保存在哪个组件)
交互(从绑定事件监听开始)
TODO List
- 生成唯一 UUID
yarn add nanoid
- 点击复选框切换提示是否选中及附带信息
- 点击按钮
handleCheck = id => {
return e => {
this.props.updateTodo(id, e.target.checked)
}
}
<input type="checkbox" defaultChecked={false} onChange={this.handleCheck(id)} />
handleDelete = id => {
if (window.confirm('确定删除吗?')) {
this.props.deleteTodo(id)
}
}
<button onClick={() => this.handleDelete(id)}>删除</button>
- 拆分组件、实现静态组件,注意:className、style 的写法
- 动态初始化列表,如何确定将数据放在哪个组件的 state 中
- 某个组件使用:放在自身的 state 中
- 某个组件使用:放在他们共同的父组件 state 中(官方称此操作:状态提升)
- 父子之间通信
- 【父组件】给【子组件】传递数据:通过 props 传递
- 【子组件】给【父组件】传递数据:通过 props 传递,要求父提前给子传递一个函数
- 注意 defaultChecked 和 checked 区别,类似的还有:defaultValue 和 value
- 状态在哪里,操作状态的方法就在哪里
App.jsx
import React, { Component } from 'react'
import './index.css'
import Header from './component/Header'
import List from './component/List'
import Footer from './component/Footer'
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: true },
],
}
addTodo = todoObj => {
const { todos } = this.state
const newTodos = [todoObj, ...todos]
this.setState({ todos: newTodos })
}
updateTodo = (id, done) => {
const { todos } = this.state
const newTodos = todos.map(todoObj => {
if (todoObj.id === id) return { ...todoObj, done }
else return todoObj
})
this.setState({ todos: newTodos })
}
deleteTodo = id => {
const { todos } = this.state
const newTodos = todos.filter(todoObj => todoObj.id !== id)
this.setState({ todos: newTodos })
}
checkAllTodo = done => {
const { todos } = this.state
const newTodos = todos.map(todoObj => {
return { ...todoObj, done }
})
this.setState({ todos: newTodos })
}
clearAllDone = () => {
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>
)
}
}
Header.jsx
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { nanoid } from 'nanoid'
import './index.css'
export default class Header extends Component {
static propTypes = {
addTodo: PropTypes.func.isRequired,
}
handleKeyUp = e => {
const { keyCode, target } = e
if (keyCode !== 13) return
if (target.value.trim() === '') return
const todoObj = { id: nanoid(), name: target.value, done: false }
this.props.addTodo(todoObj)
target.value = ''
}
render() {
return (
<div className="todo-header">
<input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认" />
</div>
)
}
}
List.jsx
import React, { Component } from 'react'
import Item from '../Item'
import PropTypes from 'prop-types'
import './index.css'
export default class List extends Component {
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>
)
}
}
Item.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 })
}
}
handleCheck = id => {
return e => {
this.props.updateTodo(id, e.target.checked)
}
}
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' : '#fff' }} onMouseLeave={this.handleMouse(false)} onMouseEnter={this.handleMouse(true)}>
<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>
)
}
}
Footer.jsx
import React, { Component } from 'react'
import './index.css'
export default class Footer extends Component {
handleCheckAll = e => {
this.props.checkAllTodo(e.target.checked)
}
handleClearAllDone = () => {
this.props.clearAllDone()
}
render() {
const { todos } = this.props
const doneCount = todos.reduce((pre, cur) => pre + (cur.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 className="btn btn-danger" onClick={this.handleClearAllDone}>
清除已完成任务
</button>
</div>
)
}
}