React必需掌握主要知识点总结(面试专用)

536 阅读12分钟

前言

作为新人 今年八月 成功加入前端大家庭 其实网络上一大堆学习资料 可是很多资料都是长篇大论 于是我用自己话进行了总结 浅显易懂 本篇本章适用于有一定基础的同学

必备基础

React核心思想

MVC架构
  • 视图(View):用户界面。
  • 控制器(Controller):业务逻辑
  • 模型(Model):数据保存

image.png

函数式编程

www.ruanyifeng.com/blog/2012/0… 这里有详细介绍

image.png

jsx语法

把html代码直接写在js里 遇到  < >   的内容用html解析,遇到   {  }   的内容用js解析

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('example')
);
单向数据流

只要将一些服务端的数据和前端页面绑定好,开发者只关注实现业务就行了

虚拟DOM与Diff算法

虚拟Dom是用js对象来描述的节点 真实Dom的抽象,一些列的操作会映射在真实Dom
  • 用 JS 对象构建Dom树结构 当状态变更的时候 重新构造一个新的Dom树
  • 新的DOM树与旧的DOM树对比,记录两个树的差距,在应用到一个DOM上
  • 通过diff同层树节点比较逐步搜索遍历 时间复杂只有O(n),是一种高效的算法
Diff算法
  • 同级比较 key值比较 组件比较
  • 加入Fiber算法 将耗时长的任务碎片化 碎片化中间可执行其它任务
  • 减少了Dom节点跨层移动 对同一个层级的子节点 通过唯一的ID来区分
React的key值
  • 每一个key是唯一个标识 判断两个元素是否为同一个 没有修改时候 就会复用之前元素
  • key相同,组件有所变化,react会只更新组件对应变化的属性
  • key不同,组件会销毁之前的组件,将整个组件重新渲染

函数组件与类组件的区别

//函数组件
const About = () => {
  return (
    <>
      <h1>你好</h1>
    </>
  );
}
export default About;

//类组件
class About extends Component {
  render() {
    return (
      <>
        h1>你好</h1>
      </>
    );
  }
}

export default About;
  1. 函数组件没有this、生命周期、状态state、
  2. 函数组件可以接收props用户来做渲染 组件名必须大写
  3. 函数组件比类组件性能更高 所以尽量使用函数组件

使用CSS样式

开发者可以选择合适自己的进行开发

内嵌css样式
//对象里面使用后面需字符串 -字符需改小驼峰命名
const style = {
  color:'red',
  fontSize:18
}

const About = () => {
  return (
    <>
      <span style={style}>你好</span>
    </>
  );
}
外联样式 常用
//导入外包css文件
import "./css/style.css"

const About = () => {
  return (
    <>
      //使用className添加类名
      <span className="Text">你好</span>
    </>
  );
}

export default About;
使用条件控制css显示
//安装插件
//npm install --save classnames/bind

class About extends Component {
  constructor(props){
    super(props);
    this.state={  
      isActive:true
    }
  }
  //isActive值为false将不渲染
  render() {
    return (
      <>
          <span className=({"Text":this.state.isActive})>你好</span>
      </>
    );
  }
}

