React初探

react首先是类似一个组件库的js文件,包含view和controller的库。

react组件根据平台本身可以映射成原生控件和web dom。

采用babel的编译工具将jsx转换成js来描述对应的元素。

 

Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。

一个 portal 的典型用例是当父组件有 overflow: hidden 或 z-index 样式时,但你需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框:

ReactDOM.createPortal(child, container)

 

Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用。

React.createElement() 会预先执行一些检查,以帮助你编写无错代码,但实际上它创建了一个这样的对象:

这些对象被称为 “React 元素”。它们描述了你希望在屏幕上看到的内容。React 通过读取这些对象,然后使用它们来构建 DOM 以及保持随时更新。

const element = {
  type: 'h1', props: { className: 'xxxClass', children: 'Hello, world!' } };

无状态组件:使用无状态函数构建的组件成为无状态组件,只传入props,context两个参数,不存在state,没有生命周期方法。无状态组件在调用时不会创建新实例,避免了不必要的检查和内存分配。

 

FancyButton 使用 React.forwardRef 来获取传递给它的 ref,然后转发到它渲染的 DOM button

const FancyButton = React.forwardRef((props, ref) => (  <button ref={ref} className="FancyButton">  {props.children}  </button> )); // 你可以直接获取 DOM button 的 ref: const ref = React.createRef(); <FancyButton ref={ref}>Click me!</FancyButton>;

 

Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。

还有一种新的短语法可用于声明它们,但尚未得到所有流行工具的支持。

 

React.PureComponent 来代替手写 shouldComponentUpdate。但它只进行浅比较,所以当 props 或者 state 某种程度是可变的话,浅比较会有遗漏,那你就不能使用它了。

 

React.memo 为高阶组件。它与 React.PureComponent 非常相似,但它适用于函数组件,但不适用于 class 组件。

如果你的函数组件在给定相同 props 的情况下渲染相同的结果,那么你可以通过将其包装在 React.memo 中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。这意味着在这种情况下,React 将跳过渲染组件的操作并直接复用最近一次渲染的结果。

默认情况下其只会对复杂对象做浅层对比,如果你想要控制对比过程,那么请将自定义的比较函数通过第二个参数传入来实现。

此方法仅作为性能优化的方式而存在。但请不要依赖它来“阻止”渲染,因为这会产生 bug。

与 class 组件中 shouldComponentUpdate() 方法不同的是,如果 props 相等,areEqual 会返回 true;如果 props 不相等,则返回 false。这与 shouldComponentUpdate 方法的返回值相反。

function MyComponent(props) { /* 使用 props 渲染 */ } function areEqual(prevProps, nextProps) { /* 如果把 nextProps 传入 render 方法的返回结果与 将 prevProps 传入 render 方法的返回结果一致则返回 true, 否则返回 false */ } export default React.memo(MyComponent, areEqual);

 

isValidElement()验证对象是否为 React 元素,返回值为 true 或 false

 

react-dom:findDOMNode(ReactComponent) 获取真正的DOM元素,返回该react组件对应的DOM节点。

 

react-dom:unstable_renderSubtreeIntoContainer: ReactMount._renderSubtreeIntoContainer(parentComponent,

nextElement, container, callback)。将目标元素插入指定节点container

render: ReactMount._renderSubtreeIntoContainer(null, nextElement, container, callback)。 两者的区别在于是否传入父节点。

 

React.cloneElement: 给指定组件传递props,以 element 元素为样板克隆并返回新的 React 元素。返回元素的 props 是将新的 props 与原始元素的 props 浅层合并后的结果。新的子元素将取代现有的子元素,而来自原始元素的 key和 ref 将被保留。

React.cloneElement(
  element,
  [props],
  [...children]
)

React.children: 获取当前组件的子组件

 

