关于前端:React-高级指引的学习笔记-3

41次阅读

共计 4662 个字符,预计需要花费 12 分钟才能阅读完成。

Profiler

该组件实现了简略的测试被包裹的组件渲染的工夫,次要是设置 id 属性和 onRender 函数返回相应的测试后果。其中 onRender 的返回值包含:

  • id: string – 产生提交的 Profiler 树的 id。如果有多个 profiler,它能用来分辨树的哪一部分产生了“提交”。
  • phase: "mount" | "update" – 判断是组件树的第一次装载引起的重渲染,还是由 props、state 或是 hooks 扭转引起的重渲染。
  • actualDuration: number – 本次更新在渲染 Profiler 和它的子代上破费的工夫。这个数值表明应用 memoization 之后能体现得多好。(例如 React.memouseMemoshouldComponentUpdate)。现实状况下,因为子代只会因特定的 prop 扭转而重渲染,因而这个值应该在第一次装载之后显著降落。
  • baseDuration: number – 在 Profiler 树中最近一次每一个组件 render 的持续时间。这个值预计了最差的渲染工夫。(例如当它是第一次加载或者组件树没有应用 memoization)。
  • startTime: number – 本次更新中 React 开始渲染的工夫戳。
  • commitTime: number – 本次更新中 React commit 阶段完结的工夫戳。在一次 commit 中这个值在所有的 profiler 之间是共享的,能够将它们按需分组。
  • interactions: Set – 当更新被制订时,“interactions”的汇合会被追踪。(例如当 render 或者 setState 被调用时)。

以上内容引自 react 官网

通过一个简略的 demo 来展示成果:

import React, {Component, Profiler} from "react";
import styles from "./styles.module.less";

class Test extends Component {constructor(props) {super(props);
    this.state = {};}

  componentDidMount() {console.log("test");
  }
  callback = (id, ...props) => {
    let params = {...props,};
    console.log(` 查看 ${id}的工夫 `, params);
  };
  render() {
    return (<div className={styles.contain}>
        <Profiler id="testPro" onRender={this.callback}>
          <button> 测试 </button>
        </Profiler>
      </div>
    );
  }
}

export default Test;

下面的 demo 用于测试 button 组件的渲染速度,控制台打印后后果如下:

不应用 ES6

尽管说大概率不可能有不便的 es6 不必,本人去写那一长串代码,然而还是看了一下根本的。

次要是创立组件的语法差别:

类型 es6 不应用 es6
class class Counter extends React.Component {} var Counter = createReactClass({});
state this.state getInitialState: function() {return {count: this.props.initialCount};}
bind this.handleClick = this.handleClick.bind(this) 或应用箭头函数 可能主动绑定
mixin 不反对 反对

不应用 JSX

前文曾经说过了,JSX 只是 react 的语法糖,让你更加容易的进行编程,如果是本人配置的 webpack,不想要更麻烦的配置 JSX 相干的内容,不应用 JSX 也是一样的,不过是须要将原先的 JSX 的写法改成 React.createElement() 函数创立的内容。

外面有一个较为简化的写法:

const e = React.createElement

ReactDOM.render(e('div', null, 'Hello World'),
  document.getElementById('root')
);

协调

这一节次要讲了 react 对于更新组件的 diffing 算法的设计决策,为了让程序更加精确的执行编写者的目标,对于一些性能的实现,局部参数的配置上,须要尽可能的 协调需要满足和性能耗费

Diffing 算法

  • 比照不同的根元素 :如果根元素不同,间接卸载原有的根元素,从新加载新的根元素。
    生命周期如下:
    componentWillUnmount():卸载组件,现有的 state 销毁
    componentWillMount():新的元素将要开始加载,新的 state 初始化
    componentDidMount():组件加载实现
  • 比照同一类型的元素:元素统一的状况下,之更改元素内属性,如果款式发生变化,之更新发生变化的那一项。
  • 比照同类型组件元素:因为类型统一,所以会保留组件实例,以保障该组件 state 渲染一致性,因而,外部生命周期只需调用更新的局部即可:
    componentWillReceiveProps():当 props 发生变化时执行,外部能够应用 this.setState() 来更新 state,此处调用更新不会触发额定的 render
    componentWillUpdate():当 propsstate 发生变化时执行, 除初始化 render 以外的 render 之后执行,外部不能够应用 this.setState(),调用完结后会将 nextPropsnextState 更新现有 this.propsthis.state

在生命周期后,对组件内元素吊柜调用之前的 diffing,实现整棵组件树的更新和渲染。

PS: 在这个算法中,如果你在现有元素中插入一个元素,会先卸载整个根元素在重构一个新的元素,这会造成较大的性能耗费,这大略也是 react 不倡议进行 dom 操作的起因之一

