React - 基础笔记梳理

备战秋招,复习基础。若有错误,欢迎批评指正,共同进步!javascript

基本概念

声明式:建立用户交互界面,高效渲染页面。声明式编写UI方便调试。html

组件化:使用组件传递数据,将应用状态和DOM拆分开。当内部状态(this.state)改变时,组件会调用方法(render())从新渲染。java

箭头函数var func = (x,y) => {return x+y;};react

class → className for → htmlFor算法

样式 style须要{{}}express

JSX:很像XML的js语法扩展。可用{js表达式} → 没有if else,只有三元表达式redux

Key:每次构建动态列表的时候,指定一个合适的key。在同一级元素之间保证惟一!segmentfault

组件API:promise

  • setState 设置状态 每次调用时会自动更新子组件,但不是当即生效!
  • replaceState 替换状态
  • setProps 设置属性
  • replaceProps 替换属性
  • forceUpdate 强制更新
  • findDOMNode 获取DOM节点
  • isMounted 判断组件挂载状态

实现原理

参考资料:react基本原理及性能优化性能优化

react只负责解决view层的渲染

虚拟DOM

参考资料:浅谈React的最大亮点——虚拟DOM

虚拟DOM是在DOM的基础上创建了一个抽象层,是一个js对象。对数据和状态所作的任何改动,都会被自动且高效的同步到虚拟DOM,最后再批量同步到DOM中。

虚拟DOM能够确保只对界面上真正变化的部分进行实际的DOM操做。

真实的DOM tree结构:

<div id="container">
    <p>Real DOM</p>
    <ul>
        <li class="item">Item 1</li>
        <li class="item">Item 2</li>
        <li class="item">Item 3</li>
    </ul>
</div>
复制代码

虚拟DOM结构:

let virtualDomTree = CreateElement('div', { id: 'container' }, [
    CreateElement('p', {}, ['Virtual DOM']),
    CreateElement('ul', {}, [
        CreateElement('li', { class: 'item' }, ['Item 1']),
        CreateElement('li', { class: 'item' }, ['Item 2']),
        CreateElement('li', { class: 'item' }, ['Item 3']),
    ]),
]);

let root = virtualDomTree.render();   //转换为一个真正的dom结构或者dom fragment
document.getElementById('virtualDom').appendChild(root);


function CreateElement(tagName, props, children) {
    if (!(this instanceof CreateElement)) {
        return new CreateElement(tagName, props, children);
    }

    this.tagName = tagName;
    this.props = props || {};
    this.children = children || [];
    this.key = props ? props.key : undefined;
}


CreateElement.prototype.render = function() {
    let el = document.createElement(this.tagName);
    let props = this.props;

    for (let propName in props) {
        setAttr(el, propName, props[propName]);
    }

    this.children.forEach((child) => {
        let childEl = (child instanceof Element) ? child.render() : document.createTextNode(child);
        el.appendChild(childEl);
    });

    return el;
};
复制代码

diff算法

参考资料:React之diff算法

计算出Virtual DOM中真正变化的部分,并只针对该部分进行原生DOM操做,而非从新渲染整个页面。

  • react diff算法制定了三条策略,将算法复杂度从 O(n3)下降到O(n)。
  1. WebUI中DOM节点跨节点的操做特别少,能够忽略不计。
  2. 拥有相同类的组件会拥有类似的DOM结构。拥有不一样类的组件会生成不一样的DOM结构。
  3. 同一层级的子节点,能够根据惟一的ID来区分。
  • react diff实施的具体策略是:
  1. diff对树进行分层比较,只对比两棵树同级别的节点。跨层级移动节点,将会致使节点删除,从新插入,没法复用。

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

  3. 在对比同一层级的子节点时,diff算法会以新树的第一个子节点做为起点遍历新树,寻找旧树中与之相同的节点。若是节点存在,则移动位置。若是不存在,则新建一个节点。 在这过程当中,维护了一个字段lastIndex,这个字段表示已遍历的全部新树子节点在旧树中最大的index。在移动操做时,只有旧index小于lastIndex的才会移动。 这个顺序优化方案其实是基于一个假设,大部分的列表操做应该是保证列表基本有序的。 能够推倒倒序的状况下,子节点列表diff的算法复杂度为O(n2)

