前言
作为新人 今年八月 成功加入前端大家庭 其实网络上一大堆学习资料 可是很多资料都是长篇大论 于是我用自己话进行了总结 浅显易懂
本篇本章适用于有一定基础的同学
必备基础
React核心思想
MVC架构
- 视图(View):用户界面。
- 控制器(Controller):业务逻辑
- 模型(Model):数据保存
函数式编程
www.ruanyifeng.com/blog/2012/0… 这里有详细介绍
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;
- 函数组件
没有this
、生命周期、状态state、 - 函数组件可以接收props用户来做渲染 组件名必须大写
- 函数组件比类组件性能更高 所以
尽量使用函数组件
使用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里的事件是驼峰onClick
,React的事件并不是原生事件,而是合成事件
函数组件点击事件 常用
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来获取值
生命周期 重要
注意
√
代表16.3之前新增
〇
代表16.3之后版本
初始化阶
更新阶段
卸载阶段
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")
);
};
子组件
- 使用 react-redux 引入 { connect } 执行后返回一个高阶组件
- connect ( mapStateToProps , mapDispatchToProps ) ( 组件 )
- mapStateToProps = ( state ) => { xxx } 遍历所有的state,将所有的state转成当前props
- mapDispatchToProps = ( dispatch ) => { xxxx } 将action里面的方法绑定到当前组件
- 组件里面直接使用 { 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
- 可以将异步acton与普通acton区分 相当于在Redux原有的数据流中增加一层
- 通过对Action进行监听 通过state驱动view的更新
- 使用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’ 可控制组件是否跳转 >组件
渲染方式
- Route之后直接写组件
- Route参数component里面写组件 既可以时类组件也可以是函数组件 携带路由信息
- render属性 只能渲染函数组件 -可以在渲染前做一些逻辑判断
- children渲染 只能渲染函数组件 不管有没有匹配到都会渲染
Hooks 重点
路由里面的Hooks
- Hooks是给函数组件用的,使函数组件拥有所有的类组件的功能
- Hooks都是方法,必须写在函数组件的顶层
import {
useHistory //路由跳转
useLocation //路由路径各种参数<link to={ xxx }> 里的信息
useParams //路由参数信息
useRouterMatch }
from "react-router-dom";
使用URLSearchParams可以是快速获取url参数
组件里面的Hooks
现在阿里有一个开源的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)
原理:将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/