合成事件:

    事件委派:React并不会把事件处理函数直接绑定到 真实的节点上,而是把所有事件绑定到结构的最外层,使用一个统一的事件监听器,这个事件监 听器上维持了一个映射来保存所有组件内部的事件监听和处理函数。当组件挂载或卸载时,只是 在这个统一的事件监听器上插入或删除一些对象;当事件发生时,首先被这个统一的事件监听器 处理,然后在映射里找到真正的事件处理函数并调用。

   自动绑定:React 组件中,每个方法的上下文都会指向该组件的实例,即自动绑定 this 为当前组件。或者利用箭头函数自动获取当前this。

       事件捕获:会优先调用结构树最外层的元素上绑定的事件监听器,然后依 次向内调用,一直调用到目标元素上的事件监听器为止。

       事件冒泡:则与事件捕获的表现相反,它会从目标元素向外传播事件,由内而外直到最外层。

React的合成事件则并没有实现事件捕获,仅仅支持了事件冒泡机制。

React 事件是天生的事件代理,看起来事件散落在元素上,其实 React 仅仅在 根 元素绑定 事件,所有事件都通过事件代理响应。

 

非受控组件:一个表单组件没有 value props(单选按钮和复选框对应的是 checked prop) 时,就可以称为非受控组件。

      它是一种反模式,它的值不受组件自身的 state 或 props 控制。通常, 需要通过为其添加 ref prop 来访问渲染后的底层 DOM 元素。

 

classnames样式库:classNames({ 'btn': true, 'btn-pressed': this.state.isPressed, 'btn-over': !this.state.isPressed && this.state.isHovered,});

 

CSS Modules: 能最大化地结合现有 CSS 生态和 JavaScript 模块化能力,其 API 非常简洁。 发布时依旧编译出单独的 JavaScript 和 CSS 文件。

         现在,webpack css-loader 内置 CSS Modules 功能。

启用 CSS Modules 的代码如下:

// webpack.config.js

css?modules&localIdentName=[name]__[local]-[hash:base64:5]

加上 modules 即为启用,其中 localIdentName 是设置生成样式的命名规则。

使用了 CSS Modules 后,就相当于给每个 class 名外加了 :local,以此来实现样式的局部化。如果我们想切换到全局模式,可以使用 :global 包裹

对于样式复用,CSS Modules 只提供了唯一的方式来处理——composes 组合。

/* components/Button.css */ .base { /* 所有通用的样式 */ }

