React高级玩家指南

3,364 阅读5分钟

深入JSX,首先知道JSX是React.createElement(component, props, ...children)的语法糖。

1 在JSX文件中可以使用.表达式

在jsx中,也可以使用.表达式来引入React组件,如果你在一个类型中声明了很多React组件,这样做会很方便引用,同时也避免命名的冲突。

const Common = {
  CardComponent(props){
    return <div>Card</div>
  }
}

function CardList(){
  return <Common.CardComponent color='red' />
}

2 运行时选择组件类型

在开发复杂业务时经常会碰到需要在运行时选择组件类型,比如通过prop来决定渲染哪个组件,要实现这样的功能需要注意,必须先要把它赋值给一个大写变量。

import { Notice, Activity,HomeWork } from './CardStyle';

const components = {
  notice: Notice,
  activity: Activity
};

function CardList(props) {
  // JSX 类型组件首字母必须大写
  const Specific = components[props.cardType];
  return <Specific story={props.data} />;
}

3 传递函数作为后代

通常在JSX中的js表达式会被当做字符串,React元素或者js变量。但是,可以利用props.children把任何数据传递给子组件。例如:

function CardList(props) {
  return <div>{
     props.list.map((i)=>
       props.children(i)
     )
  }</div>;
}

function Card() {
  return (
    <CardList numTimes={10}>
      {(item) => <div key={item.index}>This is item {item.val} in the list</div>}
    </CardList>
  );
}

这种用法可以减少代码量,也比较少见到 -.-

4. 善于使用Context

Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性。简单说就是,当你不想在组件树中通过逐层传递props或者state的方式来传递数据时,可以使用Context来实现跨层级的组件数据传递。

//Card.jsx
import React, { createContext } from 'react'
export default const ThemeContext = createContext('ReactContext');
function card() {
  return (
    <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

//Toolbar.jsx
import ThemedButton from './ThemedButton';
function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

//ThemedButton.jsx
import React, { useContext } from 'react'
import ThemeContext from './Card';

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme.background, color: theme.foreground }}>
      {theme}
    </button>  
  );
}

注意:useContext(MyContext) 只是让你能够读取 context 的值以及订阅 context 的变化。你仍然需要在上层组件树中使用 <MyContext.Provider> 来为下层组件提供 context。另外,即使祖先使用 React.memo 或 shouldComponentUpdate,也会在组件本身使用 useContext 时重新渲染。

结合useReducer, 在一定程度甚至可以代替React-redux。

5. 异常捕获边界(Error)

如果模块加载失败(如网络问题),它会触发一个错误。你可以通过异常捕获边界(Error boundaries)技术来处理这些情况,以显示良好的用户体验并管理恢复事宜。 下面例子中,MyErrorBoundary组件在 react.docschina.org/docs/error-…

import MyErrorBoundary from './MyErrorBoundary';
const OtherComponent = React.lazy(() => import('./OtherComponent'));

const MyComponent = () => (
    <MyErrorBoundary>
      <Suspense fallback={<div>Loading...</div>}>
        <OtherComponent />
      </Suspense>
    </MyErrorBoundary>
);

如果在 MyComponent 渲染完成后,包含 OtherComponent 的模块还没有被加载完成,我们可以使用加载指示器为此组件做优雅降级。这里我们使用 Suspense 组件来解决

6 性能优化

在内部,React使用几种巧妙的技术来最小化更新UI所需的昂贵的DOM操作的数量。对于许多应用程序,使用React将导致快速的用户界面,而无需进行大量工作来专门优化性能。不过,有几种方法可以加快React应用程序的速度。 通过安装React Developer Tools for Chrome 可以进行app的检查和极致优化。比如通过TimeLine查看代码的运行时间等。 官网地址:reactjs.org/docs/optimi…

最常见也是最终要的性能优化就是避免视图重复刷新

影响网页性能最大的因素是浏览器的重排(repaint)和重绘(reflow)。React的Virtual DOM就是尽可能地减少浏览器的重排和重绘。 从React渲染过程来看,如何防止不必要的渲染是解决问题的关键。

  1. 拆分组件为子组件,对组件做更细粒度的控制。保持纯净状态,可以让方法或组件更加专注(focus),体积更小(small),更独立(independent),更具有复用性(reusability)和可测试性(testability)。 这里有一个重要概念就是纯函数。纯函数非常方便进行方法级别的测试及重构,它可以让程序具有良好的扩展性及适应性。纯函数是函数式开发的基础。React组件本身就是纯函数,即传入指定props得到一定的Virtual DOM,整个过程都是可预测的。

  2. 使用react Hooks和自定义Hooks 例如将依赖传入useMemo,useCallback第二个参数,来避免依赖未改变带来的重复渲染。

  3. 懒加载 使用react新增apireact.lazy来做组件的懒加载,提高用户体验。

  import React, { Suspense } from 'react';

  const OtherComponent = React.lazy(() => import('./OtherComponent'));

  function MyComponent() {
    return (
      <div>
        <Suspense fallback={<div>Loading...</div>}>
          <OtherComponent />
        </Suspense>
      </div>
    );
  }

像渲染常规组件一样处理动态引入(的组件)。

这个代码将会在渲染组件时,自动导入包含 OtherComponent 组件的包。React.lazy 接受一个函数,这个函数需要动态调用 import()。它必须返回一个 Promise,该 Promise 需要 resolve 一个 defalut export 的 React 组件。

然后应在 Suspense 组件中渲染 lazy 组件,如此使得我们可以使用在等待加载 lazy 组件时做优雅降级(如 loading 指示器等)。

另外,也可以在打包方面做webpack的动态加载:传送门

  1. 运用immutablejs JavaScript中对象一般是可变的,因为使用引用赋值,新的对象的改变将影响原始对象。为了解决这个问题是使用深拷贝或者浅拷贝,但这样做又造成了CPU和内存的浪费。Immutable data很好地解决了这个问题。

Immutable data就是一旦创建,就不能再更改的数据。对Immutable对象进行修改、添加或删除操作,都会返回一个新的Immutable对象。Immutable实现的原理是持久化的数据结构。即使用旧数据创建新数据时,保证新旧数据同时可用且不变。同时为了避免深拷贝带来的性能损耗,Immutable使用了结构共享(structural sharing),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其他节点则进行共享。

以上内容都是在业务开发中遇到学习的,合理的运用技巧一方面可以提高开发效率,另一方面也可以使得代码整洁,结构清晰,有更好的建议或意见欢迎点赞留言评论,本文持续更新... 下期分享你不知道的硬核数据状态管理,欢迎关注~