学习总结

React 原理

react做了什么?1Virtual Dom模型  2生命周期管理  3setState机制  4diff算法  5React patch、事件系统  6react的 Virtual Dom模型

virtual dom 实际上是对实际Dom的一个抽象,是一个js对象。react所有的表层操作实际上是在操作virtual dom。

经过diff算法会计算出virtual dom的差异,然后将这些差异进行实际的dom操作更新页面。

生命周期管理

挂载阶段:getDefaultProps getInitialState componentWillMount render componentDidMount

更新阶段:componentWillReceiveProps  shouldComponentUpdate   componentWillUpdate  render  componentDidUpdate

卸载阶段:componentDidUnMount

setState机制

理想情况:

setState是“异步”的,调用setState只会提交一次state修改到队列中,不会直接修改this.state。

等到满足一定条件时,react会合并队列中的所有修改,触发一次update流程,更新this.state。因此setState机制减少了update流程的触发次数,从而提高了性能。

由于setState会触发update过程,因此在update过程中必经的生命周期中调用setState会存在循环调用的风险。

另外用于监听state更新完成,可以使用setState方法的第二个参数,回调函数。在这个回调中读取this.state就是已经批量更新后的结果。

特殊情况:

在实际开发中,setState的表现有时会不同于理想情况。主要是以下两种。

在mount流程中调用setState。这种情况下,不会进入update流程,队列在mount时合并修改并render。

在setTimeout/Promise回调中调用setState。在种情况下,setState将不会进行队列的批更新,而是直接触发一次update流程。

这是由于setState的两种更新机制导致的,只有在批量更新模式中,才会是“异步”的。

diff算法

diff算法用于计算出两个virtual dom的差异,是react中开销最大的地方。

传统diff算法通过循环递归对比差异,算法复杂度为O(n3)。react diff算法制定了三条策略,将算法复杂度从 O(n3)降低到O(n)。

1.WebUI中DOM节点跨节点的操作特别少,可以忽略不计。

2.拥有相同类的组件会拥有相似的DOM结构。拥有不同类的组件会生成不同的DOM结构。

3.同一层级的子节点,可以根据唯一的ID来区分。

针对这三个策略,react diff实施的具体策略是:

diff对树进行分层比较,只对比两棵树同级别的节点。跨层级移动节点,将会导致节点删除,重新插入,无法复用。

diff对组件进行类比较,类相同的递归diff子节点,不同的直接销毁重建。diff对同一层级的子节点进行处理时,会根据key进行简要的复用。两棵树中存在相同key的节点时,只会移动节点。

另外,在对比同一层级的子节点时:diff算法会以新树的第一个子节点作为起点遍历新树,寻找旧树中与之相同的节点。如果节点存在,则移动位置。如果不存在,则新建一个节点。

在这过程中,维护了一个字段lastIndex,这个字段表示已遍历的所有新树子节点在旧树中最大的index。

在移动操作时,只有旧index小于lastIndex的才会移动。这个顺序优化方案实际上是基于一个假设,大部分的列表操作应该是保证列表基本有序的。

可以推倒倒序的情况下,子节点列表diff的算法复杂度为O(n2)

二、性能优化方案

由于react中性能主要耗费在于update阶段的diff算法,因此性能优化也主要针对diff算法。

1.减少diff算法触发次数  减少diff算法触发次数实际上就是减少update流程的次数。

正常进入update流程有三种方式:

1.setState

setState机制在正常运行时,由于批更新策略,已经降低了update过程的触发次数。

因此,setState优化主要在于非批更新阶段中(timeout/Promise回调),减少setState的触发次数。常见的业务场景即处理接口回调时,无论数据处理多么复杂,保证最后只调用一次setState。

2.父组件render

父组件的render必然会触发子组件进入update阶段(无论props是否更新)。此时最常用的优化方案即为shouldComponentUpdate方法。

最常见的方式为进行this.props和this.state的浅比较来判断组件是否需要更新。或者直接使用PureComponent,原理一致。

需要注意的是,父组件的render函数如果写的不规范,将会导致上述的策略失效。