/* settings.css */.primary-color { color: #f40; }

.primary {composes: base; composes: $primary-color from './settings.css'; /* primary 其他样式 */}

如果不想频繁地输入 styles.**,可以使用 react-css-modules 库。它通过高阶组件的形式来 避免重复输入 styles.**。可以这么写---styleName="root"

使用 CSS Modules,容易使用 :global 去解决特殊情况,使用 react-css-modules 可写成 <div className="global-css" styleName="local-module"></div>,

这种形式轻松对应全局和局部;

 

跨级组件通信:

在子组件定义 static contextTypes = {color: PropTypes.string} ,通过this.context.color获取顶层组件的color属性

在顶层组件定义 static childContextTypes = {color: PropTypes.string},  实现方法 getChildContext() {return{color: 'red'}

 

没有嵌套关系的,那只能通过可以影响全局的一些机制去考虑。import { EventEmitter } from 'events';

在 componentDidMount 事件中,如果组件挂载完成,再订阅事件;当组件卸载的时候,在 componentWillUnmount 事件中取消事件的订阅。

 

对于广义的 mixin 方法,就是用赋值的方式将 mixin 对象里的方法都挂载到原对象上,来实 现对对象的混入。

 

React 在使用 createClass 构建组件时提供了 mixin 属性,mixins: ['xxx','xxx'],

在不同的 mixin 里实现两个名字一样的普通方法,这会造成冲突。因此, 在 React 中是不允许出现重名普通方法的 mixin。

如果是 React 生命周期定义的方法,则会将各个模块的生命周期方法叠加在一起顺序执行。

 

使用我们推荐的 ES6 classes 形式构建组件时,它并不支持 mixin。

对于实现 mixin 方法来说,这就没什么不一样了。但既然讲到了语法糖,就来讲讲另一个语 法糖 decorator,正巧可以用来实现 class 上的 mixin。

core-decorators 库为开发者提供了一些实用的 decorator,其中实现了我们正想要的 @mixin。 下面解读一下其核心实现:

function handleClass(target, mixins) {

  if (!mixins.length) { throw new SyntaxError(`@mixin() class ${target.name} requires at least one mixin as an argument`); }

  for (let i = 0, l = mixins.length; i < l; i++) {
    // 获取 mixins 的 attributes 对象
    const descs = getOwnPropertyDescriptors(mixins[i]);

    // 批量定义 mixins 的 attributes 对象

    for (const key in descs) {

      if (!(key in target.prototype)){

        defineProperty(target.prototype, key, descs[key]);

      }

    }

  }

}

源代码十分简单,它将每一个 mixin 对象的方法都叠加到 target 对象的原型上 以达到 mixin 的目的。这样,就可以用 @mixin 来做多个重用模块的叠加了。

这里用了getOwnPropertyDescriptor 和 defineProperty 这两个方法,

好处在于 defineProperty 这个方法,也就是定义与赋值的区别,定义是 对已有的定义,赋值则是覆盖已有的定义。所以说前者并不会覆盖已有方法,但后者会。

 

高阶组件:

  属性代理是常见高阶组件的实现方法

  const MyContainer = (WrappedComponent) =>

  class extends Component {---这里将Component替换成WrappedComponent就实现了反向继承,除了一些静态方法,包括生命周期,state,各种function,我们都可以得到。

我们同时可以以此进行hijack(劫持),也就是控制它的render函数。在render()中调用superRender(),然后通过在外层嵌套的方式改变原有渲染

    handleClick = () => {console.log('clicked');}

    render() {

      const otherProps = {handleClick:this.handleClick}
      return <WrappedComponent {...this.props}  ref={instanceComponent => this.instanceComponent = instanceComponent}/>;

    }

  }

  export default MyContainer//--->这是一个hoc组件

 

  class MyComponent extends Component { ... }

  export default MyContainer(MyComponent);

  高阶组件可以看做是装饰器模式(Decorator Pattern)在React的实现。即允许向一个现有的对象添加新的功能,同时又不改变其结构,属于包装模式(Wrapper Pattern)的一种

ES7中添加了一个decorator的属性,使用@符表示,上面一行可以改写成@MyContainer

  可以在hoc高阶组件中自定义事件,并通过props传递下去,在hoc高阶组件中使用ref,获取当前被包含组件的引用ref

 

 

Immutable

  React 做性能优化时最常用的就是 shouldComponentUpdate 方法,但它默 认返回 true,即始终会执行 render 方法,

然后做 Virtual DOM 比较,并得出是否需要做真实 DOM的更新,这里往往会带来很多没必要的渲染。我们也可以在 shouldComponentUpdate 

中使用深拷贝和深比较来避免无必要的 render, 但深拷贝和深比较一般都是非常昂贵的选择。

  Immutable.js则提供了简洁、高效的判断数据是否变化的方法,只需 === 和 is 比较就能知 道是否需要执行 render,而这个操作几乎零成本,所以可以极大提高性能。

  for (const key in nextProps) {

    if (nextProps.hasOwnProperty(key) &&

    !is(thisProps[key], nextProps[key])) { return true;}

   }

 

 

react生命周期:

主要通过 3 个阶段进行管理—— MOUNTING、RECEIVE_PROPS 和 UNMOUNTING。

当首次挂载组件时,按顺序执行 getDefaultProps、getInitialState、componentWillMount、render 和 componentDidMount。

当重新挂载组件时,此时按顺序执行 getInitialState、componentWillMount、render 和componentDidMount,但并不执行 getDefaultProps。

当再次渲染组件时,组件接受到更新状态,此时按顺序执行 componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render 和 componentDidUpdate。

当卸载组件时,执行 componentWillUnmount。

creatClass是创建自定义组件的入口方法,负责管理生命周期中的 getDefaultProps。该方法在整个生命周期中只执行一次,这样所有实例初始化的 props 将会被共享。

由于 getDefaultProps 是通过构造函数进行管理的,所以也是整个生命周期中最先开始执行 的。

在 componentWillMount 中调用 setState 方法,是不会触发 re-render的,而是会进行 state 合并,且 inst.state = this._processPendingState (inst.props, inst.context) 是在 componentWillMount 之后执行的,

因此 componentWillMount 中 的 this.state 并不是最新的,在 render 中才可以获取更新后的 this.state。

React 是利用更新队列 this._pendingStateQueue 以及更新状态 this._pendingReplace State 和 this._pendingForceUpdate 来实现 setState 的异步更新机制。

 

在 componentWillReceiveProps 中调 用 setState,是不会触发 re-render 的,而是会进行 state 合并。

且在 componentWillReceiveProps、shouldComponentUpdate 和 componentWillUpdate 中也还是无法获取到更新后的 this.state,

即此 时访问的 this.state 仍然是未更新的数据,需要设置 inst.state = nextState 后才可以,因此 只有在 render 和 componentDidUpdate 中才能获取到更新后的 this.state。

 

在 componentWillUnmount,则执行并重置所有相关参数、更新队列以及更新状态,如 果此时在 componentWillUnmount 中调用 setState,

是不会触发 re-render 的,这是因为所有更新 队列和更新状态都被重置为 null,并清除了公共类,完成了组件卸载操作。

 

react中的setstate:

React 利用状态队列机制实现了 setState的异步更新,避免频繁地重复更新 state。

当调用 setState 时,实际上会执行 enqueueSetState 方法,并对 partialState 以及_pendingStateQueue 更新队列进行合并操作,

最终通过 enqueueUpdate 执行 state 更新。

而 performUpdateIfNecessary 方法会获取 _pendingElement、_pendingStateQueue、_pendingForceUpdate,

并调用 receiveComponent 和 updateComponent 方法进行组件更新。

在 shouldComponentUpdate 或 componentWillUpdate 方 法 中 调 用 setState , 此 时this._pendingStateQueue != null,

则 performUpdateIfNecessary 方法就会调用 updateComponent方法进行组件更新,但 updateComponent 方法又会调用 

shouldComponentUpdate 和 componentWillUpdate 方法,因此造成循环调用,使得浏览器内存占满后崩溃。

 

setState 最终是通过 enqueueUpdate 执行 state 更新。

function enqueueUpdate(component) {

  ensureInjected();

  // 如果不处于批量更新模式
  if (!batchingStrategy.isBatchingUpdates) {

    batchingStrategy.batchedUpdates(enqueueUpdate, component);

    return;

  }

  // 如果处于批量更新模式,则将该组件保存在 dirtyComponents 中

  dirtyComponents.push(component);

}

setTimeout 里面调用setState的时候,把它丢到列队里,并没有去执行,而是先执行的 finally 主进程代码块,等 finally 执行完了, isBatchingUpdates 又变为了 false ,
导致最后去执行队列里的 setState 时候,表现就会和原生事件一样,可以同步拿到最新的state的值。
 

batchedUpdates: function(callback, a, b, c, d, e) {
  var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;

  ReactDefaultBatchingStrategy.isBatchingUpdates = true;

  if (alreadyBatchingUpdates) {

    callback(a, b, c, d, e);

  } else {
    transaction.perform(callback, null, a, b, c, d, e);----事务

  }

},

转载于:https://www.cnblogs.com/diyigechengxu/p/10792031.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值