React 入门(7): 动态加载组件

react

import()异步加载模块

在webpack中, 调用import()函数可以将依赖模块进行切割, 打包为非入口点文件, 这是通过Promise+ajax完成的. 请求路径是相对路径, 对于单页应用来说没有问题.

非入口点文件的命名由webpack.config.output.chunkFilename(可以定义路径, 使用[name]和[id]变量)以及Magic Comment(定义[name]变量)共同确定.

reactlazy函数">React.lazy()函数

React.lazy() 允许你定义一个动态加载的组件。这有助于缩减 bundle 的体积,并延迟加载在初次渲染时未用到的组件。

// 这个组件是动态加载的

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

渲染 lazy 组件依赖该组件渲染树上层的 <React.Suspense> 组件。这是指定加载指示器(loading indicator)的方式。

使用 React.lazy 的动态引入特性需要 JS 环境支持 Promise。在 IE11 及以下版本的浏览器中需要通过引入 polyfill 来使用该特性。

! 不支持服务端渲染。

例子 -- lazy加载组件

import { Component, lazy, Suspense } from \'react\';

import css from \'./style.css\';

/** 异步组件使用lazy()函数加载, 传递一个使用import()函数的Promise异步方法, 该方法最终返回import()函数的结果 */

const AsyncComponent = lazy(() => {

return new Promise((resolve, reject) => {

import(\'./AsyncComponent\').then(AsyncComponent => {

console.log(\'加载完毕, 延迟传送\');

setTimeout(() => {

console.log(\'传送\');

resolve(AsyncComponent);

}, 5000);

});

});

});

export default (

<div id={css.app}>

<Suspense fallback={<h1>加载中</h1>}>

<AsyncComponent></AsyncComponent>

</Suspense>

</div>

);

Suspense组件

React关注DOM和事件, 数据更新更是重要, 因此, 我们来看Suspense组件如何在异步操作与UI更新之间建起桥梁.

代码分割: https://zh-hans.reactjs.org/docs/code-splitting.html#reactlazy

Suspense: https://zh-hans.reactjs.org/docs/concurrent-mode-suspense.html

Suspense还用于异步数据的获取:

官方示例: https://codesandbox.io/s/frosty-hermann-bztrp?file=/src/fakeApi.js

抛出Promise: 使用throw关键字陷入React内核

在执行异步操作的过程中, 我们只需要在Promise未完成状态时将该Promise抛出到React核心即可:

function fetchName() {

console.log(\'尝试联网获取用户名...\');

throw new Promise(()=> {

console.log(\'抛出一个永久pending状态的Promise\');

});

}

function FunctionComponent(props) {

console.log(\'尝试获取用户名并渲染UI...\');

return <h2>用户名: {fetchName()}</h2>

}

export default (

<div id={css.app}>

<Suspense fallback={<h1>正在联网获取用户名...</h1>}>

<FunctionComponent/>

</Suspense>

</div>

);

Promise的拒绝状态会导致组件立即重新渲染, 并可能不断重复:

function fetchName() {

console.log(\'尝试联网获取用户名...\');

throw new Promise((_, reject)=> {

console.log(\'抛出一个拒绝状态的Promise\');

reject();

});

}

抛出其它非Promise异常会被React重新抛出, 导致页面报错.

如果一个抛出的Promise结束了成为success状态, 那么它之后应该返回相应的结果, 而不是再次抛出Promise, 因为success状态的Promise(这里可能是全部的Promise结束后再调用)会导致方法组件再次被调用以渲染元素.

import { Component, lazy, Suspense, createElement, useRef } from \'react\';

import css from \'./style.css\';

// 单例

let fetchNameByInternet = () => new Promise(resolve => {

console.log(\'网络请求开始了, 将于4秒后完成\');

setTimeout(() => resolve(\'develon\'), 4000);

});

let resolved = false; // 标志网络请求是否已完成

let name = "Don\'t get the Name"; // 存储从网络获取的用户名

function fetchName() {

console.log(\'尝试联网获取用户名...\');

if (resolved) {

console.log(`获取到数据: "${name}" !`);

return name;

}

throw fetchNameByInternet().then(network_name => {

resolved = true;

name = network_name;

});

}

function FunctionComponent(props) {

console.log(\'方法组件被调用, 尝试获取用户名并渲染UI...\');

let name = fetchName(); // 此处会抛出异常, 不可进行捕获, 从而就像CPU中断指令一样陷入React内核

console.log(\'方法组件继续执行, 开始渲染元素\'); // 整个方法结束, 只有当Promise成功之后, 才会再次调用该方法组件, 所以说这些异步操作是有顺序的

return <h2>用户名: {name}</h2>;

}

export default (

<div id={css.app}>

<Suspense fallback={<h1>正在联网获取用户名...</h1>}>

<FunctionComponent/>

</Suspense>

</div>

);

END

以上是 React 入门(7): 动态加载组件 的全部内容, 来源链接: utcz.com/z/383021.html

回到顶部