PS: 算法在不同类型之间切换的性能耗费要远高于雷同类型,因而当初输入类似的时候尽量应用同一类型的组件

Keys

当咱们在应用遍历数组渲染的时候,须要设置 key 值,然而应该尽量避免应用数组的下标作为 key 值输出。因为这样做的话,可能会导致可受控组件的内容在对数组进行插入,重排时显示异样的问题。

同时,如果咱们应用了诸如随机数的形式生成一个 key 值(比方应用 Math.random())并应用它时,每一次 render 都会触发卸载元素并重构组件的操作,这会造成许多不必要的性能耗费,以及一些子组件的状态失落问题。

Refs & DOM

前文说过了通过 Refs 转发来获取一般元素或组件元素的实例,以实现在数据流之外对 dom 进行批改(以上 refs 转发的办法仅反对 react16.3 以上版本)。

  • 实用 refs 的状况:

治理焦点,文本抉择或媒体播放。
触发强制动画。
集成第三方 DOM 库。
防止应用 refs 来做任何能够通过申明式实现来实现的事件。

  • 将子组件 DOM 裸露给父组件

react 不倡议做这样的操作,除非逼不得已
在 16.3 及以上版本中,能够应用 React.forwardRef 实现
在 16.3 以下或者想要更加灵便的应用 ref,能够应用一些别名的 props 向下传递 ref,比方:<Comp inputRef = {this.ref} />

回调 Refs

回调 refs 个人感觉像是在父组件中接管一个子组件的 ref 回调后果,该后果是依据子组件返回的值进行批改的,在子组件中,调用父组件的函数作为 ref,造成一个以函数为中间件的 ref 穿透,以下代码引自 https://zh-hans.reactjs.org/d…

function CustomTextInput(props) {
  return (
    <div>
 <input ref={props.inputRef} /> </div>
  );
}

class Parent extends React.Component {render() {
    return (
      <CustomTextInput
 inputRef={el => this.inputElement = el}      />
    );
  }
}

由上例能够看进去 CustomTextInput 组件中,调用 props.inputRef 函数作为 ref,将该值返回追传递给了父组件 Parent 组件,最终造成了父组件应用管制 this.inputElement 获取子组件 DOM 的目标。

须要留神的是,尽量不要把回调 ref 写成内联函数的款式,因为内联函数在更新的时候会调用两次,最好就是写成类的绑定函数:

 // 内联函数
<div test = {()=>{this.setState({ test:false})
}}>

// 类的绑定函数
constructor(props){this.state={ test:true}
    this.changeTest= this.changeTest.bind(this) 
}
// 或者用箭头函数
changeTest = () => {this.setState({ test:false})
}

Render Props

render prop 是一个用于告知组件须要渲染什么内容的函数 prop

就比方罕用的,咱们想要定义一个很难看的盒子,盒子外面可能装了很多不晓得的货色,然而盒子的外包装是统一的,这时候咱们就想要有一个组件,能够自定义接管的渲染内容,而外层不变。基于此,罕用的办法是:

class Box extend Component{render(){
        return (<div style={padding:2,mrgin:4,backgroundColor:'red'}>
                {this.props.children}
            </div>
        )
    }
}

下面的例子外面,咱们定义了一个红色底,内边框为 2px,外边框为 4px 的盒子,通过 this.props.children 来接管外界传入的渲染内容,而在外界通过间接包裹的形式传入:

<Box>{'test: input a props children'}</Box>

这就是一个简略的 Render Props, 在这个例子外面,咱们不须要定义一个叫 childrenprop 属性,是因为 react 默认了 children 属性的含意,除非你本人手动更改了他。此外,你也能够应用本人定义的属性,如:

<Test render={(test)=>{return <div> {test} <div>
}}

// 定义一个和上文一样的红盒子
function Test(props){
     return (<div style={padding:2,mrgin:4,backgroundColor:'red'}>
            {props.render}
        </div>
    )
}

另外,应用 Render Props 能够很不便的生成一个 HOC,你能够把盒子外面的渲染内容,动静的通过参数传入其中。

PS:在应用 React.PureComponent 生成组件的时候,可能会造成 Render Props 生效的问题,因为

React.PureComponent 中的 shouldComponentUpdate() 仅作对象的浅层比拟。如果对象中蕴含简单的数据结构,则有可能因为无奈查看深层的差异,产生谬误的比对后果。(引自 https://zh-hans.reactjs.org/d…)

这样的后果会使得 render 中每一个 props 都是不同的,这会毁坏应有的渲染成果。—- 然而我依照官网的例子测试了良久,没有看到异样的景象:( 后续找到差别再来补充填坑

正文完
 0