React填坑

Virtual Dom

在浏览器环境下,DOM操作远比JS操作开销大,且项目越复杂,DOM操作的开销越大,为了提升DOM渲染的效率,需要遵循的原则是尽量减少DOM操作。于是Virtual Dom横空出世!
Virtual Dom顾名思义,是使用JS模拟DOM结构;并把DOM变化的对比,放在JS层来做,以此来提高重绘性能(将开销大的Dom操作转化为高效的JS操作)

<ul id='list'>
  <li class='item'>Item 1</li>
  <li class='item'>Item 2</li>
</ul>

//用JS模拟DOM如下:
{
  tag: 'ul',
  attrs: {
    id: 'list'
  },
  children: [
    {
      tag: 'li',
      attrs: {className: 'item'},
      children: ['Item 1']
    },
    {
      tag: 'li',
      attrs: {className: 'item'},
      children: ['Item 1']
    }
  ]
}

snabbdom

snabbdom为virtual dom的一个实现库,其核心API有2:
h():创建virtual dom
patch(vnode, mewnode):渲染前对比两个虚拟节点后并完成真实渲染

diff

virtual dom中(React、Vue)的diff算法起源于Linux的diff命令,用于比对文本的不同之处,是一个古老的命令。在virtual dom操作中,找出需要重新渲染的节点的过程,就是React中的diff算法。

//简略模拟diff:
function creatElement(vnode){
  var tag = vnode.tag
  var attrs = vnode.attrs || {}
  var children = vnode.children || {}
  if(!tag){
    return null
  }
  var elem = document.creatElement(tag)
  var attrName
  for (attrName in attrs) {
    if(attrs.hasOwnProperty(attrName)){
      elem.setAttribute(attrName, attrs[attrName])
    }
  }
  chlidren.forEach((childVnode) => {
    elem.appendChild(creatElement(childVnode))//递归
  })
  return elem
}
function updateChildren(Vnode, newVnode){
  var children = vnode.children || {}
  var newChildren = newVnode.children || {}
  children.forEach((childVnode, index) => {
    var newChildVnode = newChildren[index]
    if(childVnode.tag === newChildVnode.tag){
      updateChildren(childVnode, newChildVnode)
    } else {
      replace(childVnode, newChildVnode)
    }
  })
}

JSX

在React中负责VIew的编写,其本质上是语法糖,由于浏览器无法识别JSX,实际运行时由React库中的核心函数React.creatElement将JSX翻译成JS,再由JS渲染为HTML,类似于virtual dom经由h()转为真实节点一样。
React首次渲染时,调用patch(container, vnode)
通过setState()函数再次渲染时,调用patch(newVnode, vnode)

JSX自定义组件的解析过程

  1. React.creatElement(App, null)
    首先调用React的核心函数,传入自定义组件App
  2. var app = new App()
    Appclass,进行类的实例化
  3. return app.render()
    调用类实例的render()方法,并继续步骤1,直至没有自定义组件为止

setState()方法

  • 异步
    setState()方法是异步的,这是因为一次操作中,可能执行多次setState,根据浏览器单线程的特性和性能上的考量,无需每次执行setState的时候都重新渲染,只需进行最后一次渲染即可。
......
{
setState({a: 1})
setState({a: 2})
setState({a: 3})//只执行最后一次,前两次不执行
}
......
  • 过程
    1 每个React组件实例,均有继承自Component类的renderComponent方法。
    2 执行renderComponent方法会再次执行组件的render方法。
    3 render方法返回newVnode,同时系统缓存有preVnode的信息。
    4 执行patch(preVnode, newVnode)

React生命周期相关

组件生命周期

shouldComponentUpdate

React开发时,一个很奇妙的事情就是当stateprops未发生改变时,组件依然会重新渲染,所以当追求性能的时候,shouldComponentUpdate就派上了用场。shouldComponentUpdate生命周期函数是重渲染时render()函数调用前被调用的函数,它接受两个参数:nextPropsnextState,分别表示下一个props和下一个state。并且,当函数返回false时候,阻止接下来的render()的调用,阻止组件重渲染,而返回true时,组件照常重渲染。

//通过对比属性来确定组件是否更新
shouldComponentUpdate(nextProps,nextState){
  if(nextProps.numberObject.number == this.props.numberObject.number){
    return false
  }
  return true
}

一个警告

Warning: setState(...): Can only update a mounted or mounting 
component. This usually means you called setState() on an 
unmounted component. This is a no-op. Please check the code for 
the OrderTableList component.

在组件生命周期中,设立标志位来检验组件是否挂载,可规避此警告

componentDidMount(){
  this.mounted = true
  //some code
}
if(this.mounted){
  this.setState({})
}
componentWillUnmount(){
  this.mounted = false
}

react数据流

在并不复杂的webapp中,其实是可以不用redux的!

  • 父组件向子组件传递数据,类似如下的思路
<Father>
  <Son data={this.state.data}/>
</Father>
  • 子组件向父组件传递数据
<Father>
  <Son changeData={this.addData.bind(this)}/>
