Keep-alive是缓存路由使用的,保留之前路由的状态
实现方法:
使用npm库:
主要的api(生命周期):
注意事项:
动态加载(异步组件)加载时会有延迟,在延迟期间可以将一些内容展示给用户,比如:loading
(react16.6新增的API)
const resource = fetchProfileData();
function ProfilePage() {
return (
<Suspense fallback={<h1>Loading profile...</h1>}>
<ProfileDetails />
<Suspense fallback={<h1>Loading posts...</h1>}>
<ProfileTimeline />
</Suspense>
</Suspense>
);
}
function ProfileDetails() {
// 尝试读取用户信息,尽管该数据可能尚未加载
const user = resource.user.read();
return <h1>{user.name}</h1>;
}
function ProfileTimeline() {
// 尝试读取博文信息,尽管该部分数据可能尚未加载
const posts = resource.posts.read();
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.text}</li>
))}
</ul>
);
}
参考资料:
何为 Suspense? (opens new window)
只有当组件被加载时,对应的资源才会导入
主要api:
创建过程:
只要你能确保 context是可控的,合理使用,可以给react组件开发带来强大体验
结论:如果换行就有问题
原因:babel会将jsx语法编译成js,同时会在每行自动添加分号(;), 如果 return 换行了,那么就变成了return; 就会导致报错
//这样会报错,react
class App extends React.Component{
render(){
return
<div>111</div>
}
}
功能:给纯函数组件加上state,响应react的生命周期
优点:hoc的缺点render prop 都可以解决
缺点:
参考资料:
React 中的 Render Props (opens new window)
当props/states改变时,PureComponent会对它们进行浅比较,起到性能优化的作用; 相当于在component组件的shouldComponentUpdate方法中进行了比较才渲染
特别说明 :
JSX 使用js的形式来写html代码
jsx本身是语法糖,无法直接被浏览器解析,需要通过babel或者typescript来转换成 js。
许多包含预配置的工具,例如:create- react app 和 next.js 在其内部也引入了jsx转换。
旧的 JSX转换会把jsx 转换为 React.createElement调用。
jsx调用js本身的特性来动态创建UI,与于传统模式下的模板语法不同
react组件之前通讯主要要四种方式
容器组件:拥有自己的状态,生命周期
UI组件:只负责页面UI渲染,不具备任何逻辑,功能单一,通常是无状态组件,没有自己的state,生命周期。
react 生命周期主流的主要有2个大的版本;
一个是 v16.3之前的:
一个是v16.3之后的;
v16.3之前 的生命周期主要分为4个阶段,8个生命周期:
v16.3之后的生命周期:
新引入了两个生命周期:
提示3个不安全的生命周期(在v17中要被删除)
新的命令周期:
错误观念:componentWillMount中可以提前进行异步请求,避免白屏时间;
分析:componentWillMount比 componentDidMount相差不了多少微秒;
问题
目前官方推荐异步请求在 componentDidMount中
优点:
快速生成架手架
缺点:
Immutable是一种不同变的数据类型,数据一旦被创建,就不能更改的数据,每当对它进行修改,就会返回新的immutable对象,在做对象比较时,能提升性能;
实现原理:
immutable实现原理是持久化数据结构,结构共享,避免对数据对象进行深拷贝;
区别 | react | vue |
---|---|---|
模板引擎 | JSX,更多灵活,纯js语法(可以通过babel插件实现模板引擎) | vue template,指令,更加简单 (vue也可以使用jsx语法) |
复用 | Mixin->Hoc->render props->hook | Mixin-> slot->Hoc(比较用,模板套模板,奇怪做法)-> vue3.0 function baseed APi |
性能优化方式 | 手动优化:PureComponent/shouldComponentUpdate | 自动优化:精确监听数据变化 |
监听数据变化的实现原理 | 手动:通过比较引用的方式(diff) | 自动:getter/setter以及一些函数的劫持(当state特别多的时候,当watcher也会很多,导致卡顿) |
数据流 | 数据不可变,单向数据流,函数式编程思想 | 数据可变,双向数据绑定,(组件和DOM之间的),响应式设计 |
设计思想 | all in js (html->jsx, css->style-component/jss) | html,,css,js写在一个文件中,使用各自的方式 |
功能内置 | 少(交给社区,reactDom,propType) | 多(使用方便) |
优点 | 大型项目更合适 | 兼容老项目,上手简单 |
数据流对比:
react 数据更改逻辑:
vue数据更改逻辑:
参考资料:
Vue 和 React 的优点分别是什么? (opens new window)
个人理解Vue和React区别 (opens new window)
Vue与React的区别之我见 (opens new window)
基于抽象语法树AST,实现解析模板指令的插件(应该是实现一个babel插件,因为jsx解析成js语法,是通过babel解析的)
优点:
不足:
jsx以js为中心来写html代码
jsx语法特点:
优点:jsx类型安全的,在编译过程中就能发现错误;
定义:create-react-app是一个快速生成react项目的脚手架;
优点:
缺点:
如果一个函数,接受一个或多个函数作为参数或者返回一个函数,就可称之为高阶函数
特点:
eg: array 对象中的 map,filter,sort方法都是高阶函数
function add(x,y,f){
return f(x)+f(y)
}
let num=add(2,-2,Math.abs)
console.log(num);
高阶组件就是一个函数(react函数组件),接收一个组件,处理后返回的新组件 高阶组件是高阶函数的衍生 核心功能:实现抽象和可重用性
它的函数签名可以用类似hashell的伪代码表示
hocFactory:: W: React.Component => E: React.Component
高阶组件,不是真正意义上的组件,其实是一种模式;
可以对逻辑代码进行抽离,或者添加某个共用方法;
高阶组件是装饰器模式在react中的实现
主要用途:
参考资料:React 中的高阶组件及其应用场景 (opens new window)
一、静态方法丢失
二、refs属性不能透传
三、反向继承不能保证完整的子组件树被解析
react-redux :connect就是一个高阶组件,接收一个component,并返回一个新的componet,处理了监听store和后续的处理 ;
react-router :withrouter 为一个组件注入 history对象;
不谈场景的技术都是在耍流氓
参考资料:
React 中的高阶组件及其应用场景 (opens new window)
高阶组件可以重写传入组件的state,function,props;可以对代码逻辑进行抽离,重写 ;
父组件只是控制子组件的view层;
参考:
[译] 深入 React Hook 系统的原理 (opens new window)
hooks组件有生命周期,函数组件没有生命周期
hooks的生命周期其实就是:
//hooks模拟生命周期函数,与class的生命周期有什么区别
react hook是v16.8的新特性;
传统的纯函数组件,
react hooks设计目的,加强版的函数组件,完全不使用‘类’,就能写出一个全功能的组件,不能包含状态,也不支持生命周期), hook在无需修改组件结构的情况下复用状态逻辑;
优势:
缺点(坑):
参考资料: hooks中的坑,以及为什么? (opens new window)
react hook底层是基于链表(Array)实现,每次组件被render时,会按顺序执行所有hooks,因为底层是链表,每个hook的next指向下一个hook,所有不能在不同hooks调用中使用判断条件,因为if会导致顺序不正确,从而导致报错
//错误示例
function App(){
const [name,setName]=useState('xz');
//这里不能使用if判断
if(configState){
const [age,setAage]=useState('0')
}
}
一、如何在组件加载时发起异步任务
二、如何在组件交互时发起异步任务
三、其他陷阱
参考资料:
React Hooks 异步操作踩坑记 (opens new window)
hooks(本质是一类特殊的函数,可以为函数式注入一些特殊的功能)的主要api:
基础Hook:
额外的Hook:
demo:
//useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
//useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init);
参考资料: Hook API 索引 (opens new window)
useEffect可以让你在函数组件中执行副使用(数据获取,设置订阅,手动更改React组件中的DOM)操作
默认情况下每次函数加载完,都会执行(不要在此修改state,避免循环调用),useEffect相当于是class组件中的componentDidMount,componentDidUpdate,componentWillUnMount三个函数的组合
参数说明:
分类:
一)不需要清除
react更新DOM之后运行的一些额外代码,操作完成即可忽略
使用场景:
二)需要清除
effect返回一个函数,在清除时调用 (相当于class中componentWillUnmount生命周期)
由于添加/删除订阅代码的紧密性,所以useEffect设计在同一个地方,如果effect返回一个函数,react将会在执行清除时调用它
使用场景:
共享状态钩子,在组件之间共享状态,可以解决react 逐层通过props传递数据的问题
使用流程(使用流程和react-redux差不多):
const { useState, createContext, useContext } = React;
function App(){
//创建store
const AppContext=createContext({});
function A(){
//从store中取值
const {name}=useContext(AppContext);
return <div>A组件Name:{name}</div>
}
function B(){
//从store中取值
const {name}=useContext(AppContext);
return <div>B组件Name:{name}</div>
}
//在顶层包裹所有元素,注入到每个子组件中
return (
<AppContext.Provider value={{name:'xz'}}>
<A/>
<B/>
</AppContext.Provider>
)
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
参考资料:
React Hooks 常用钩子及基本原理 (opens new window)
action 钩子,提供了状态管理
实现过程(和redux差不多,但无法提供中间件等功能 ):
实现步骤:
useState
是一个内置的 React Hook。useState(0)
返回一个元组,其中第一个参数count
是计数器的当前状态,setCounter
提供更新计数器状态的方法。
...
const [count, setCounter] = useState(0);
const [moreStuff, setMoreStuff] = useState(...);
...
const setCount = () => {
setCounter(count + 1);
setMoreStuff(...);
...
};
react fiber 是一种基于浏览器的单线程调度算法
react算法的改变(Fiber实现的原理):
fiber: 一种将 recocilation(递归diff),拆分成无数据个小任务的算法;它随时能够停止,恢复。停止恢复的时机取决于当前的一帧(16ms)内,还有没有足够的时间允许计算
fiber是react16中新发布的特性;
解决的问题:
react在渲染过程时,从setState开始到渲染完成,中间过程是同步;
如果渲染的组件比较大,js执行会长时间占有主线程,会导致页面响应度变差,使得react在动画,手势等应用中效果比较差;
实现过程及原理(核心理念就是:time slicing):
react 主要提供了一种标准数据流的方式来更新视图;
但是页面某些场景是脱离数据流的,这个时候就可以使用 refs;
react refs 是用来获组件引用的,取后可以调用dom的方法;
使用场景
refs 注意事项:
不能在无状态组件中使用refs
Virtual DOM 是对 DOM的抽象,本质是js对象,这个对象就是更加轻量级对DOM的描述
优点:
可以通过chrome的console面板中的
参考资料:
虚拟DOM的创建
虚拟DOM是对真实DOM的抽象,根据不同的需求,可以做出不同的抽象,比较 snabbdom.js 的抽象方式
基本结构
/*
* 虚拟DOM 本质是一个JS对象,这个对象是更加轻量级的对DOM的描述
* */
//tagName,props,children
const element={
tagName:'ul',
props:{
id:'list'
},
children: [{
tagName:'li',
props:{
class:'item'
},
children:['item1'] //item1 只是没有包裹的子节点
}]
}
传统的页面更新,是直接操作dom来实现的,比如原生js或者jquery,但是这种方式性能开销比较大;
react 在初始化的时候会生成一个虚拟dom,每次更新视图会比较前后虚拟dom的区别;
这个比较方法就是diff算法,diff算法很早就已经出现了;但是react的diff算法有一个很大区别;
react diff 算法优势:
传递diff算法:
react diff 算法:
目前社区里的两个算法库:
react 算法 PK inferno.js
A:[a,b,c,d]
B:[d,a,b,c]
同层比较
参考资料:
diff 算法原理概述 (opens new window)
结论:有时表现出同步,有时表现出“异步“
表现场景:
react异步说明:
setState 异步并不是说内部代码由异步代码实现,其实本身执行过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前;在异步更新中,多次setState后面的值会覆盖前面的;
保持state,props.refs一致性;
react会对依据不同的调用源,给不同的 setState调用分配不同的优先级;
调用源包括:事件处理、网络请求、动画 ;
异步获取数据后,统一渲染页面;保持一致性,
一、事件注册
二、事件存储
{
onClick:{
fn1:()=>{}
fn2:()=>{}
},
onChange:{
fn3:()=>{}
fn4:()=>{}
}
}
三、事件触发/执行
事件执行顺序(原生事件与合成事件)
四、合成事件
参考资料:
【React深入】React事件机制 (opens new window)
语法区别:
react事件的优点
/*
* 执行结果:
* 1. dom child
* 2. dom parent
* 3. react child
* 4. react parent
* 5. dom document
* */
react事件与原生事件最好不要混用
原因:
stopProagation
方法,则会导致其他 react 事件失效,因为所有元素的事件将无法冒泡到 document上使用 重定向 Api : Redirect