forceUpdate 其中forceUpdate方法调用后将会直接进入componentWillUpdate阶段,无法拦截,因此在实际项目中应该弃用。

其他优化策略

shouldComponentUpdate 使用shouldComponentUpdate钩子,根据具体的业务状态,减少不必要的props变化导致的渲染。如一个不用于渲染的props导致的update。另外, 也要尽量避免在shouldComponentUpdate 中做一些比较复杂的操作, 比如超大数据的pick操作等。

合理设计state,不需要渲染的state,尽量使用实例成员变量。不需要渲染的props,合理使用context机制,或公共模块(比如一个单例服务)变量来替换。

2.正确使用diff算法

不使用跨层级移动节点的操作。对于条件渲染多个节点时,尽量采用隐藏等方式切换节点,而不是替换节点。尽量避免将后面的子节点移动到前面的操作,当节点数量较多时,会产生一定的性能问题。

redux 原理

引入redux的时候,会调用createStore 函数 有两个参数

最常用的是recucers 函数,enhancer :强化器,改进器

如果存在enhancer 把它作为参数在传进createStore 里面 把它变强;高阶函数eg:applyMiddleware中间件,可以吧crateStore变得更强

currentState :当前状态 用户存在这里的数据

currentListeners : 当前监听器

getState()  把store内部的属性全部返回出去 暴露出去

subscribe():用户期望 状态变更以后要执行的那些回调函数都放到 currentListeners数组中,将来在某个时刻统一去执行currentListeners的所有东西

dispatch(){currentState = reducer(currentState, action) }:

来执行真正的更新操作;执行以下reducer(更新状态的一个函数);根据action的指令来确定做什么样的更新;来返回全新的状态,覆盖之前的老状态,而不是更新,然后通知所有的currentListeners去做更新

dispatch一个动作,返回了一个 这样的接口。创建store以后可以通过这三个接口去调用做事情

enhancer 做了什么事情?applyMiddleware 可以传递若干中间件 有顺序,顺序是怎么一回事呢

中间件最终return 的还是一个函数

接收一个createStore 传进去的reducer 还要进行加工 创建store 还想做其他额外的事情就是中间件,想要在action 之前去做, 给中间件提供了它的api : getState  dispatch,除了可以做它自己的事情以外,还可以把状态拿出来让中间件可以获取当前状态,也可以派发动作,接着做下一步的事情,;然后做 了一个map操作,作用是希望 拿出中间件里的每个函数,执行一个中间件并且 呢把刚才暴露的接口api传进去,这样中间件里面就可以调用这些api了;把返回的数组称为中间件链,因为它产生了一条链,有先后顺序关系;使用compse (高阶)函数:作用简单理解就是 可以把一个数组按照顺序就像洋葱一样包裹,进行复核组合;最终生成一个函数,也就是由一个函数组成的数组变成一个函数,同时把store.dispatch方法作为参数传进去。最终返回的函数会强化一下store.dispatch,进行处理最终返回;

总之就是reducer 函数处理之前先去执行中间件的函数

compose 就是一个聚合的作用;如果有多个函数进行聚合操作 reduce()

react-redux原理

核心就是connect 函数 传进来两个函数的映射

connect 函数 本身返回一个函数,这个函数接收一个组件然后返回一个新组件;封装的这个组件是会被强化的,强化方式是怎样的?想办法吧store中的值拿出来,通过上下文的方式,隔代的把store拿出来,通过context 拿到状态,然后可以订阅更新,讲来如果有状态发生变化的时候,可以执行更新函数,然后让组件重新渲染;更新的时候可以把用户传递的这种映射函数,怎么能把store中的状态拿出来,然后映射到组件的属性中去,就是安装mapStateProps规则进行映射的,从store中拿出来映射,返回一个对象,这个对象就是我希望传递给要包装的组件它的对象;dispatchProps 也是一个对象,希望把那些actioncreater 创建的action 加上dispatch函数,一起组合成一个新函数,对象的属性的值是个函数,可以派发action,让组件变得更干净,然后做一个setState处理,就会更新函数,真正要包装的组件上就可以使用state props

provider就是做上下文的数据提供

redux thunk

