微信号:QunarTL

介绍:Qunar技术沙龙是去哪儿网工程师小伙伴以及业界小伙伴们的学习交流平台.我们会分享Qunar和业界最前沿的热门技术趋势和话题;为中高端技术同学提供一个自由的技术交流和学习分享平台.

React16 新特征总览

2018-03-28 08:00 钟钦成

 点击上方蓝字关注我们 


钟钦成


钟钦成,网名为司徒正美,国内著名的前端专家,对浏览器兼容性问题/选择器引擎/react内部机制等具有深厚的积累,开发有avalon/anu等前端框架,著有《JavaScript框架设计》一书。


时代在不断进步,转眼间, fetch 有了 abort , Promise 有了 finally , class 有了静态、私有属性, 页面性能优手段从 requestAnimationFrame ,

InsterSectionObserver 到 requestIdleCallback 不断累积,JS 也着手支持 bigint 。React 也不是我们熟悉的 React,从 0.14 起,官方不知出了多少个最佳实践,然后不断打脸自己。因此我们是时候翻一翻官网或源码了,看看 React16 变成了什么样子。

React16 早期三大功能

1、组件支持返回数组。这是一个千呼万唤的功能,因此有的情况真的不能包一层,像 td , li 就不能包一个 div ,因为外面已经有一个 tr 或 ul .... 2、 React.ceatePortal , 俗称传送门。早期 React 生成的节点该挂在什么元素下,是不可改变的,于是有了 unstable_renderSubtreeIntoContainer 这样的怪异手段,害得官方迟迟不能废弃这个 API 。这个新 API 还有一个好处时,它的事件冒泡是沿着虚拟 DOM 进行,而不是真实 DOM ,这样会解决一些逻辑上的问题 3、 componentDidCatch错误边界,一个新生命周期钩子,其他钩子, render , ref ,组件 contructor 出错,它就会唤起。它会在 componentDidMount/Update 后执行。它的传参有一个详细的组件包嵌栈,清晰地告诉你哪个组件有问题,是在哪个方法中。有了它,不用你到处写 try catch 了。

React16.1~3的新功能

<Fragment/> 组件闪亮登场,这其实相当于一个数组,只是他们不想写数组间的逗号。加上 babel 加持的语法糖, <><span/><span/></> 实在不能更爽了。但这也开了一个坏头, babel 已经不能中立了。不过谁叫 babel 的作者被 facebook 收编了呢,肯定要为老板开个小灶。

compoentWillXxx 要退出历史,被废弃掉。多了一个叫 getDerivedStateFromProps 的静态方法代替 componentWillReceiveProps ,它会生成一个新 state ,方便让你 setState 。所有生命周期钩子(除了构造器)的 context 传参都被下岗了。另,还新增了一个恶心的 UNSAFE_xxxx 系列,现在只能靠源码推断,但是异步模式(自己手动改源码里面的开关),一大堆 BUG ,因此目前不用深究。

推出<StrictMode/> 组件,有点像 es6 的严格模式。因为多出了一些生命周期,内部肯定多跑一些代码,在严格模式下,声明被废弃的 compoentWillXxx 就不会被调用。相信以后有越来越多被废弃的方法会加入这个白名单中。

推出 newContextAPI context 是一种跨层级的数据传送方案,被 react-redux 与 react-router 所深度使用。但要使用 context 非常恶心,首先顶层,默认有一个空对象,作为顶级 context 往下传,传到某个子组件,它有一个 getChildContext 方法,那么它返回的对象与上层的 context 进行合并,继续往下。但这个子组件的子孙想用这个新 context (官方源码称之为 unmaskedContext),需要一些手续。就是在 type 里面定义 contextTypes ,然后根据 contextTypes 的键名再从这个新 context 扒出一个子集(官方源码称之为 maskedContext ) ,然后这个子子组件就可以高兴享用它了。但这个子子组件的孩子,子子子组件用什么 context 呢,什么也没有, unmaskedContext 只是路过它,但一条毛都没有给它,它只拿到一个空对象。在 React16 新架构中, context 受尽冷眼,首先它的搭档 PropTypes 已经打进冷宫,其次,内部许多方法不再接受 context 传参。fiber 架构通过 contextStack、beginWork、completeWork 等方法巧妙地解决 React15 低效的传输问题,并且也避开 shouldComponentUpdatereturnfalse 的意外。 createContext 会生成两个组件( Provider,Consumer),通过观察者模式建立关联,跳过中间 N 多组件,直接触发 Consumer 下方的组件更新。这其实有许多内容,以后有机会再分享详述。

 
           
  1. // https://github.com/RubyLouvre/anu/blob/master/test/modules/ReactNewContext-test.jsx

  2. // by司徒正美 迁移自官方测试

  3. it('simple mount and update', () => {

  4.    const Context = React.createContext(1);

  5.    function Consumer(props) {

  6.      return <Context.Consumer>{(value) => <span>{'Result: ' + value}</span>}</Context.Consumer>;

  7.    }

  8.    const Indirection = React.Fragment;

  9.    function App(props) {

  10.      return (

  11.        <Context.Provider value={props.value}>

  12.          <Indirection>

  13.            <Indirection>

  14.              <Consumer />

  15.            </Indirection>

  16.          </Indirection>

  17.        </Context.Provider>

  18.      );

  19.    }

  20.    ReactNoop.render(<App value={2} />);

  21.    ReactNoop.flush();

  22.    expect(ReactNoop.getChildren()).toEqual([span('Result: 2')]);

  23.    // Update

  24.    ReactNoop.render(<App value={3} />);

  25.    ReactNoop.flush();

  26.    expect(ReactNoop.getChildren()).toEqual([span('Result: 3')]);

  27. })