生命周期

react生命周期

  1. 初始化
  • getDefaultProps() 设置默认的props
  • getInitialState() 定义初始this.state
  • componentWillMount() 修改state的最后一次机会
  • render()建立一个虚拟DOM,表示组件的输出(惟一必须的方法)
  • componentDidMount()访问真实DOM,进行数据监听、数据请求
  1. 更新
  • componentWillReceiveProps() 可更新state,触发render
  • shouldComponentUpdate() 决定是否须要从新渲染,优化渲染性能
  • componentWillUpdate() 从新渲染前调用
  • componentDidUpdate() 访问并修改DOM,监听props或state变化。这里放setState必须加条件,不然会致使死循环。
  1. 销毁
  • componentWillUnmount() 将组件从DOM中卸载并销毁`

setState

  • 理想状况:

    setState是“异步”的,调用setState只会提交一次state修改到队列中,不会直接修改this.state。 等到知足必定条件时,react会合并队列中的全部修改,触发一次update流程,更新this.state。

    所以setState机制减小了update流程的触发次数,从而提升了性能。

    因为setState会触发update过程,所以在update过程当中必经的生命周期中调用setState会存在循环调用的风险。

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

  • 特殊状况:在实际开发中,setState的表现有时会不一样于理想状况。主要是如下两种。

    1. 在mount流程中调用setState:不会进入update流程,队列在mount时合并修改并render。

    2. 在setTimeout/Promise回调中调用setState:将不会进行队列的批更新,而是直接触发一次update流程。 这是因为setState的两种更新机制致使的,只有在批量更新模式中,才会是“异步”的。

  • setState会引起一次组件的更新过程,从而引起页面的从新绘制。主要会涉及如下几个生命周期函数:

    1. shouldComponentUpdate(被调用时this.state没有更新;若是返回了false,生命周期被中断,虽然不调用以后的函数了,可是state仍然会被更新)
    2. componentWillUpdate(被调用时this.state没有更新)
    3. render(被调用时this.state获得更新)
    4. componentDidUpdate

性能优化

减小diff算法触发

  1. 减小setState:主要在于非批更新阶段中(timeout/Promise回调),减小setState的触发次数。
  2. 父组件的render:会触发子组件进入update阶段(不管props是否更新)。此时最经常使用的优化方案即为shouldComponentUpdate方法。最多见的方式为进行this.props和this.state的浅比较来判断组件是否须要更新。或者直接使用PureComponent,原理一致。

合理diff

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

PureComponent

资料参考:详解在React.js中使用PureComponent的重要性和使用方式

资料参考:React 的 PureComponent Vs Component

资料参考:什么时候使用Component仍是PureComponent?

基本概念

PureComponent改变了生命周期方法 shouldComponentupdate,而且会自动检查组件是否须要从新渲染。这时,只有PureComponent检测到 state 或者 props 发生变化(引用类型的引用变化,或基本类型string number值变化)时,PureComponent才会调用 render 方法。

浅比较源码:

if (this._compositeType === CompositeTypes.PureClass) {
  shouldUpdate = !shallowEqual(prevProps, nextProps) || ! shallowEqual(inst.state, nextState);
}
复制代码

使用pureComponent至关于在component中优化检测:

-----这是component中的优化 在purecomponent中默认包含-----
shouldComponentUpdate(nextProps, nextState) {
    return (nextState.person !== this.state.person);
  }
复制代码

使用

class MyComponent extends PureComponent {...}
复制代码

若是一个纯组件(PureComponent)的 state 或 props 引用了一个新对象,那么这个组件就会被从新渲染(re-render)。

handleClick() {
 this.setState(prevState => ({
  words: prevState.items.concat(['new-item'])  ← 引用新对象 浅比较不一样
 }));
}
复制代码

使用PureComponent的最佳状况就是展现组件,它既没有子组件,也没有依赖应用的全局状态。

  • 父组件继承PureComponent,子组件继承Component时,若是渲染被父组件拦截,子组件也不会从新渲染。
  • 父组件继承Component,子组件继承PureComponent时:父组件会从新渲染,子组件不必定会从新渲染。

若是prop和state每次都会变,那么PureComponent的效率还不如Component!(由于还要浅比较)

不要在PureComponent中使用shouldComponentUpdate,由于根本没有必要~

一些操做

事件监听

<button onClick = { (e) => this.deleteRow(id,e) } Delete </button>
复制代码

等价于

<button onClick = { this.deleteRow.bind(this,id) } Delete </button>
复制代码

具体方法 调用中 e/this → e

deleteRow (id, e){...}
复制代码

判断执行

true && expression
复制代码

为真才执行,取代if条件渲染

React-router

参考资料:react-router的实现原理

react-router就是控制不一样的url渲染不一样的组件。react-router在history库的基础上,实现了URL与UI的同步。

  • 原理:DOM渲染完成以后,给window添加onhashchange事件监听页面hash的变化,而且在state属性中添加了route属性,表明当前页面的路由。

  • 具体步骤:

    1 当点击连接,页面hash改变时,触发绑定在 window 上的 onhashchange 事件;

    2 在 onhashchange 事件中改变组件的 state中的route属性,react组件的state属性改变时,自动从新渲染页面;

    3 页面随着 state 中的route属性改变,自动根据不一样的hash给Child变量赋值不一样的组件,进行渲染。

Redux

数据管理中心:单一数据源(全部state维护在一个根级store);状态只读(没法直接修改,严控修改执行);纯函数(只能用reducer描述修改)

Store:全局单例。

  • 方法:

    getState:获取state;

    dispatch:触发action,更新state;

    subcribe:订阅数据变动,注册监听器。

  • const store = createStroe(Reducer, initStore);

action:行为载体,映射响应Reducer。可成为数据载体,是store惟一数据源。

Reducer:修改行为的实质,用于描述如何修改数据的纯函数。

纯函数:对于相同的输入,永远会获得相同的输出,并且没有任何可观察的反作用,也不依赖外部环境的状态。
缘由:Redux只经过比较新旧两个对象的存储位置来比较新旧两个对象是否相同(浅比较)。若是你在reducer内部直接修改旧的state对象的属性值,那么新的state和旧的state将都指向同一个对象。所以Redux认为没有任何改变,返回的state将为旧的state。两个state相同的话,页面就不会从新渲染了。由于比较两个Javascript对象全部的属性是否相同的的惟一方法是对它们进行深比较。可是深比较在真实的应用当中代价昂贵,由于一般js的对象都很大,同时须要比较的次数不少。所以一个有效的解决方法是做出一个规定:不管什么时候发生变化时,开发者都要建立一个新的对象,而后将新对象传递出去。同时,当没有任何变化发生时,开发者发送回旧的对象。也就是说,新的对象表明新的state。
复制代码

处理异步:引入Redux-thunk或者redux-promise这种中间件,延迟事件的派发。

!!!!!!!!!!!!!写一个!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

React Hook

在函数定义组件中使用React特性

引入React到html中

layout.ftl 最初渲染页面

<html ...>
    <body>
        <div id = "app-container"></div>
        <script type = "text/javascript" ... ></script
    </body>
</html>
复制代码

client.js 配置redux的页面

在此引入 store  router
...
ReactDom.render(
    ...引入其余设置及组件
    document.getElementById('app-container');
);
复制代码
相关文章
相关标签/搜索