action dispatch 的是对象,如果dispatch的是函数 则thunk在处理的时候 会特别处理,当发现action是函数的时候,把action执行一下,这时候这个函数就执行了,写异步的时候接受到的两个函数是 dispatch 和getState函数,在里面可以通过dispatch派发动作,还可以通过gestate拿到一些想要的状态,如果就是一个普通的对象,就会进去洋葱的下一层

Redux saga  和 redux thunk 区别

thunk 不遵循react本身 中间件可能是一个异步函数而不是纯函数

saga使用副作用 异步数据获取 浏览器缓存获取 易于管理、执行、测试和失败处理,基于generater,异步更强,解决更复杂的处理方案

从saga里面导出来三个方法:call 用来调用异步函数 put  异步函数得出结果去做状态更新的时候调用put  takEvery  负责全局监听action 截住后去执行这些异步事情

使用时注册的时候需要run 一下

umi

纯前端框架 开箱即用 内置react 、react-router

类 next.js且功能完备的路由约定,同时支持配置的路由方式

完善的插件体系,覆盖从源码到构建产物的每个生命周期

高性能,通过插件支持pwa,以路由为单元的code splitting 

支持静态页面导出,适配各种环境,比如中台业务,无线业务,egg,支付宝钱包,等

开发快速启动

一键兼容到 ie9

完善的typescript支持 包括d.ts 定义 和 umi test 

与dva 数据了深入融合 支持duck directory,model的自动加载,code splitting 等等

dva

redux saga太复杂了,每次都要重新配置,繁琐

原理:路由组件 通过dispatch 派发动作action ,去更新 还会有一些异步操作dva里面叫effect 副作用跟后台服务器去交互,得到的数据后去更新reducer,同步直接调用,异步先去effect 在回来调用reducer;subscription:特别的用户事件,比如键盘事件,监听,浏览器的地址变化之类的特殊事件,进行订阅,可以做一些特殊的事情派发dispatch 去做更新;当更新后  会把state状态通过 connect 函数去做更新

Hook


vuex 原理 状态管理单项数据流

用户通过触发dispach  action通过commit 触发 mutations 方法 改变state render模板渲染 生成数据流

action 如何触发mutations的一个时机

getters计算数学 和computed 一样的用法做数据加工的

computed需要数据继续加工计算

mapActions是一个数组 代替action 把组件中的事件和action里的事件做映射

modules文件夹存放不同状态


vue 原理?

NewVue  

$mount 初始化 

compile编译 : 1.parse(使用正则解析template中的vue指令v-xxx变量等等形成AST语法树);2. optimize(标记一些静态节点,用作后面的性能优化,在diff的时候直接略过);3. gererate (把第一步生产的AST转化为渲染函数render function)

 render function之后:1.virtual dom tree 虚拟树比较 进行不定;2.依赖收集   进行更新


数据响应式?

双向绑定原理defineProperty 属性把data里 每一个属性都定义了的get  set函数,有机会去监听这些

属性,当这些属性发生变化的时候,可以通知那些需要更新的地方进行更新;


vue 编译过程?

先说什么是编译,为什么要编译 首先vue模板的语句 html 根本就不识别,

通过编译的过程可以进行依赖收集,依赖收集以后,就data中的数据模型和视图之间产生了绑定关系,依赖关系,以后如果模型发生变化的时候,就可以通知这些依赖的地方,让他们进行更新,这就是执行编译的目的,我们把这些界面全部编译以后进行更新操作,就做到了模型驱动视图的变化,这就是编译过程


双向绑定的原理

通常使用v-modle 指令放在input这样的输入元素上,为什么放v-model ?编译的时候可以解析出v-model,在做操作的时候有两件事情,第一件事情 把当前 v-model所属的元素上面加了事件监听,v-model指定的回调函数作 为input事件监听的回调函数去监听,如果input发生变化的时候,就可以把最新的值设置到vue的实例上,因为vue的实例已经实现了数据的响应化,响应化的set 函数会触发,界面中所有模型的依赖的更新,会通知所有依赖去做更新和刷新操作,所以界面中跟这个部分相关的地方就更新了


webpack

