React 代码分割和 Context

一、代码分割

大多数 React 应用都会使用 webpack 这类的构建工具来打包文件,打包是一个将文件引入并合并到一个单独文件的过程,最终形成一个 “bundle”;接着在页面引入该 bundle,整个应用即可以一次性加载。

Webpack 支持代码分割,打包的时候可以创建多个包并在运行时动态加载。

对应用进行代码分割虽然不能帮助我们减少应用整体的代码体积,但是可以避免加载用户不需要的代码,并在初始加载的时候减少所需加载的代码量。能够帮助我们“懒加载” 当前用户所需要的内容,能够显著提高应用性能。

代码分割的方式:

  • import()(写法上不太友好)

使用前:

import { add } from './math';
console.log(add(16, 26));

使用后:

import("./math").then(math => {
  console.log(math.add(16, 26));
});
  • React.lazy (这个可以试一下)

Suspense 组件中渲染 lazy 组件,如此使得我们可以使用在等待加载 lazy 组件时做优雅降级(如 loading 指示器等)。React.lazy 定义一个动态加载的组件,有助于缩减bundle的体积,延迟加载未使用的组件。
fallback 属性接受任何组件加载过程中你想展示的React元素

import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));

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

渐进增强(Progressive Enhancement):一开始就针对低版本浏览器进行构建页面,完成基本的功能,然后再针对高级浏览器进行效果、交互、追加功能达到更好的体验。

优雅降级(Graceful Degradation):一开始就构建站点的完整功能,然后针对浏览器测试和修复。比如一开始使用 CSS3 的特性构建了一个应用,然后逐步针对各大浏览器进行 hack 使其可以在低版本浏览器上正常浏览。

  • 异常捕获边界(Error boundaries)

如果模块加载失败(比如说:网络问题),就会触发问题,可以通过异常捕获边界技术来处理这些问题。

import React, { Suspense } from 'react';
import MyErrorBoundary from './MyErrorBoundary';

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

const MyComponent = () => (
  <div>
    <MyErrorBoundary>
      <Suspense fallback={<div>Loading...</div>}>
        <section>
          <OtherComponent />
          <AnotherComponent />
        </section>
      </Suspense>
    </MyErrorBoundary>
  </div>
);
  • 基于路由的代码分割
    大多数用户习惯于页面之间可以有一个加载切换过程;我们可以选择重新渲染整个页面,这样用户就不必在渲染的同时再和页面上的其他元素进行交互。

使用React Router来进行代码分割

import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/about" component={About}/>
      </Switch>
    </Suspense>
  </Router>
);

二、Context

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法,不需要自伤而下逐层传递props

使用context,可以避免通过中间元素传递props

当React渲染一个订阅了context对象的组件,这个组件就会从组件树中离自身最近的那个匹配的Provider 中读取到当前的 context 值。

当Provider 的value 值发生变化时,它内部的所有消费组件都会重新渲染;

// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context对象(“light”为默认值)。
const ThemeContext = React.createContext('light');
class App extends React.Component {
  render() {
    // 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
    // 无论多深,任何组件都能读取这个值。
    // 在这个例子中,我们将 “dark” 作为当前的值传递下去。
    // ⚠️ 这里,没有使用默认值“light”,而是使用了“dark”
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class ThemedButton extends React.Component {
  // 指定 contextType 读取当前的 theme context。
  // React 会往上找到最近的 theme Provider,然后使用它的值。
  // 在这个例子中,当前的 theme 值为 “dark”。
  static contextType = ThemeContext;
  render() {
  	let value = this.context; // 基于这个value值进行渲染工作
    return <Button theme={value} />;
  }
}

这里一定要注意 ⚠️

Context 主要应用场景在于很多不同层级的组件需要访问同样一些数据;但是一定要谨慎使用,因为这会使得组件的复用性变差。

如果只是想避免层层传递一些属性,组件组合有时候是一个比 context 更好的解决方案。

context 使用动态值

theme-context.js

export const themes = {
  light: {
    foreground: '#000000',
    background: '#eeeeee',
  },
  dark: {
    foreground: '#ffffff',
    background: '#222222',
  },
};

export const ThemeContext = React.createContext(
  themes.dark // 默认值
);

themed-button.js

import {ThemeContext} from './theme-context';

class ThemedButton extends React.Component {
  render() {
    let props = this.props;
    let theme = this.context;
    return (
      <button
        {...props}
        style={{backgroundColor: theme.background}}
      />
    );
  }
}
ThemedButton.contextType = ThemeContext;

export default ThemedButton;

在嵌套组件中更新Context

可以通过context传递一个函数,使得consumers 组件更新context;

export const ThemeContext = React.createContext({
  theme: themes.dark,
  toggleTheme: () => {},
});
import {ThemeContext} from './theme-context';

function ThemeTogglerButton() {
  // Theme Toggler 按钮不仅仅只获取 theme 值,
  // 它也从 context 中获取到一个 toggleTheme 函数
  return (
    <ThemeContext.Consumer>
      {({theme, toggleTheme}) => (
        <button
          onClick={toggleTheme}
          style={{backgroundColor: theme.background}}>
          Toggle Theme
        </button>
      )}
    </ThemeContext.Consumer>
  );
}

export default ThemeTogglerButton;

消费多个Context

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值