1.什么是react
React是一个用于构建用户界面的javaScript库,起源于facebook的内部项目,后续在13年开源了出来

2.react的基本使用
2.1 JavaScript直接引入js
npm i react react-dom
<script src="./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
<script>
// 创建元素节点
// 元素名称 元素属性(传递的是一个对象) 元素内容
let myli = React.createElement('li', { className:'myli' }, 'hellow react');
// 渲染到页面
ReactDOM.render(myli, root)
</script>
2.2 react脚手架
脚手架意义:
- 脚手架是开发现代Web应用的必备
- 充分利用 Webpack,Babel,ESLint等工具辅助项目开发
- 零配置,无需手动配置繁琐的工具即可使用
- 关注业务,而不是工具配置
# node自带react脚手架
npx create-react-app 项目名
# 全局安装react脚手架
npm i -g create-react-app
create-react-app 项目名
// 入口函数 index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(<App />, document.getElementById('root'))
// App组件
import React, { Component, Fragment } from 'react';
export default class App extends Component {
render() {
return (
<div>hello world</div>
);
}
}
3.JSX的使用
JSX就是Javascript和XML结合的一种格式。React发明了JSX,可以方便的利用HTML语法来创建虚拟DOM,当遇到
<,JSX就当作HTML解析,遇到{就当JavaScript解析
render(){
// 只允许使用一个根组件
return (
// React.Fragment 和vue中的template标签性质一样 为了不产生多余的div
// 也可以使用 <></>空标签
<Fragment>
<div>123</div>
{this.renderlist()}
</Fragment>
)
}
// 最好使用箭头函数 这样this的指向就是当前的组件
renderlist = ()=>{
return <ul>
// 循环用map
[1,2,3].map(item=>{
//循环渲染得加key
return <li key={item}>{item}<li>
})
</ul>
}
注意点:
- React元素的属性名使用驼峰命名法
- 特殊属性名:class -> className,for -> htmlFor,tabindex -> tabIndex
- 推荐:使用 小括号包裹JSX,从而避免JS中换行的问题
jsx语法的转化过程
- JSX仅仅是
createElement()方法的语法糖(简化语法) - JSX语法被 @babel/preset-react 插件编译为
createElement()方法 - React 元素: 是一个对象,用来描述你希望在屏幕上看到的内容

4.react组件
4.1 父子组件的传值
父子传值:在父组件的子组件标签上增加属性,子组件通过props接收数据
子父传值:在父组件的子组件标签上传递组件的方法,子组件通过props接收并执行该方法
props.children:表示组件标签的子节点,当组件标签有子节点时,props就会有该属性(组件中间的部分内容)
单项数据流:父组件向子组件传递数据,当父组件的数据改变时,子组件也会改变,但子组件不能改变父组件传递的数据,props里的值为readonly
兄弟传值
-
一层一层传值
-
redux/react-redux
-
Context(爷孙传值) 了解
const { Provider, Consumer } = React.createContext() // 必须是value <Provider value='数据值'>爷爷的jsx</Provider> // data就是爷爷传来的值 <Consumer>{ (data)=><span>孙子接收到的data</span> </Consumer>- 如果两个组件相隔层级比较多,可以使用Context实现组件通讯
- Context提供了两个组件:Provider 和 Consumer
- Provider组件: 用来提供数据
- Consumer组件: 用来消费数据
4.2 PropTypes校验传递值
import PropTypes from 'prop-types'
// 设置props每项的数据类型
Son.propTypes = {
list:PropTypes.array, // 为数组
delete:PropTypes.func, // 函数
a:PropTypes.string.isRequired // 是字符串切必填
}
// 如何没传使用默认值
Son.defaultProps = {
a: '默认值'
}
export default Son;
4.3 函数式编程
- 函数式编程让我们的代码更清晰,每个功能都是一个函数。
- 函数式编程为我们的代码测试代理了极大的方便,更容易实现前端自动化测试。
React框架也是函数式编程,所以说优势在大型多人开发的项目中会更加明显,让配合和交流都得心应手。
4.4 ref的使用方法
// ref可以挂载到类组件上也可以是dom元素上
<input ref={(item)=>{this.myinput=item}}>
// 这样访问this.myinput就相当于拿到了input元素
// 第二种使用
constructor(props) {
super(props);
this.refInput = React.createRef()
}
render() {
return ( <div>
<input ref={this.refInput} />
</div> );
}
4.5 有状态组件和无状态组件
- 函数组件又叫做无状态组件,类组件又叫做 有状态组件
- 状态(state) 即数据
- 函数组件没有自己的状态,只负责数据展示
- 类组件有自己的状态,负责更新UI,让页面动起来
// 函数组件
import React from 'react';
export default (props) => {
return <div></div>
}
// 类组件
import React, { Component } from 'react';
class s1 extends Component {
constructor(props) {
super(props);
this.state = { }
}
render() {
return ( <div></div> );
}
}
export default s1;
4.6 setState()修改state
- 状态是可变的
setState()更新数据是异步的- 注意:使用该语法,后面的
setState不要依赖前面setState的值 - 多次调用
setState,只会触发一次render - 语法:this.setState()
- 注意:不要直接修改state中的值,这是错误的
- setState() 作用:1.修改 state 2.更新UI
// 参数一:{属性名:属性值}
// 参数一:(state,props) => {return {属性名:属性值} } 最新的state和props
this.setState(参数一,()=>{
//setState是一个异步函数 回调函数会在数据更新后执行
})
4.7 this的指向
// 方法一:箭头函数
方法名 = ()=> {}
// 方法二:bind
constructor(props) {
super(props);
this.state = {}
this.方法名 = this.方法名.bind(this)
}
5.生命周期
constructor:是es6的语法,可算可不算生命周期
挂载阶段:render、componentDidMount
更新阶段:shouldComponentUpdate、render、componentDidUpdate
卸载阶段:componentWillUnmount
React.PureComponent替换React.Component是纯组件 默认调用shouldComponentUpdate 数据更新return为true
旧版生命周期如下:

新版生命周期如下:

6.react-router路由
6.1 基本使用
Router组件:包裹整个应用,一个React应用只需要使用一次- 两种常用的Router: HashRouter和BrowserRouter
- HashRouter: 使用URL的哈希值实现 (localhost:3000/#/home)
- 推荐 BrowserRouter:使用H5的history API实现(localhost3000/home)
Link组件:用于指定导航链接(a标签)- 最终Link会编译成a标签,而to属性会被编译成 a标签的href属性
Route组件:指定路由展示组件相关信息- path属性:路由规则,这里需要跟Link组件里面to属性的值一致
- component属性:展示的组件
- Route写在哪,渲染出来的组件就在哪
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
import Index from './pages/Index'
import List from './pages/List'
import Dynamic from './pages/Dynamic'
class AppRouter extends Component {
render() {
return (
<Router>
<ul>
<li><Link to='/'>首页</Link></li>
<li><Link to='/list'>列表</Link></li>
<li><Link to='/dynamic/123'>动态列表</Link></li>
</ul>
<!-- exact 精准匹配 默认是模糊匹配 /login也能匹配上 -->
<Route path='/' exact component={Index}></Route>
<Route path='/list' component={List}></Route>
<!-- this.porps.match.params.id 获取动态参数的值 -->
<Route path='/dynamic/:id' component={Dynamic}></Route>
</Router>
);
}
}
export default AppRouter;
6.2 编程式导航
- **编程式导航:**通过JS代码来实现页面跳转
- history是React路由提供的,用于获取浏览器历史记录的相关信息
- **push(path):**跳转到某个页面,参数path表示要跳转的路径
- go(n):前进或后退功能,参数n表示前进或后退页面数量
this.props.history.push('/home') // 跳转到home路由
6.3 嵌套路由
嵌套路由:就是在一级路由的component组件里再写Route组件,二级路由的path的前缀必在一级路由的而基础上,且一级路由不能为exact匹配
6.4 自我封装鉴权路由
相当于vue的导航守卫
/* 鉴权路由 */
import React from 'react'
import { Route, Redirect } from 'react-router-dom'
import { isAuth } from '../../utils' // 自我封装是否登录的方法
/* 函数组件 */
function AuthRoute({ path, exact, children,Yemian }) {
return <Route
exact={exact}
path={path}
render={(props) => {
if (!isAuth()) {
return <Redirect to={{
pathname: '/login',
state:{
from: props.location // 记录当前页面
}
}}></Redirect>
}
return <Yemian {...props}></Yemian>
}}>
</Route>
}
export default AuthRoute
// 使用
<AuthRoute path='/路径' exact={true} Yemian={组件}></AuthRoute>
6.5 其他路由
-
Switch(了解就行)-> 只显示第一个可以匹配到的组件
<Switch> <Route/> <Route/> </Switch> -
重定向
<Route exact path="/" render={props => { // Redirect 组件:是路由的重定向组件,通过 to 属性,来指定要重定向到的路由地址 return <Redirect to="/home" /> }} />
7. redux
7.1 redux
// store.js
import { createStore } from 'redux'
import reducer from './reducer'
export default createStore(
reducer,
// 为了redux_DevTools可以使用
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
// reducer.js
export default (state = { list: ['a', 'b', 'c'] }, action) => {
switch (action.type) {
case 'add':
let newState = JSON.parse(JSON.stringify(state));
newState.list.push(action.value)
// 返回必须是一个新对象, 否则可能不生效
return newState
default:
return state
}
}
获取数据是 store.getState() 调用方法是 store.dispatch(action) action是一个对象 {type:'',...}
// 组件使用
/*
获取数据是 store.getState()
调用方法是 store.dispatch(action) action是一个对象 {type:'',...}
*/
import stort from './store'
constructor(props) {
super(props);
this.state = store.getState()
store.subscribe(this.storeChange) // 订阅
}
// 订阅调用方法
storeChange= ()=>{
this.setState(store.getState())
}
7.2 异步处理
redux-thunk与redux-saga均是处理异步请求的,与vuex中的action功能类似
7.2.1 redux-thunk
// store.js
import { createStore, applyMiddleware, compose } from 'redux'
import reducer from './reducer'
import thunk from 'redux-thunk'
// 写个增强函数 为了两个函数都作为第二个参数一起执行
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose
const enhancer = composeEnhancers(applyMiddleware(thunk))
const store = createStore(
reducer,
enhancer
)
export default store
// 组件使用
// action 不是一个对象 是一个函数 异步方法在函数里执行
const fn = function () {
return (dispatch) => {
setTimeout(() => {
let action = {
type: 'async'
}
dispatch(action)
}, 1000)
}
}
store.dispatch(fn())
7.2.2 redux-saga
// store.js
import { createStore, applyMiddleware, compose } from 'redux'
import reducer from './reducer'
import mySaga from './sagas'
import createSagaMiddleware from 'redux-saga'
const sagaMiddleware = createSagaMiddleware()
// 写个增强函数 为了两个函数都作为第二个参数一起执行
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose
const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware))
const store = createStore(
reducer,
enhancer
)
sagaMiddleware.run(mySaga) // run函数开启
export default store
// sagas.js
import { takeEvery, put } from 'redux-saga/effects'
import axios from 'axios'
// generator函数
function* mySaga() {
yield takeEvery('async', 函数名); // 把async放入队列 用到时执行函数
}
function* 函数名() {
let res = yield axios.get('url地址')
const action = {type:'xx',value:res}
yield put(action)
}
export default mySaga;
// 普通组件
store.dispatch({type:'async'})
7.3 react-redux
react-redux是react特有的redux,依赖于react和redux
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
++ import { Provider } from 'react-redux'
++ import store from './store'
// 渲染的组件用Provider包裹
const App =
<Provider store={store}>
<普通组件 />
</Provider>
ReactDOM.render(App, document.getElementById('root'));
// 普通组件
import { connect } from 'react-redux'
// 把state存入props中
const mapStateToProps = (state)=>{
return {
参数名:state.字段名
}
}
// 把方法存入props中
const mapDispatchToProps = (dispatch)={
return {
方法名:()=>{
let action = {type:xx}
dispatch(action)
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(普通组件)
8.react局部样式
import styles from './xx.module.css' // 引入css局部样式
// styles.样式名 获取到真正的样式名
xx.module.css // 必须这种格式起名 里面式样与css一样
9.React Hooks
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
9.1 简单使用
import React, { useState } from 'react'
function Example() {
// 设置一个count值和给count赋值的方法 默认值为0
const [count, setCount] = useState(0)
return (
<Router>
<p>{count}</p>
<button onClick={() => { setCount(count + 1) }}>点击添加</button>
)
}
export default Example
不要在循环,条件或嵌套函数中调用 Hook
不要在普通的 JavaScript 函数中调用 Hook。
9.2 useState
const [count, setCount] = useState(0)
// count 等价于 this.state={count:0}
// setCount 等价于 this.setState({count:xx})
// count => this.state.count
// setCount(1) => this.setState({count:1})
9.3 useEffect
componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合
import React, { useEffect } from 'react'
export default function (props) {
const [count, setCount] = useState(0)
useEffect(
() => {
console.log("useEffect执行了");
return () => {
console.log('useEffect离开了');
}
},
[count]
)
return (
<div></div>
)
}
- 当进入组件或修改数据时执行第一个函数参数
- 若不带第二个数组参数,则更改数据是先输出
离开再输出执行 - 若存在第二个数组参数,这表示数组的中项更新时才会输出
离开,空数组表示更新任何数据都不执行 - 当组件销毁时输出
离开
9.4 useContext
作用:上下文传参 父子传参
import React, { useState, createContext, useContext } from 'react'
// 1. createContext() 得到一个Context组件
const CountContext = createContext()
function Son() {
// 3. 子组件通过useContext(创建Context的组件)获取值
const count = useContext(CountContext)
return (<div>{count}</div>)
}
function Example() {
const [count, setCount] = useState(0)
return (
<div>
<p>{count}</p>
<button onClick={() => { setCount(count + 1) }}>点击添加</button>
// 2.要接受的子组件用 Context.Provider 包裹 属性value传值
<CountContext.Provider value={count}>
<Son></Son>
</CountContext.Provider>
</div>
)
}
export default Example
9.5 useReducer
import React, { useReducer } from 'react'
function Example() {
// useReducer: 第一个参数是reducer函数 第二个参数是初始值
// 返回结果数组:第一项是返回的return的值,第二项是要调用的dispatch
const [count, dispatch] = useReducer((state, action) => {
switch (action) {
case 'add':
return state + 1
case 'sub':
return state - 1
default:
return state
}
}, 0)
return (
<div>
<p>{count}</p>
<button onClick={()=>dispatch('sub')}>SUB</button>
<button onClick={()=>dispatch('add')}>Add</button>
</div>
)
}
export default Example
9.6 useContext与useReducer代替redux
请参考下列案例:点击按钮改变title颜色
Example.js
import React from 'react'
import Buttons from './Buttons'
import Title from './Title'
import { Color } from './color'
function Example() {
return (
<div>
<Color>
<Title></Title>
<Buttons></Buttons>
</Color>
</div>
)
}
export default Example
color.js
import React, { createContext, useReducer } from 'react'
export const ColorContext = createContext()
export const UPDATE_COLOR = 'update_color'
function reducer(state, action) {
switch (action.type) {
case UPDATE_COLOR:
return action.color
default:
return state
}
}
export const Color = (props) => {
const [color, dispatch] = useReducer(reducer, 'yellow')
return (
<ColorContext.Provider value={{ color, dispatch }}>
{props.children}
</ColorContext.Provider>
)
}
Title.js组件
import React, { useContext } from 'react'
import { ColorContext } from './color'
export default function () {
const { color } = useContext(ColorContext)
return (
<div>
<h2 style={{ color }}>我是标题</h2>
</div>
)
}
Buttons.js组件
import React, { useContext } from 'react'
import { UPDATE_COLOR, ColorContext } from './color'
export default function () {
const { dispatch } = useContext(ColorContext)
return (
<div>
<button onClick={()=>{dispatch({type:UPDATE_COLOR,color:'red'})}}>红色</button>
<button onClick={()=>{dispatch({type:UPDATE_COLOR,color:'green'})}}>绿色</button>
</div>
)
}
9.7 useMore
类似于:shouldComponentUpdate
性能优化:当父组件中的每一个state属性改变时,子组件的都会重新渲染,用useMore来提高性能
import React, { useState, useMemo } from 'react'
function Son({ age }) {
function a() {
console.log('a函数执行了');
return '123'
}
// useMemo:第一个函数return的函数是判断是否要执行的函数,
// 第二个数组参数是 只有这些值改变时才执行函数
// 返回值等于函数的返回值
const aValue = useMemo(() => a(), [age])
return (
<div>
我是子组件+{age}
</div>)
}
9.8 useRef
作用:绑定元素节点
import React, { useRef } from 'react'
function Example() {
const myRef = useRef()
return (
<>
<input ref={myRef} />
<button onClick={()=>{myRef.current.value='123'}}>添加123</button>
</>
)
}
export default Example
也可以用来保存数据:不绑定在标签上,可以在useEffect()里保存useState里的数据 保存在myRef.current
10.react社区产品
react-transition-group:react官方动画库