总结:webpack 是⼀一个模块打包⼯工具,可以识别出引⼊入模块的语法 ,早起的webpack只是个js模块的 打包⼯工具,现在可以是css,png,vue的模块打包⼯工具

webpack 配置⽂文件 

当我们使⽤用npx webpack index.js时,表示的是使⽤用webpack处理理打包,名为index.js的⼊入⼝口模块。默 认放在当前⽬目录下的dist⽬目录,打包后的模块名称是main.js,当然我们也可以修改

webpack有默认的配置⽂文件,叫webpack.config.js,我们可以对这个⽂文件进行修改,进行个性化配

默认的配置⽂文件:webpack.config.js

npx webpack //执⾏行行命令后,webpack会找到默认的配置⽂文件,并使⽤用执⾏

不使⽤用默认的配置⽂文件: webpackconfig.js

npx webpack --config webpackconfig.js //指定webpack使⽤用webpackconfig.js⽂文件来作为 配置⽂件并执行行

修改package.json scripts字段:有过vue react开发经验的同学 习惯使⽤用npm run来启动,我们也 可以修改

"scripts":{ "bundle":"webpack"//这个地方不要添加npx ,因为npm run执行的命令,会优先使用项⽬工程里 的包,效果和npx非常类似 }

npm run bundle

Webpack 的核⼼心概念

entry:指定打包⼊入⼝口⽂文件

output: 打包后的⽂文件位置

loader

webpack是模块打包工具,⽽而模块不仅是js,还可以是css,图片或者其他格式

但是webpack默认只知道如何处理js模块,那么其他格式的模块处理,和处理⽅式就需要loader了

当webpack处理到不认识的模块时,需要在webpack中的module处进行配置,当检测到是什么格式的 模块,使用什么loader来处理。

Plugins :plugin 可以在webpack运行到某个阶段的时候,帮你做一些事情,类似于生命周期的概念;eg:htmlwebpackplugin会在打包结束后,⾃自动⽣生成⼀一个html⽂文件,并把打包⽣生成的js模块引⼊入到该html 中。

sourceMap: 源代码与打包后的代码的映射关系在dev模式中,默认开启,关闭的话 可以在配置文件里

WebpackDevServer :提升开发效率的利器每次改完代码都需要重新打包一次,打开浏览器,刷新一次,很麻烦,我们可以安装使用webpackdevserver来改善这块的体验启动服务后,会发现dist目录没有了,这是因为devServer把打包后的模块不会放在dist目录下,而是放 到内存中,从而提升速度

跨域:联调期间,前后端分离,直接获取数据会跨域,上线后我们使⽤用nginx转发,开发期间,webpack就可 以搞定 这件事修改webpack.config.js 设置服务器代理  proxy: {      "/api": {        target: "http://localhost:9092"      }    }

Hot Module Replacement (HMR:热模块替换)

Babel处理理ES6 :

npm i babel-loader @babel/core @babel/preset-env -D

//babel-loader是webpack 与 babel的通信桥梁梁,不会做把es6转成es5的工作,这部分工作需要用 到@babel/preset-env来做

//@babel/preset-env⾥里里包含了了es6转es5的转换规则

通过上⾯面的⼏几步 还不不够,Promise等⼀一些还有转换过来,这时候需要借助@babel/polyfill,把es的新特 性都装进来,来弥补低版本浏览器器中缺失的特性

@babel/polyfill

会发现打包的体积⼤了很多,这是因为polyfill默认会把所有特性注⼊入进来,假如我想我⽤用到的es6+,才 会注⼊入,没⽤用到的不注⼊入,从而减少打包的体积,可不可以呢?useBuiltIns:"usage" //按需注入

当我们开发的是组件库,⼯具库这些场景的时候,polyfill就不不适合了了,因为polyfill是注⼊入到全局变量量, window下的,会污染全局环境,所以推荐闭包⽅方式:@babel/plugin-transform-runtime

配置React打包环境 :npm install react react-dom --save  npm install --save-dev @babel/preset-react

treeShaking: webpack2.x开始支持 “”摇树“” 只支持es module 方式;没有用的东西(不需要引用的模块)摇掉,从而缩小打包的体积

code spliting 

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容