环境搭建
npm i -g yarn #全局安装yarn
yarn -v #查看yarn版本号
全局安装react环境
sudo npm i -g create-react-app// mac环境
npm i -g create-react-app // 全局安装 win
create-react-app --version 查看版本号
create-react-app react-demo // 初始化项目
防止版本兼容的话,使用npx在本地安装相关包
yarn init -y #初始化项目
yarn add -D create-react-app #使用本地安装
npx create-react-app --version #查看脚手架版本
yarn或者npx创建本地项目
yarn create-react-app react-demo
npx create-react-app react-demo
Vue中使用Flow做静态类型检查,在React中使用prop-types进行校验
# 15以后的版本我们的prop-types是另外的包,需要手动下载
yarn add -D prop-types
使用如下:
// 下载完成后在子组件中引入
import PropTypes from 'prop-types'
// 使用语法:组件名.propTypes添加一个属性,属性值为对象
Son.propTypes = {
name: PropTypes.string.isRequired, // 给isRequired属性代表的必传项,不传就会报错.
age: PropTypes.number
}
// 还可以使用defaultProps指定它的默认值,那么我们调用子组件的时候不传值也不会报错
Son.defaultProps = {
name: '肥羊',
age: 18
}
组件传值
props 父传子、子传父、以及context(已经快淘汰)跨组件之间的通信
Context使用如下:
// context.js中内容
import React from 'react'
// 创建一个 Context 对象,组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。
const MainContext = React.createContext('这里面可以设置默认值')
export default MainContext
// 传值使用Provider接收value属性,传递给消费组件
<MainContext.Provider value={/* 某个值 */}>
// 接收值有两种方法
// 1、使用Context.Consumer(多用于函数式组件)
<MainContext.Consumer>
{value => /* 基于 context 值进行渲染*/}
</MainContext.Consumer>
// 2、用Class.contextType挂载在class上,然后使用 this.context 来消费最近 Context 上的那个值。可以在任何生命周期中访问到它,包括 render 函数中。(多用于类组件)
static contextType = MainContext
// 或者
Hello.contextType = MainContext
// 就可以直接用this.context访问了
兄弟组件中,使用eventbus事件总线进行通信,采用发布-订阅event bus进行
yarn add -D events
// event.js
import { EventEmitter } from 'events'
// 导入事件总线,利用这个对象发射和监听事件,这个对象是全局的
const eventBus = new EventEmitter()
export default eventBus
// 发送事件eventBus.emit('事件名', 参数)
eventBus.emit('sayHello', send)
// 添加事件监听,监听从header组件发送过来的sayHello事件
componentDidMount() {
eventBus.addListener('sayHello', this.sayHelloListener)
}
// 处理事件监听
sayHelloListener(data){
console.log(data) // 这样就拿到从另一个兄弟组件中传过来的值了
}
// 移除事件监听 一定要移除监听!!!
componentWillUnmount() {
eventBus.removeListener('sayHello', this.sayHelloListener)
}
setState
react没有实现类似于Vue2中Object.defineProperty或者是Vue3 proxy的方式来监听数据的变化
必须通过setState来告知react状态的改变。
setState是继承自Component,当我们调用setState的时候,会重新执行render方法。
在生命周期里是异步状态
在合成事件如onClick中,异步 并不是异步代码 执行顺序导致异步
在componentDidMount和定时器是同步 执行顺序导致同步
在原生事件也是同步
获取真实DOM
- string类型(已经被淘汰掉了)
- createRef创建(官方推荐,16.3之后的版本使用这个)
使用如下:
import React, { Component,createRef } from 'react'
export class AppRef extends Component {
constructor(){
super()
this.headerRef = createRef()// 下面的也一样 不过这种不要轻易去操作dom
}
inputRefs = createRef()
userRefs = createRef()
changeText = ()=>{
console.log(this.headerRef,this.inputRefs.current.value)
this.userRefs.current.innerHTML = this.inputRefs.current.value
}
render() {
return (
<div>
<input type='text' placeholder='输入' ref={this.inputRefs} onChange={this.changeText} />
createRef App
<Header ref={this.headerRef} />
<p className='red' ref={this.userRefs}></p>
</div>
)
}
}
React生命周期四个阶段
- 组件初始化阶段:
getInitialState:初始化 this.state 的值,只在组件装载之前调用一次
getDefaultProps:只在组件创建时调用一次并缓存返回的对象(即在 React.createClass 之后就会调用)。
因为这个方法在实例初始化之前调用,所以在这个方法里面不能依赖 this 获取到这个组件的实例。
- 组件加载阶段:
componentWillMount:只会在装载之前调用一次
componentDidMount:只会在装载完成之后调用一次
- 组件更新阶段:
componentWillReceiveProps 、shouldComponentUpdate(后面可以直接组件基础PureComponent,就不需要走shouldComponentUpdate判断了)、
componentWillUpdate、componentDidUpdate这些方法不会在首次render组件的周期调用。
组件优化:
1.shouldComponentUpdate(nextProps, nextState)
2.PureComponent当组件组件更新时,如果组件的props和state没有改变,render就不会触发,就不会生成和对比virtualDom.
只适用于一些展示性质的页面。
不能在函数组件用,只浅比较。不适合含有多层嵌套对象的state和props中
- 组件销毁阶段:
componentWillUnmount:卸载组件触发
下面举例:
import React, { Component } from 'react'
export class Btn extends Component {
constructor(props) {
// 我们需要执行第一个阶段,就是初始化阶段
super(props)
this.state = {
cout: 0
}
console.group('%c 1-初始化阶段constructor', 'color: green', props, this.state)
}
handleClick(e) {
console.log(e.target)
// setState会触发render
this.setState({ cout: 10+this.state.cout })
}
UNSAFE_componentWillReceiveProps(){
console.group('%c componentWillReceiveProps','color:#0c9ab4')
}
UNSAFE_componentWillMount(){
// 严格模式下已经被抛弃了 18版本
// 在组件挂载,redner之前调用,这里可以发起请求
// 可以在这个方法里面调用setState改变状态,不会导致额外调用一次render
console.group('%c 2-组件加载前WillMount','color:#be0e24')
}
componentDidMount(){
console.group('%c 4-组件加载后tDidMount','color:#0681d0')
}
componentDidUpdate(){
console.group('%c 7-组件更新后DidUpdate','color:#ec7259')
}
shouldComponentUpdate(){
console.group('%c 5-是否更新组件页面shouldComponentUpdate','color:#2c801f')
// return false
return true
}
UNSAFE_componentWillUpdate(){
console.group('%c 6-组件将要更新WillUpdate','color:#970ebe')// 后就走render-组件加载或者数据更新
}
componentWillUnmount() {
// 这里完成组件的卸载和数据的销毁,清除组件所有的setTimeout以及移除所有的事件监听
console.group('%c 8-组件销毁WillUnmount')
}
render() {
// 只要状态发生变化,一定会执行render方法
// render函数会插入jsx生成的dom结构,react会生成一个虚拟的dom树,在每一次组件更新时
// 通过其diff算法比较更新前后的新旧DOM树
// 比较以后,会找到最小的有差异的DOM节点,并重新渲染。
console.group('%c 3-组件加载或者数据更新render', 'color: blue')
let {cout} = this.state
return (
<div>
<button onClick={this.handleClick.bind(this)}>
你操作DOM变化{cout}次
</button>
</div>
)
}
}
export default Btn
高阶组件
JS函数其实都是指向某个变量,变量可以指向函数,函数的参数能接收变量, 所以一个函数可以去接收另外一个函数作为参数,这种函数就称之为高阶函数。
高阶函数
1、接收一个或者多个函数作为输入
2、输出一个函数
// 一个最简单的高阶函数
function add(x, y, fn) {
return fn(x) + fn(y)
}
add(-5, 5, Math.abs) // 10
高阶组件就是一个函数,这个函数接收一个组件作为参数,并返回一个新的组件。
高阶组件(HOC)是React中用于复用组件逻辑的一种高级技巧。
是一种基于React的组合特性而形成的设计模式。
高阶组件的缺陷
HOC需要在原组件上进行包裹或是嵌套
如果说大量使用HOC,将会产生非常多的嵌套,让调试会非常困难
HOC可以去劫持props,有时候会导致冲突。
Hooks
Hook是React16.8的新增特性。它可以让你在不编写class组件的情况下使用state以及其他的React特性(生命周期方法)。
Hook的出现解决了之前React存在的很多问题,比如说HOC的嵌套问题。
Hook是JS的函数,这个函数可以帮你钩入react的state以及生命周期方法(创建,更新,销毁)。
凡是 use 开头的 React API 都是 Hooks,
Hook的使用原则
1. 不要在循环里面、条件判断或者分支里面去使用Hook,只能在函数的最外层调用
2. 只能在react函数组件中使用hook
3. 单独在一个useState我们称之为hook,Hooks是对一类Hook的总称。
useState&useEffect
useState
hook:让函数式组件具备类组件的一些特性,比如状态和生命周期.
useEffect
函数式组件中是没有生命周期的,就可以使用useEffect来替代。
useEffect=组件加载、组件更新、组件卸载的三个生命周期方法的组合。
副作用:函数副作用,当调用函数的时候,除了返回值以外,还对调用函数产生附件的影响。
如 ajax 请求、访问原生dom 元素、本地持久化缓存、绑定/解绑事件、添加订阅、设置定时器、记录日志。useEffect 在全部渲染完毕后才会执行,useLayoutEffect 会在浏览器 layout 之后,painting 之前执行。
[3,4].pop()
// 每次执行pop函数的时候,原数组都会减少一个元素,改变了原数组其实就是一种副作用
React中,更改组件中的DOM都属于副作用,有些副作用需要去清除,需要返回一个函数,比如挂载时设置的定时器/监听,卸载的时候取消定时器/监听。
useRef
用法跟createRef类似,都是存储DOM元素对象的
useReducer
在hooks中,可以为函数式组件提供类似Redux的功能,类似于vuex都是提供状态管理的。
官方提供的两种state管理:useState,useReducer。
路由router以及axios等
// 安装路由
yarn add -D react-router-dom
// "react-router-dom": "^6.3.0" package.json查看是否安装成功
// 安装 渲染路由的方法
yarn add -D react-router-config
// "react-router-config": "^5.1.1", 引入渲染路由的方法 react-router-dom的版本要跟这里适配都用5.+
yarn add -D react-router-dom@5
// 使用:
// index.js BrowserRouter history模式 HashRouter hash模式 路由可以通过as起别名
import { BrowserRouter as Routes } from 'react-router-dom'
<Routes>
<App />
</Routes>
// nav.js或者其他需要用路由的地方比如ui下的index.js集中管理路由
import { Link, withRouter } from 'react-router-dom'
<Link to={key}>{label}</Link>
// 或者
props.history.push(e.key)
// 路由集中管理
// router文件配置数据
import routes from './router/' // 引入全局配置的路由规则
import { renderRoutes } from 'react-router-config' // 引入渲染路由的方法
{renderRoutes(routes)}// 渲染
// 路由跳转的页面props里就有路由信息,普通组件渲染没有路由信息,需要用到withRouter,比如nav组件如下调整
export default withRouter(Nav)// withRouter让Nav组件传入props (history,location,match,staticContext)
// 1、Route用于路径的匹配(用于定义组件和路径的映射关系,相当于一个占位符,在哪里占位就在哪里渲染)
// 2、path属性:用于设置匹配到的路径
// 3、component: 用于匹配带的路径渲染组件
// 4、exact 精确匹配 比如/ 和/home /user 可以防止/user的时候/ 也展示
// 路由传参
// 动态路由:路由中的路径并不固定,那么我们可以path在route匹配的时候写成/user/:id,这种时候获取参数使用this.props.match.params
// 如果是比较复杂的数据,比如'/userinfo?name=casey&age=18'这种时候需要使用location中的search获取,并且需要下载安装query-string进行处理,这个方法React已经不推荐了。
// cnpm i -D query-string
// 所以这种复杂数据的传参我们可以在Link to中直接传入一个对象
// 路由的集中管理
// 到这里我们会感觉React的路由没有Vue路由好用,因为在我们Vue中路由是集中管理的,但是其实在React中也是可以对路由进行集中管理的。需要用到react-router-cofig
yarn add -D events
// 兄弟组件中,使用eventbus事件总线进行通信,采用发布-订阅event bus进行 "events": "^3.3.0",
// 这个版本可以直接使用scss,不需要配置webpack
// 安装node-sass "node-sass": "^7.0.1",
yarn add -D node-sass
// 安装axios "axios": "^0.27.2",
yarn add -D axios
Antdesign ui框架使用,也可以用其他框架
yarn add antd// "antd": "^4.21.3",
// https://ant.design/index-cn 官网地址
// 修改 src/App.css,在文件顶部引入 antd/dist/antd.css。
@import '~antd/dist/antd.css';
// 通常需要优化,按需引入更好,打包体积更小,官网说比如无法进行主题配置
// 使用craco "@craco/craco": "^6.4.3",
yarn add @craco/craco
yarn add -D babel-plugin-import// 配置按需引入设置 "babel-plugin-import": "^1.13.5",
// 创建craco.config.js 配置如下
module.exports = {
babel: {
plugins: [
[
"import",
{
"libraryName": "antd",
"libraryDirectory": "es",
"style": "css" // 设置为true默认是less
}
]
]
}
}
// 注释或者去掉 @import '~antd/dist/antd.css'; 重启项目
/* package.json */
"scripts": {
- "start": "react-scripts start",
- "build": "react-scripts build",
- "test": "react-scripts test",
+ "start": "craco start",
+ "build": "craco build",
+ "test": "craco test",
}
// 使用图标,需要先安装 "@ant-design/icons": "^4.7.0",
yarn add -D @ant-design/icons
mock数据
// mock文件下
yarn init -y// 创建package.json
yarn add koa -D // 下载koa 基于nodejs的web开发框架 优雅、简洁、表达力强、自由度高 官网 https://koa.bootcss.com/
yarn add koa-router -D
yarn add mockjs -D
redux
存储状态state的容器 可预测的状态管理 主要是用来做组件通信的
之前兄弟组件,子孙等组件,传值很麻烦,通过props或者eventbus,context等
业务场景需要涉及到不同层次的组件进行组件通信时,用redux
使用Redux的五步骤为:
1、创建一个对象,作为状态state
2、创建Store存储state
3、通过action来修改state
4、修改reducer中的处理代码
(reducer 纯函数:一个函数的返回结果只依赖于它的参数,并且在执行过程里面没有副作用)
5、可以在派发action之前,监听store的变化
yarn add redux -D // 或者 npm i redux -D
// "redux": "^4.2.0"
// index.js的内容
const redux = require('redux')
// redux有三大块:store、action、reducer
const initState = {
count: 0
}
// 3、reduce 是去连接store和action的
const reducer = (state=initState, action) => {
switch(action.type) {
case 'INCREMENT':
return {...state, count: state.count + 1}
// 如果没有匹配到任何的action就直接返回state
default:
return state
}
}
// 1、store 保存状态,创建一个store对象即可
const store = redux.createStore(reducer)
// 2、action 是用来修改store的
const action = { type: 'INCREMENT' }
// 5、在派发action之前可以订阅store的修改,监听store的变化
store.subscribe(() => {
console.log('store被修改了')
console.log(`count: ${store.getState().count}`) // count: 1
})
// 4、派发action
store.dispatch(action)
// redux的三大原则
// 1. 单一数据源
// 整个应用程序的state被存储在一颗object tree 中,并且这个object tree只存储在一个store中,
// 单一的数据源可以让整个应用程序的state变得很方便去维护、追踪、修改
// 2. state是只读的
// 唯一修改的state的方法是:触发action,不用试图在其他地方通过其他的方式来修改state
// 可以保证所有的修改都被集中处理,并且严格按照顺序来执行
// 3. 使用纯函数来执行修改
// 通过reducer将state和action联系在一起,并且返回一个新的state
React-redux
用Provider包括根组件,这样就能让所有的子组件都能拿到store中的数据
yarn add react-redux -D //cnpm i -D react-redux
中间件
redux推荐的网络请求的中间件是redux-thunk,这个中间件的目的就是在dispatch和action最终到达reducer之间扩展一些代码
yarn add -D redux-thunk
防止浏览器警告
// 没有添加被动事件监听器来阻止'whell’事件,请考虑添加事件管理者’passive’
yarn add default-passive-events -S
// index.js
import 'default-passive-events'