export default About;
Styled Components
  //安装插件
  //npm install --save styled-components
  
    const DivElement = styled.div`
    width:100px;
    height:100px;
    border: 1px solid tomato;
    、
    
  const About = () => {
  return (
    <>
      //直接使用 会有默认样式
      <DivElement>你好</DivElement>
    </>
  );
}

export default About;

事件处理

采用on+事件名的方式来绑定一个事件,注意,这里和原生的事件是有区别的,原生的事件全是小写onclick, React里的事件是驼峰onClickReact的事件并不是原生事件,而是合成事件

函数组件点击事件 常用

import React from 'react';

const style = {
  width:100,
  height:100
}
//输出事件对象
const handClick = (num:any)=>{
  console.log(num);

}
//注意react里面事件都会用{}括起来
const About = () => {
  return (
    <>
      <div style={style} onClick={handClick(8)}>你好</div>
    </>
  )}

export default About;

类组件事件处理传参 常用

 class About extends React.Component {
    constructor(props:any){
      super(props);
      this.state = {
        initNum:8
      }
      //修改this 使用箭头函数可不需要
      //this.handleClick = this.handleClick.bind(this);
    }
    handleClick(num){
    //通过setState改变值
      this.setState({
        initNum:num
      });
    }
    render() {
      return (
        <>
         <div style={style} onClick={()=>this.handleClick(10)}>你好--{this.state.initNum}</div>
        </>
      );
    }
  }

setState的参数

- 第一个参数
可以说return一个对象里面是state的响应式数据
也可以是个方法 该方法有两个参数(prevState,props)上一个state的结果与props
- 第二个参数
因为setState是异步,所以想要最新的数据,就有一个第二个参数,是一个回调函数,里面始终是在第一个参数之后执行
  handleClick(num:any){
      this.setState({
        initNum:num
      },()=>{
        console.log("这里永远是在修改值之后执行");
      });
    }
同步异步
  • 定时器 原生js 是同步
  • 合成事件 生命周期 是异步
React中setState()为什么是异步的

可以保证内部唯一性 虽然this.state会立即更新但是this.props不会 而且没有重新渲染父组件的情况下 我们不能立即更新this.props 因为会立即更新会放弃这批处理 会造成性能降低

props的用法

传值 重点
//父组件
const About = () => {
  return (
    <>
      <Abouts name="zhang"/>
    </>
  )}
  
//子组件
const Abouts = (props:any) => {
    return (
      <>
        <div>这是字组件{props.name}</div>
      </>
    );
  }
设置默认值与限定类型
//导入类型插件
import PropTypes from 'prop-types';

//父组件
const About = () => {
  return (
    <>
      <Abouts name="zhang"/>
    </>
  )}

  //子组件
  const Abouts = (props:any) => {
    return (
      <>
        <div>这是子组件{props.name}</div>
      </>
    );
  }
  
  //限定参数默认值
  Abouts.defaultProps = {
    name:"cong"
  }
  
  //限定参数默认类型
  Abouts.propTypes = {
    name: PropTypes.string
  }
插槽 重点

插槽工作中会经常用到 而且路由中也可以用到 后续会详细讲

const About = () => {
  return (
    <>
      <Abouts name="zhang">
        <span>这是插槽</span>
      </Abouts>
    </>
  )}

const Abouts:any = (props:any) => {
    return (
      <>
        <div>这是子组件{props.name}</div>
        {props.children}
      </>
    );
  }

受控组件与非受控组件

表单元素表单元素(如input、 textarea 和 select等)通常需要自己维护自己的state,并根据用户输入来更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用setState()来更新。 组件的渲染是否被调用者传递的props完全控制 控制则为受控 不受控则为非受控

一个简单的受控组件 推荐
  const Input = () => {
    const [info,setInfo] = useState([""]);
    const handleChange =(e:any)=>{
      setInfo(e.target.value);
    }
    return (
      <>
        <input type="text" value={info} onChange={handleChange} />
        <p>
          {info}
        </p>
      </>
    );
  }
一个简单的非受控组件
 class Inputs extends React.Component {
    constructor(props:any){
      super(props);
      this.handInput = this.handInput.bind(this);
      this.state = {
        name:""
      }
    }
    handInput(e:any){
      this.setState({
        name:e.target.value
      })
    }
    render() {
      return (
        <>
          <input type="text" onInput={this.handInput}/>
          {this.state.name}
        </>
      );
    }
  }
区别
  • 前者: 使用value = { this.state.xxx }绑定变量 onChange={ this.xxx }绑定方法
  • 后者: 解构出 createRef 接收返回值 ref = { this.返回值 } onInput = { this.xxx } 绑定事件 使用current.value来获取值

生命周期 重要

image.png 注意 代表16.3之前新增 代表16.3之后版本

  • 初始化阶

image.png

  • 更新阶段

image.png

  • 卸载阶段 componentWillUnmount ( ) : 卸载 清除定时器 事件监听 长连接

高级用法

react的依赖注入

依赖注入提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性

一个简单案例
import React,{createContext} from 'react';
//设置默认值
const {Provider,Consumer} = createContext("name");

//父组件
const selfDate = () => {
  return (
    <>
    //携带参数
     <Provider value="test">
        <SelfDates />
     </Provider>
    </>

  );
}

//子组件
const SelfDates = () => {
  return (
    <>
      <Consumer>
          //获取父组件参数
         {value=><p>{value}</p>}
      </Consumer>
    </>

  );
}

export default selfDate;

高阶组件

  • 与高阶函数类似 是一个函数 把组件当参数传入另一个组件 返回一个新的组件 配置后可以用@
  • 作用:组件复用、路由鉴权、Props
一个简单的高阶组件实现组件内容都增加HOC
import React from 'react';
const Hoc = (SelfDatess:any) => {
  //把组件直接返回
  const newPorps = {type:"HOC"}
  return class extends React.Component {
    render() {
      return <SelfDatess {...this.props} {...newPorps}/>;
    }
  };
}
//子组件一
const SelfDates = (props:any) => {
  return (
    <>
      <p>
        哈哈哈哈--{props.type}
      </p>
    </>

  );
}

//子组件二
const SelfDatesz = (props:any) => {
  return (
    <>
      <p>
        hhhh-{props.type}
      </p>
    </>

  );
}
//高阶函数的使用
export default Hoc(SelfDates);
export default Hoc(SelfDatesz);

更多用法参考 juejin.cn/post/684490…

Ref

React提供的这个ref属性,表示为对组件真正实例的引用,其实就是ReactDOM.render()返回的组件实例,ref可以挂载到组件上也可以是dom元素上。

  • 挂到组件(class声明的组件)上的ref表示对组件实例的引用。不能在函数式组件上使用 ref 属性,因为它们没有实例:
  • 挂载到dom元素上时表示具体的dom元素节点。
一个简单的获取Dom案例
class App extends Component {
  constructor() {
    super()
    // 创建inputRef
    this.inputRef = createRef()
  }
  
  //输出真实Dom
  componentDidMount () {
    console.log(this.inputRef.current) // <input type="text">
  }
  
  render () {
    return (
     <div>
        {/* 关联ref和dom */}
        <input type="text" ref={this.inputRef} />
      </div>
      )
  }
}

React-redux

需要使用Redux的项目:

  • 用户的使用方式复杂
  • 不同身份的用户有不同的使用方式(比如普通用户和管理员)
  • 多个用户之间可以协作
  • 与服务器大量交互,或者使用了WebSocket
  • View要从多个来源获取数据 从组件层面考虑,什么样子的需要Redux:
  • 某个组件的状态,需要共享
  • 某个状态需要在任何地方都可以拿到
  • 一个组件需要改变全局状态
  • 一个组件需要改变另一个组件的状态 Redux的使用的三大原则:
  • Single Source of Truth(唯一的数据源)
  • State is read-only(状态是只读的)
  • Changes are made with pure function(数据的改变必须通过纯函数完成)-
  • 纯函数: 如果相同的入参,那么一定是相同的返回值
  • 不能改变入参 Redux的基本流程:
graph TD
用户触发action动作包含数据 --> store通过dispatch调用reducer,并传入两个参数当前state与动作 --> reducer通过深拷贝state并返回新的state --> 使用subscribe订阅更新,重新渲染组件
具体实现(加减法案例)
store.js

先引入createStore 从 ”redux“ 与 reducer文件 const store = createStore ( reducer ) 暴露 store

import { createStore } from "redux";
import reducer from "./reducer";

const store = createStore(reducer);

export default store;
reducer.js

一个defaultState方法放数据、一个reducer(state = defaultState,action)方法 必须是纯函数

const defaultState = {
  count: 8,
};

const reducer = (state = defaultState, action) => {
  switch (action.type) {
    case "increment":
      return {
        ...state,
        count: state.count + action.num,
      };
    case "decrement":
      return {
        ...state,
        count: state.count - 1,
      };
    default:
      return state;
  }
};

export default reducer;

父组件

引入 store 使用 react-redux 包连接器 引入{ Provide } 放在组件最外层

// 从react里面解构出来的,大写一律是组件,小写的一律是方法
import React from "react";
import ReactDOM from "react-dom";
// react-redux是用来连接react和redux的

// 第一步是从里面解构出Provider
import { Provider } from "react-redux";
import App from "./13-react-redux/App";
import App from "./18-hooks/09-useLayoutEffect";


const render = () => {
  ReactDOM.render(
    <Provider store={store}>
        <App />,
    </Provider>, // 写成标签就是创建实例的另一种方式
    document.querySelector("#root")
  );
};
子组件
  1. 使用 react-redux 引入 { connect } 执行后返回一个高阶组件
  2. connect ( mapStateToProps , mapDispatchToProps ) ( 组件 )
  3. mapStateToProps = ( state ) => { xxx } 遍历所有的state,将所有的state转成当前props
  4. mapDispatchToProps = ( dispatch ) => { xxxx } 将action里面的方法绑定到当前组件
  5. 组件里面直接使用 { this.props.xxx }
import React, { Component } from "react";
import store from "./store/index";
import { connect } from "react-redux";

// 映射仓库的state到组件的props,它可以接收一个参数是state
const mapStateToProps = (state) => {
  return {
    count: state.count,
  };
};

// 映射仓库的方法到组件的props,它可以接收一个参数是dispatch
const mapDispatchToProps = (dispatch) => {
  return {
    add(num) {
      dispatch({ type: "increment", num });
    },
    jian() {
      dispatch({ type: "decrement" });
    },
  };
};

@connect(mapStateToProps, mapDispatchToProps)
class App extends Component {
  render() {
    return (
      <>
        <h3>redux</h3>
        <button onClick={this.props.jian}>-</button>
        <span>{this.props.count}</span>
        <button onClick={() => this.props.add(2)}>+</button>
      </>
    );
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(App);
export default App;

使用redux-saga
  1. 可以将异步acton与普通acton区分 相当于在Redux原有的数据流中增加一层
  2. 通过对Action进行监听 通过state驱动view的更新
  3. 使用Generator来处理异步

路由Router 重点

路由模式
  • BrowserRouter 最常用的路由模式
  • HashRouter 在路径前加入#号成为一个哈希值 刷新不会失去路径
  • MemoryRouter 路由过程保存在内存里,不能进行前进后退,因为地址栏没有发生任何变化
  • NativeRouter 适用于移动端
  • StaticRouter 服务端渲染时使用
详细介绍
import {
BrowserRouter as Router,//使用路由需套在组件最外层 可以直接写在index.js 里
Switch,//默认包含路由 加上变成排它路由 
Route, //两个参数 path 路径 与 component 组件 
Link//跳转 to (参数与path一样) 
withRouter, //是一个高阶组件 使没有路由信息的路由增加路由信息 Redirect, //路由重定向 **NavLink**//路由高亮可以代替Link } 
from "react-router-dom";

路由传参方式

params方式

用 { this.props.match.params?.id } 获取

也可以使用Hooks的useParams 直接获取

优点 刷新地址栏 参数不会丢失

search方式

用 this.props.location.search获取url带问号的参数

使用URLSearchParams可以是快速获取url参数

用query或state传值

通过this.props.location.state/query 缺点就是一刷新页面,参数就会丢失

路由重定向

Redirect 有两个参数 from ="原路径" to ="需重定向的目标路径"

阻止路由跳转
<Prompt when='true 或者 false’ 可控制组件是否跳转 >组件
渲染方式
  1. Route之后直接写组件
  2. Route参数component里面写组件 既可以时类组件也可以是函数组件 携带路由信息
  3. render属性 只能渲染函数组件 -可以在渲染前做一些逻辑判断
  4. children渲染 只能渲染函数组件 不管有没有匹配到都会渲染

Hooks 重点

路由里面的Hooks
  • Hooks是给函数组件用的,使函数组件拥有所有的类组件的功能
  • Hooks都是方法,必须写在函数组件的顶层
import {
useHistory //路由跳转 
useLocation //路由路径各种参数<link to={ xxx }> 里的信息 
useParams //路由参数信息 
useRouterMatch } 
from "react-router-dom";

使用URLSearchParams可以是快速获取url参数

组件里面的Hooks

image.png 现在阿里有一个开源的hooks非常好用 建议尝试上手 ahooks.js.org/zh-CN

一个组件调用接口的案例
import React, { useState, useEffect } from "react";

const UseEffect = () => {
 //两个变量num count
 const [count, set_count] = useState(5);
 const [num, set_num] = useState(100);
 
 //各自加一
 const fn = () => {
    set_count((count) => count + 1);
  };
 const fn2 = () => {
    set_num((num) => num + 1);
  };

//获取接口数据
 const getData = () => {
    return fetch(
      "https://www.fastmock.site/mock/15579798b9f988acd4d04ff978a2bd7c/api/list"
    )
      .then((response) => response.json())
      .then((res) => {
        return res;
      });
  };

  useEffect(() => {
    (async () => {
      const res = await getData();
      console.log(res);
    })();
  }, []);

  //渲染
  return (
    <>
      <h3>useEffect</h3>
      <p>{count}</p>
      <button onClick={fn}>btn</button>
      <p>{num}</p>
      <button onClick={fn2}>btn2</button>
    </>
  );
};

export default UseEffect;

适配移动端

设备像素比 = 设备像素( 垂直方向像素*水平方向像素 )/css像素(通过js获取window.devicePixelRatio)

淘宝flexible.js布局 (px的转换成rem)

blog.csdn.net/weixin_4313…

原理:将css中px编译为rem js根据不同手机型号计算出dpr的值 修改< meta >的viewport值和< html >的根font-size大小

React性能优化 面试必问

  • 利用shoudcomponentUpdate钩子来判断组件是否重新渲染
  • 给DOM加上唯一Key值,判断是否需要重新渲染,不要使用index
  • 能用const就用const
  • 组件里面传值少用箭头函数,可以使用柯里化解决传参问题
  • 减少对真实DOM的操作
  • 使用组件按需加载
  • 尽量使用函数组件
  • 组件懒加载

react与vue的区别

  • 架构思想
  • 组建后缀名
  • 都是使用虚拟DOM 通过Diff算法比较

总结

这是本人花了三天总结出来的文章,本以为能让读者简单快速理解 结果写着写着把自己都写晕了 其中还有很多东西没有讲到 最近我手上项目有开始了 所以很多知识点我都带过 如果想要更入了解建议跟着官网系统学一遍。最后在实际工作中react主要是后台管理系统居多 所以会与antd eChart这些比较搭 如果感兴趣 后面可能会出一篇文档 实际案例应用一下。附上官链接 react.docschina.org/