一、基础知识
1、函数式编程两大核心理念
1)纯函数
输出结果只受输入参数的影响,执行过程是封闭的,不会对外界造成影响。
只要入参是一样的,输出的结果必然是一致的。
2)函数柯里化(高阶函数的一种特殊用法)
函数柯里化是一种将使用多个参数的函数转换成一系列只使用1个参数的函数的技术
优点:提高参数复用率,代码拓展性强
缺点:代码逻辑不太好捋
2、react Fiber
就是指react 16.x版本,最主要、日常用的比较多更新的特点:
1】废弃一些生命周期,新增生命周期getDerivedStateFromProps
2】react hooks
3】注意:
最新的react17,几乎没有更新任何新的功能,主要是替换底层代码,向下兼容
3、react设计理念
1】单向数据流,单向绑定(单向渲染)
2】虚拟dom
3】组件化
4、创建react应用
1】创建
脚手架工具 create-react-app,template 后面加上typescript即可创建基于 ts的项目
2】tsconfig.json
noImplicitAny: false //不需要显示声明变量的类型any
5、JSX
react将html和js进行耦合,就形成了jsx,本质上是一个对象(React.createElement()对象),它是一种基于 all in JS 的思想。
1】花括号声明变量(React会自动转义,防止XSS注入攻击)
2】可嵌入表达式
3】可指定子元素
4】命名约定使用小驼峰,比如className
注意:
a、
6、CSS in JS(JSS)
以对象的方式引入CSS;避免CSS样式全局污染
7、网络API请求
可以使用自带的fetch函数
8、react组件的生命周期
1)初始化:mounting
创建虚拟dom,渲染UI
1】constructor:用于初始化state
2】componentWillMount(废弃):初始化数据后,但是还未渲染DOM
3】render:渲染UI
4】componentDidMount:创建好dom元素,挂载进页面的时候调用,可以在此生命周期发起网络请求
2)更新:updating
更新虚拟dom,重新渲染UI
1】componentWillReceiveProps(废弃):在接受父组件改变后的props需要重新渲染组件时,使用这个会导致组件循环render的问题,目前使用这个代替:getDerivedStateFromProps(nextProps, prevState)
2】shouldComponentUpdate
state发生变化的时候触发的生命周期,默认返回true,允许渲染;若返回false时不会重新render
一般用于父组件发生更新的时候,如果子组件不需要更新,则false。
3】render:渲染UI
4】componentDidUpdate:组件更新后调用
3)销毁:unmounting
删除虚拟dom,移除UI
1】componentWillUnmount:组件销毁后调用
9、state 和 props
1】state
a、state是用于组件内部的数据传递,组件的私有属性
b、constructor构造函数唯一可以初始化state的地方
c、需要使用setState来进行绑定,注意这里的setState是异步操作(react在这里会把多次修改的动作合并为一次)
2】props
a、用于父组件向子组件传递数据
b、只读属性,单向数据流(只能父组件里面修改,子组件不可改)
注:
e.target是指事件发生的元素
e.currentTarget是指事件绑定的元素
3】单向绑定
与vue不同,react中的受控组件比如input/select要建立双向绑定需要setState和onChange结合使用
可以使用状态管理工具mbox的observable和autobind
注意:
a、忌讳浅拷贝
对于state里面的参数对象,千万不要用浅拷贝,要用深拷贝
b、异步问题
因为this.setState()是异步的,所以某种特殊情况下,为了避免异步带来的困扰,可以直接:
this.setState.xxx = xxxxx,这样直接赋值的话,就是同步了
10、setState是同步还是异步?
setState本身是同步的,但是基于react本身性能机制,对setState做了优化:react会将多个setState的调用合并为1个来执行,也就是说,当执行setState的时候,state中的数据并不会马上更新。这样的合并操作可以提高性能。
新版写法:利用es6,把对象包裹在函数里面,异步返回,对性能有所提升。
注:重绘
指的就是引起 React 的更新生命周期函数4个函数:
1】shouldComponentUpdate
被调用时this.state没有更新;如果返回了false,生命周期被中断,虽然不调用之后的函数了,但是state仍然会被更新
2】componentWillUpdate:被调用时this.state没有更新
3】render:被调用时this.state得到更新
4】componentDidUpdate:更新完成
11、事件
1)把JS原生事件变成驼峰写法,比如onchange => onChange
2)这里注意this的绑定,有以下几种做法
1】绑定到dom上面的事件使用箭头函数的写法
2】constructor里面额外使用bind进行绑定
优点:会节约性能上的损耗
缺点:每个事件的函数都需要单独绑定
3】使用状态管理工具mbox的autobind
12、组件传值
1)父传子
父:通过属性来传递,如content和index,将值传递进入即可
子:用this.props接收
2)子传父
父:先绑定父组件标签里面的属性和方法,比如命名为deleteItem。注意,依旧要绑定好this
子:通过this.props调用父组件的方法
3)this.props.children
它表示组件的所有子节点
13、react中的虚拟dom(JS对象)
1)真实的dom渲染很耗性能,但是JS对象之间的对比不损耗性能
2)render函数
render函数里面的dom不是真实dom,它实际上是通过调用React.createElement来生成一个虚拟dom(JS对象),然后虚拟dom再转换成真实的dom。比如:render(
3)diff算法
就是查找原始的虚拟dom和新的虚拟dom的差异,进行了同层比对
4)虚拟dom的好处
a、提升了性能
b、使得跨端应用得以实现(虚拟dom是js对象,可以直接映射到原生app相关对应的结构)
14、refs
React提供了Refs帮助开发者可以直接操作DOM节点,就像jquery时代一样,直接操作DOM。
const input = this.refs.myInput;
const inputValue = input.value;
二、react hook
Hook 是 React16.8 的新增特性。
React Native 从 0.59 版本开始支持 Hook。
它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
1、React.FC<>
React.FC<>的在typescript使用的一个泛型,FC就是Function Component的缩写,是函数组件,在这个泛型里面可以使用useState
2、什么是hooks
1】本质上就是一种特殊的函数,hooks可以在不编写 class类组件 的情况下使用 state 以及其他的 React 特性,广泛应用于react的函数组件里面。Redux的作者就说过,能用React解决的问题就不用Redux。
2】类组件和函数组件
class类组件:
非常重,一个很小的功能模块的类组件,里面也包含一套自己的state(constructor构造函数)和完整的生命周期,多个父子组件相互调用,当state发生更新,render就会变得特别复杂。
函数组件:
非常轻量级和灵活,每个函数都是可复用的,但缺陷是函数是无状态的,所以需要用到state或者生命周期的时候,需要引入hooks。
3】命名:useXxx
3、有哪些hooks
1】useState
a、定义
纯函数组件没有状态,useState可以给函数组件里添加内部的 state
const [count, setCount] = useState(0);
第1个参数是state变量(getter),第2个参数是更新state的函数(setter)
b、多状态声明
比如现在我们要声明多个状态,有年龄(age)、性别(sex)和工作(work),可以这么写:
const [ age , setAge ] = useState(18)
const [ sex , setSex ] = useState(‘男’)
const [ work , setWork ] = useState(‘前端程序员’)
return (
JSPang 今年:{age}岁
性别:{sex}
工作是:{work}
</div>
)
在使用useState的时候只赋了初始值,并没有绑定任何的key,那React是怎么保证这三个useState找到它自己对应的state呢?
答案:React是根据useState出现的顺序来确定的。
注意:React Hooks不能出现在条件判断语句中,因为它必须有完全一样的渲染顺序。
2】useEffect 副作用钩子
副作用:与纯函数的概念相反,输入参数一样,而输出结果不确定,比如该函数涉及后端API请求,修改dom等操作。
Effect Hook 可以让你在函数组件中执行副作用操作,可用于取代生命周期函数componentDidMount、componentDidUpdate 和 componentWillUnmount
用法:
a、不传参数
useEffect(() => {
console.log(‘useEffect with no dependency’)
})
默认的行为,会每次 render 后都执行,一般表单控制中使用
类似于类组件中的componentDidMoount以及componentDidUpdate
b、空数组
传入第二个参数,每次 render 后比较数组的值没变化,不会在执行,类似于类组件中的 componentDidMount
useEffect(() => {
console.log(‘useEffect with empty dependency’)
}, [])
c、有一个或者多个值得数组
传入第二个参数,只有一个值,比较该值有变化就执行,
传入第二个参数,有2个值的数组,会比较每一个值,有一个不相等就执行
类似于类组件中的componentDidUpdate
useEffect(() => {
console.log(‘useEffect widh specify dependency’)
}, [state, props])
d、返回一个函数
返回时传递一个函数进行卸载,在组件卸载时候调用,类似于类组价中componentWillUnmout
useEffect(() => {
console.log(‘useEffect widh specify callback’);
return () => {
console.log(‘useEffect with specify callback’);
}
})
注意:
useEffect里面async/await不允许直接使用,可以在内部另外创建1个异步函数
3】useContext 上下文钩子
该钩子的作用是,在组件之间共享状态。其作用就是可以做状态的分发,在React16.X以后支持,避免了react逐层通过Props传递数据。
class 类组件是用 props 传值的,而函数组件没有了constructor构造函数也就没有了props 的接收,而useContext可以帮助我们跨越组件层级直接传递变量
a、与redux的异同
useContext:解决的是组件之间值传递的问题
redux:是应用中统一管理状态的问题
b、用法
const TestContext = React.createContext({});
父组件传递变量给子组件
子组件接受变量:
4】useReducer()
useReducer()为我们提供了状态管理。首先,关于redux我们都知道,其原理是我们通过用户在页面中发起action,从而通过 reducer方法 来改变state,从而实现页面和状态的通信。而Reducer的形式是(state, action) => newstate。类似,我们的useReducer()是这样的:
const [state, dispatch] = useReducer(reducer, initialState)
它接受reducer函数和状态的初始值作为参数,返回一个数组,其中第一项为当前的状态值,第二项为发送action的dispatch函数。
注意:
但useReducer无法为我们提供中间件等功能,加入你有这些需求,还是需要用到redux。
4、高阶组件 HOC
1】定义
高阶组件是一个函数(高阶函数),参数是一个原组件,返回值是一个经过改造的新组件
注意:高阶组件不是组件
2】高阶组件的好处
a、抽取重复代码,实现组件复用
b、条件渲染,控制组件的渲染逻辑(渲染劫持)
c、捕获/劫持被处理组件的生命周期
3】实际应用
a、mobx-react就是高阶组件是一个实际应用
@observer装饰器将组件包装为高阶组件,传入组件MyComponent后,mobx-react会对其生命周期进行各种处理,并通过调用forceUpdate来进行刷新实现最小粒度的渲染。
b、鉴权判断
1.页面按钮所在的组件 2.封装了鉴权逻辑的高阶组件 3.公共按钮组件
c、最常见的高阶函数就是Promise
三、react-router
1、react-router-dom
路由模式
3】
使用history模式,利用H5 API实现路由切换
4】
使用hash模式,利用原生JS中的window.location.hash来实现路由切换
2、路由跳转
1】
通常路径的跳转是使用Link组件,最终会被渲染成a元素;
to属性:Link中最重要的属性,用于设置跳转到的路径
2】
是的特定版本,会在匹配上当前的url的时候给已渲染的元素添加参数
a、activeStyle(object):当元素被选中时,为此元素添加样式
b、activeClassName(string):设置选中样式,默认值为active
c、exact(boolean):是否精准匹配。注意,若不添加exact属性的话,在"/about"的组件和"/profile"组件中也会渲染出"/"的组件。
3】history.push(url)
可用于手动跳转,同时要求该组件必须包裹在Router组件之内。
3、Switch组件
在路由没有加extra精确匹配的情况下,路由可能会匹配到多条url。如果是用Switch包裹住的Route,Switch匹配到第一个路由就不会继续匹配了。
4、Redirect组件
Redirect用于路由的重定向,它会执行跳转到对应的to路径中。
5、路由传递参数有三种方式
1】动态路由
path路径后面加:id
2】search传递参数
详情2
3】Link中to传入对象
<NavLink to={{
pathname: “/detail2”,
search:“?name=abc”,
state: id
}}>详情3
四、redux
1、定义
redux 用于数据状态管理,实现组件间的数据共享。redux的作者说过,只有遇到 React 实在解决不了的问题,你才需要 Redux。因为使用redux会增加很多复杂性因素。
2、应用场景
适用场景:多交互、多数据源。
1】某个组件的状态,需要共享(比如用户消息栏)
2】某个状态需要在任何地方都可以拿到
3】一个组件需要改变全局状态
4】一个组件需要改变另一个组件的状态
简单的场景不需要用到redux,因为使用redux会增加系统复杂性。
3、设计思想
Redux 的设计思想很简单,就两句话:
1)Web 应用是一个状态机,视图与状态是一一对应的。
2)所有的状态,保存在一个对象里面。
4、redux三个核心概念
1】store
Store管理着整个应用的状态,store可以理解为一个存储数据的仓库
Store提供了一个方法dispatch,这个就是用来发送一个动作(action),去修改Store里面的状态
store.dispatch({
type: ‘login’,
payload: ‘…’
});
然后还可以通过getState方法来重新获得最新的状态,也就是state。
2】action
Action是唯一可以改变状态的途径,它是一个对象。
const action = {
type: ‘login’,
payload: …,
…
};
3】Reducer
a、定义
Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。
b、用法
Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
当dispatch之后,getState的状态发生了改变,Reducer就是用来修改状态的。Action只是个数据的载体,用于告知Reducer发生了什么事情,真正搞事情的还得靠Reducer,在Reducer里更新Store里的state。Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
import { createStore } from ‘redux’;
const store = createStore(reducer);
onst actions = [
{ type: ‘ADD’, payload: 0 },
{ type: ‘ADD’, payload: 1 },
{ type: ‘ADD’, payload: 2 }
];
const total = actions.reduce(reducer, 0); // 3
c、注意点
Reducer 函数最重要的特征是,它是一个纯函数。也就是说,只要是同样的输入,必定得到同样的输出。
d、原理
略,将其引入到vuex身上,解释vuex即可。
4】store.subscribe()
Store 允许使用store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数。
import { createStore } from ‘redux’;
const store = createStore(reducer);
store.subscribe(listener);
5】redux与vuex的共同点和差异
a、相同点
1)都是通过Store来作为全局状态的存储;
2)不能直接修改Store中状态,(vuex中的mutation、redux中的reducer),只允许同步操作;
b、不同点
1)vuex自带module模块化功能,而redux没有;
2)vuex中消除了action概念;
3)vuex只适用于vue,而redux可以配合任何框架;
4)vuex中异步操作是在Action中进行的,而redux中没有为异步创建一个方法;
五、进阶知识
1、react 与 vue的差异
1】数据绑定
react是单向绑定:react 整体是函数式的思想,在 react 中是单向数据流,推崇结合 immutable 来实现数据不可变;
vue是双向绑定:vue 的思想是响应式的,也就是基于是数据可变的,通过对每1个属性建立 Watcher 来监听,当属性变化的时候,响应式的更新对应的虚拟 DOM。
2】虚拟dom的更新策略
vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。
对于react而言,每当应用的状态被改变时,全部组件都会重新渲染,所以react中会需要shouldComponentUpdate 这个生命周期函数方法来进行控制
3】模板渲染
react用jsx语法来编写,vue用自己独特的模板引擎把html+css+js结合起来
4】组件拓展
react组件的扩展一般是通过高阶组件HOC,而vue组件会使用mixin
5】内置语法糖
react内置的语法糖相对比较少,而vue相对比较多
6】应用方向
一般react用于编写大型的后台管理系统,vue用于简单业务型应用的快速开发
2、哪些方法会触发 React 重新渲染?
1】setState方法被调用
通常情况下,执行 setState 会触发 render。但是,执行 setState 的时候不一定会重新渲染。当 setState 传入 null 时,并不会触发 render。
2】父组件重新渲染
只要父组件重新渲染了,即使传入子组件的 props 未发生变化,那么子组件也会重新渲染,进而触发 render。
3、重新渲染 render 会做些什么?
1】会对新旧 VNode 进行对比,也就是我们所说的Diff算法。
2】对新旧两棵树进行一个深度优先遍历,这样每一个节点都会一个标记,在到深度遍历的时候,每遍历到一和个节点,就把该节点和新的节点树进行对比,如果有差异就放到一个对象里面
3】遍历差异对象,根据差异的类型,根据对应对规则更新VNode
4、如何判断什么时候重新渲染组件?
当React将要渲染组件时会执行shouldComponentUpdate方法来看它是否返回true(组件应该更新,也就是重新渲染)。所以需要重写shouldComponentUpdate方法让它根据情况返回true或者false来告诉React什么时候重新渲染什么时候跳过重新渲染。
Angular篇****
1】诞生于2009年,市面上最早的一套MVVM框架,目前由Google来进行维护。
2】目前最新的版本是Angular13 (2022/01/16)
3】特点
a、数据的双向绑定
这可能是其最激动人心的特性吧,view层的数据和model层的数据是双向绑定的,其中之一发生更改,另一方会随之变化,这不用你写任何代码!
b、代码模块化,每个模块的代码独立拥有自己的作用域,model,controller等。
c、强大的directive可以将很多功能封装成HTML的tag,属性或者注释等,这大大美化了HTML的结构,增强了可阅读性。
d、依赖注入
依赖注入(Dependency Injection,简称DI)是一种设计模式, 指某个对象依赖的其他对象无需手工创建,只需要“吼一嗓子”,则此对象在创建时,其依赖的对象由框架来自动创建并注入进来,其实就是最少知识法则;模块中所有的service和provider两类对象,都可以根据形参名称实现DI。
将这种后端语言的设计模式赋予前端代码,这意味着前端的代码可以提高重用性和灵活性,未来的模式可能将大量操作放在客户端,服务端只提供数据来源和其他客户端无法完成的操作。
e、测试驱动开发
angularjs一开始就以此为目标,使用angular开发的应用可以很容易地进行单元测试和端对端测试,这解决了传统的js代码难以测试和维护的缺陷。
4】与Vue、React对比
Angular、Vue、React都是基于用户体验和开发者体验演进出来技术方案,并没有什么优劣之分,各有各的长处。
a、Angular 约束多,擅长复杂中后台场景和多人协作。
b、Vue 灵活,适用于简单业务快速迭代(当然也有 Vue 做中后台的)。
c、React 组件化设计的好,可以实现比较好的组件生态进行复用。