推出 createRef forwardRef (抄自 angular2), 这是解决 refs 对象的原罪。 React 会产生元素节点,但如果获得元素节点的引用是一个难题,于是推出了 string reffunction refstringref 有重大缺点,一个 div 需要知道是哪个组件 render 了自己,于是内部就有一个叫 currentOwner 的全局对象,每当组件实例化后,就把实例放到这上面,当下面的 div , span 在执行 React.createElement(div/span,props,...children) 时, currentOwner 会神不知鬼不觉到混进内部,作为 ReactElement 的第6个参数 _owner。React.createElement 只是 ReactElement 的外壳,一个加工厂, ReactElement 的返回值才是我们熟悉的虚拟 DOM 。但 currentOwner.current 会改来改去,并且针对一些恶心情况做了许多补丁。随着 React 以后会考虑 WebWorker 方式进行更新,这全局的东西肯定是障碍。于是有了 createRef ,返回一个 object ref直接能拿到引用,它能早于组件诞生,方便用户操作。 forwardRef 是用来指定 object ref 的活动范围。当然这东西与 HOC 也有关,这个有机会也再分享详述。总之, ref 与 context 一样,从组件中解耦出来。

 
           
  1. it('should forward a ref for multiple children', () => {

  2.        class Child extends React.Component {

  3.            render() {

  4.                ReactNoop.yield(this.props.value);

  5.                return null;

  6.            }

  7.        }

  8.        function Wrapper(props) {

  9.            return <Child {...props} ref={props.forwardedRef} />;

  10.        }

  11.        const RefForwardingComponent = React.forwardRef((props, ref) => (

  12.            <Wrapper {...props} forwardedRef={ref} />

  13.        ));

  14.        const ref = React.createRef();

  15.        ReactNoop.render(

  16.            <div>

  17.                <div />

  18.                <RefForwardingComponent ref={ref} value={123} />

  19.                <div />

  20.            </div>,

  21.        );

  22.        expect(ReactNoop.flush()).toEqual([123]);

  23.        expect(ref.current instanceof Child).toBe(true);

  24. });

createRoot、 <AsyncMode/>、flushSync、batchedUpdates、deferredUpdates 等涉及异步操作 API 依然在隐藏不露,这是改变 React 生态的核武。

React 一直屈服于 redux 的恐怖之下,一帮从小众语言转型过来的脑残粉在疯狂吹棒,一些从后端过来的小白大白跟着入局,整个前端界竟然捏着鼻子写着样板代码两年之久。。。虽然也出了许多补救方案,像 http 请求,包了一层又一层。我们需要量子通信这样简捷的方案。最终等来了,一下子来了三个。 newContextAPI , simple-cache-provider , reate-subscription ,尤其是后面两个,对 Promise 友好能直接结合 await 使用,内部与 fiber 架构的异步机制相关联。这里面的东西太多,本文也不展开了。

fiber架构,这涉及到节点的遍历方式, context 切换, requestIdleCallback 。

1、 fiber 架构首先在 Fiber 类型中开了一个口子,支持 N 多种 Fiber ,原 React 只有4种虚拟 DOM ,现在多达14种。

2、每个 Fiber 像 DOM 节点一样有一些关联相邻节点的属性,不再被动从上到下流动,通过 beginWork 与 completeWork 收集将要操作指令。

3、整个运作类似于 GIT ,并行多个分支,可提交可撤销。


结言

如此种种,要展开说实在太多。所有要求都指向一个目标, React 要从一个简单的视图库变成一个大而全的框架。太多选择则无从选择,官方为你准备最好的标配。当然有人担心 React16 这么大的改动,升级项目代码会不会挂掉。放心, React 是向前兼容做得最好的库,每一个废弃的 API 都有一个很长的过渡期。新的 API 是解决早期的痛点,并寻求更好的编程体验。

最后做一个广告,推荐一下我的 anujs ,一个迷你 React 框架,是市面上对  React16 兼容性最好的库。 React 的源码看不过来(由于是多人协作,代码比较混乱,变量名很长,处处是未完工的 todo 分支),可以看一下 anujs 的源码,提高自己的读码能力。高手与低手的区别在于读库能力与调试能力。读库能力有点像围棋,需要保持大局观与强大的预读能力,你能看到之后几步,九段能看到30步之外。当然也欢迎大家提 ISSUE 与 PR ,及在项目中使用 anujs 。 https://github.com/RubyLouvre/anu



 
Qunar技术沙龙 更多文章 AR不是科幻,但可能是旅游的不归路 【转载】什么是UTXO?浅谈比特币账户模型 【转载】Redis高负载下的中断优化 HTTPS 中间人攻击及其防范 {秀出你的主题}2018Qunar技术嘉年华主题征集正式启动
猜您喜欢 为小程序而生的小(jiao)手架 【推荐】R for Data Science 新书抢先看 使用Icinga2监控服务器带宽以及内存占用 大话Android 资源类型-动画 经典算法排行榜