</Father>
//Father中有addData()操作数据,并将此方法向下传递下去(到Son)
addData(){
  //do sth
}
//Son中有方法负责传递数据,调用this.props上传递下来的方法处理,并将自身数据传入其中
do(){
  this.props.changeData(mydata)
}
  • 子组件与子组件间传递数据
    这里说的是有公共父组件的应用场景,即将子组件一的数据上传到父组件,经由父组件中转后将数据下发到子组件二
  • ref
    当父组件需要直接操作子组件时,即调用子组件方法,可以使用ref
<Father>
  <Son ref='loginAlert'/>
</Father>
~~~~~~~~~
~~~~~~~~~
this.refs.loginAlert.someFuc(this为Father组件上下文)

关于父组件更新而子组件不更新的问题

react中,当父组件通过props向子组件传递数据时,当父组件更新时,子组件会重新render,注意是重新render,子组件并不会完成卸载——加载过程,因此,constructor构造函数并不会再次执行,通过this.state语句自然不会传入props的值。这种情况下,子组件可以定义为“无状态”组件,直接使用this.props.data即可。

Immutable.js

var a = 'a'
var b = a
var b = 'b'
console.log(a) //b

修改引用型对象是一样不安全的行为

var a = 'a'
var b = Object.assign({}, a)
console.log(a === b) //false

可以使用ES6方法生成指向不同内存地址的新对象(concat(),...均可),可以使操作对象的行为变得相对安全,但是这种不断新开辟内存的行为会增大系统的开销,此时immutable.js隆重登场 ,其核心思想为,对象一经创建,其不会被改变;无论对其进行什么操作,都返回一个新对象;老对象始终不变,而新对象通过树形结构共享原则,既保留原来对象不变,又保留变化的这个部分,而因为每次变化仅为一部分,避免了深拷贝带来的性能损耗,同时可以进行任意时刻的数据回溯。

Redux

Redux数据流
Redux数据流
  • storeRedux的核心,其集成了reducers和一些中间件,Action发出动作后,集成了reducersstore随即发生状态变化,然后数据便会从根容器(Provider)向下传递;由于数据在全局有且只有一份(Store),因此在React应用中频繁修改state容易引起数据混乱,因此在Redux数据流中,需要使用Immutable Data
  • Redux数据流中,一个Action有一个或者多个Reducer与之对应,多个ReducercombineReducer包裹并传入Store
  • 容器组件和视图组件


    容器和视图组件
中间件
const logger = (store) => (next) => (action) => {
  //do something
} //科里化函数
构建store
import {creatStore, applyMiddleware} from 'redux'
import {fromJS} from 'immutable'
import thunk from 'redux-thunk'
import creatLogger from 'redux-logger'
import promiseMiddleware from 'redux-promise'

import reducers from './reducers'
const middlewares = [thunk , promiseMiddleware, creatLogger()]
function configerStore( initialState = fromJS({}){
  const store = creatStore(reducers(), initialState,  applyMiddleware(...middlewares))
  if(module.hot){
    module.hot.accept( () => {
      store.replaceReducer(require('./reducers').default) //热重载更新reducers
    })
  }
  return store
}
export default configerStore
connect

用于建立store和actions的连接,使reducers可以接收到发出的actions,从而改变store中的数据,即将action和reducer都绑定在一个实例上。connect()接受4个参数:

mapStateToProps #把store中的数据作为属性绑定到组件上,从而可以...this.props获取
mapDispatchToProps #方法的绑定,从而可以this.props获取action调用
mergeProps
options

Mobx

Mobx数据流相较Redux而言,概念更少。Mobx基于一份数据进行修改(Redux数据基于浅拷贝,每次更新都生成新的数据对象,但是由于浅拷贝和虚拟DOM的存在,对于整体性能的影响有限),因其对store数据的修改可以使用JS修改普通对象的方式,过程比较灵活(不严谨)。

const mobxStore = observable(
  count: 0,
  add: action(function(num){
    this.count += num
  })
)
mobxStore.add(1)
  • Mobx应用中用Class组织
  • Mobx重要概念
@observable
@action
@computed
autorun()//数据store更新时调用
  • 通过@inject('storeName')向组件注入数据
  • 通过@observer监听组件

React16新特性

  • rollup打包,体积小
  • 使用Fiber重写
  • 一些实用的功能:
  1. error boundary,捕获React渲染过程中出现的错误,通过新的钩子函数:componentIDidCatch
  2. New render return types,可以在Render方法中返回Array和String,也可以使用Fragment代替没有实际意义的div
  3. Portals,用于创建浮层或者Popup组件
  4. Better sever-side rendering

React中使用PropTypes进行类型检查

从React15.5起,React内置的类型检测库开始作为独立的存在,开发者可以通过‘prop-types’类型检测库捕获更多的bug,PropTypes 输出了一系列的验证器,可以用来确保接收到的参数是有效的。当给 props 传递了一个不正确的值时,JavaScript控制台将会显示一条警告。

import PropTypes from 'prop-types'

class A extends React.Component {
   render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

A.PropTypes = {
  name: PropTypes.string
}

同一组件无法重新渲染的解决方案

当使用路由切换时,不同的路由下对应的同一个组件 ,这时候React并不会重新渲染组件,此时只是传入的props有了一定的变化,这时需要利用React的钩子方法:componentWillReceiveProps,通过比对props,来完成新数据的获取,进而重新渲染组件。

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

推荐阅读更多精彩内容