react基础笔记

164 阅读12分钟

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

兄弟传值

  1. 一层一层传值

  2. redux/react-redux

  3. 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 函数式编程

  1. 函数式编程让我们的代码更清晰,每个功能都是一个函数。
  2. 函数式编程为我们的代码测试代理了极大的方便,更容易实现前端自动化测试。

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: HashRouterBrowserRouter
    • 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 其他路由

  1. Switch(了解就行)-> 只显示第一个可以匹配到的组件

    <Switch>
        <Route/>
        <Route/>
    </Switch>
    
  2. 重定向

    <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

componentDidMountcomponentDidUpdatecomponentWillUnmount 这三个函数的组合

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官方动画库