资讯专栏INFORMATION COLUMN

React 和 Redux 的动态导入

jayzou / 359人阅读

摘要:动态导入使用的是的方法来加载代码。使用到目前为止,我们已经演示了如何动态加载应用程序的模块。还需要公开一个名称,在该名称下我们的模块状态将存在于应用程序的中。剩下的唯一部分就是把注册到中。

想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!

代码分离与动态导入

对于大型 Web应用程序,代码组织非常重要。 它有助于创建高性能且易于理解的代码。 最简单的策略之一就是代码分离。 使用像 Webpack 这样的工具,可以将代码拆分成更小的部分,它们分为两个不同的策略,静态和动态。

通过静态代码分离,首先将应用程序的每个不同部分作为给定的入口点。 这允许 Webpack 在构建时将每个入口点拆分为多带带的包。 如果我们知道我们的应用程序的哪些部分将被浏览最多,这是完美的。

动态导入使用的是 Webpackimport 方法来加载代码。由于 import 方法返回一个 promise,所以可以使用async wait 来处理返回结果。

// getComponent.js
async function getComponent() {
   const {default: module} = await import("../some-other-file")
   const element = document.createElement("div")
   element.innerHTML = module.render()
   return element
}

虽然这是一个很不自然的例子,但是可以看到这是一个多么简单的方法。通过使用 Webpack 来完成繁重的工作,我们可以将应用程序分成不同的模块。当用户点击应用程序的特定部分时,才加载我们需要的代码。

如果我们将这种方法与 React 提供给我们的控制结构相结合,我们就可以通过延迟加载来进行代码分割。这允许我们将代码的加载延迟到最后一分钟,从而减少初始页面加载。

使用 React 处理延迟加载

为了导入我们的模块,我们需要决定应该使用什么 API。考虑到我们使用 React 来渲染内容,让我们从这里开始。

下面是一个使用 view 命名空间导出模块组件的简单API。

// my-module.js
import * as React from "react"

export default {
    view: () => 
My Modules View
}

现在我们使用导入方法来加载这个文件,我们可以很容易地访问模块的 view 组件,例如

async function getComponent() {
    const {default} = await import("./my-module")
    return React.createElement(default.view)
})

然而,我们仍然没有使用 React 中的方法来延迟加载模块。通过创建一个 LazyLoadModule 组件来实现这一点。该组件将负责解析和渲染给定模块的视图组件。

// lazyModule.js
import * as React from "react";

export class LazyLoadModule extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      module: null
    };
  }
 
  // after the initial render, wait for module to load
  async componentDidMount() {
    const { resolve } = this.props;
    const { default: module } = await resolve();
    this.setState({ module });
  }

  render() {
    const { module } = this.state;

    if (!module) return 
Loading module...
; if (module.view) return React.createElement(module.view); } }

以下是使用 LazyLoadModule 组件来加载模块的视图方式:

// my-app.js

import {LazyLoadModule} from "./LazyLoadModule"

const MyApp = () => (
    

Hello

import("./modules/my-module")} />
) ReactDOM.render(, document.getElementById("root"))

下面是一个线上的示例,其中补充一些异常的处理。

https://codesandbox.io/embed/...

通过使用 React 来处理每个模块的加载,我们可以在应用程序的任何时间延迟加载组件,这包括嵌套模块。

使用 Redux

到目前为止,我们已经演示了如何动态加载应用程序的模块。然而,我们仍然需要在加载时将正确的数据输入到我们的模块中。

让我们来看看如何将 redux 存储连接到模块。 我们已经通过公开每个模块的视图组件为每个模块创建了一个 API。 我们可以通过暴露每个模块的 reducer 来扩展它。 还需要公开一个名称,在该名称下我们的模块状态将存在于应用程序的store 中。

// my-module.js
import * as React from "react"
import {connect} from "react-redux"

const mapStateToProps = (state) => ({
    foo: state["my-module"].foo,
})
const view = connect(mapStateToProps)(({foo}) => 
{foo}
) const fooReducer = (state = "Some Stuff") => { return state } const reducers = { "foo": fooReducer, } export default { name: "my-module", view, reducers, }

上面的例子演示了我们的模块如何获得它需要渲染的状态。

但是我们需要先对我们的 store 做更多的工作。我们需要能够在模块加载时注册模块的 reducer。因此,当我们的模块 dispatche 一个 action 时,我们的 store 就会更新。我们可以使用 replaceReducer 方法来实现这一点。

首先,我们需要添加两个额外的方法,registerDynamicModuleunregisterDynamicModule 到我们的 store 中。

// store.js
import * as redux form "redux"

const { createStore,  combineReducers } = redux

// export our createStore function
export default reducerMap => {
    
    const injectAsyncReducers = (store, name, reducers) => {
        // add our new reducers under the name we provide
        store.asyncReducers[name] = combineReducers(reducers);
        // replace all of the reducers in the store, including our new ones
        store.replaceReducer(
            combineReducers({
                ...reducerMap,
                ...store.asyncReducers
            })
        );
    };
    
    // create the initial store using the initial reducers that passed in
    const store = createStore(combineReducers(reducerMap));
    // create a namespace that will later be filled with new reducers
    store.asyncReducers = {};
    // add the method that will allow us to add new reducers under a given namespace
    store.registerDynamicModule = ({ name, reducers }) => {
        console.info(`Registering module reducers for ${name}`);
        injectAsyncReducers(store, name, reducers);
    };
    // add a method to unhook our reducers. This stops our reducer state from updating any more.
    store.unRegisterDynamicModule = name => {
        console.info(`Unregistering module reducers for ${name}`);
        const noopReducer = (state = {}) => state;
        injectAsyncReducers(store, name, noopReducer);
    };
    
    // return our augmented store object
    return store;
}

如你所见,代码本身非常简单。 我们将两种新方法添加到我们的 store 中。 然后,这些方法中的每一种都完全取代了我们 store 中的 reducer

以下是如何创建扩充 store

import createStore from "./store"

const rootReducer = {
    foo: fooReducer
}

const store = createStore(rootReducer)

const App = () => (
    
        ...
    
)

接下来,需要更新 LazyLoadModule ,以便它可以在我们的 store 中注册 reducer 模块。

我们可以通过 props 获取 store。这很简单,但这意味着我们每次都必须检索我们的 store,这可能会导致 bug。记住这一点,让 LazyLoadModule 组件为我们获取 store

react-redux 组件将 store 添加到上下文中时,只需要使用 contextTypesLazyLoadModule 中获取它。

// lazyModule.js 
export class LazyLoadModule extends React.component {
    ...
    async componentDidMount() {
        ...
        const {store} = this.context
    }
}

LazyLoadModule.contextTypes = {
    store: PropTypes.object,
}

现在可以从 LazyLoadModule 的任何实例访问我们的 store。 剩下的唯一部分就是把 reducer 注册到 store 中。 记住,我们是这样导出每个模块:

// my-module.js
export default {
    name: "my-module",
    view,
    reducers,
}

更新 LazyLoadModulecomponentDidMountcomponentWillUnmount 方法来注册和注销每个模块:

// lazyModule.js 
export class LazyLoadModule extends React.component {
    ...
    async componentDidMount() {
        ...
        const { resolve } = this.props;
        const { default: module } = await resolve();
        const { name, reducers } = module;
        const { store } = this.context;
        if (name && store && reducers)
            store.registerDynamicModule({ name, reducers });
         this.setState({ module });
    }
    ...
    
    componentWillUnmount() {
        const { module } = this.state;
        const { store } = this.context;
        const { name } = module;
        if (store && name) store.unRegisterDynamicModule(name);
    }
}

线上示例如下:
https://codesandbox.io/s/znx1...

总结

通过使用 Webpack 的动态导入,我们可以将代码分离添加到我们的应用程序中。这意味着我们的应用程序的每个部分都可以注册自己的 components 和 reducers,这些 components 和 reducers将按需加载。此外,我们还减少了包的大小和加载时间,这意味着每个模块都可以看作是一个多带带的应用程序。

原文:
https://codeburst.io/dynamic-...

你的点赞是我持续分享好东西的动力,欢迎点赞!

交流

干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。

https://github.com/qq44924588...

我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,即可看到福利,你懂的。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102841.html

相关文章

  • react-redux初级教程,纯白话讲述redux数据、开发流程整理,redux数据持久化实现

    摘要:日常项目直接使用是完全没有问题的,可是随着项目的日益壮大,组件数量的逐渐增长,组件之间的嵌套使得数据的管理越来越繁重。最后数据保存进了中的,页面也会根据的改变自动更新。 以下文章均为个人近期所学心得,自学react、redux,逐渐找到自己的方向,现将自己的方向方式写出来,以供大家学习参考,肯定会有不足,欢迎批评指正。 日常项目直接使用react是完全没有问题的,可是随着项目的日益壮大...

    gclove 评论0 收藏0
  • React 实践心得:react-redux 之 connect 方法详解

    摘要:但这并不是最佳的方式。最佳的方式是使用提供的和方法。也就是说,与的与完全无关。另外,如果使用对做属性类型检查,方法和方法为添加的属性是存在的。注意,的变化不会引起上述过程,默认在组件的生命周期中是固定的。 转载注: 本文作者是淘宝前端团队的叶斋。笔者非常喜欢这篇文章,故重新排版并转载到这里,同时也加入了一些自己的体会。 原文地址:http://taobaofed.org/blog/...

    张春雷 评论0 收藏0
  • 前端最实用书签(持续更新)

    摘要:前言一直混迹社区突然发现自己收藏了不少好文但是管理起来有点混乱所以将前端主流技术做了一个书签整理不求最多最全但求最实用。 前言 一直混迹社区,突然发现自己收藏了不少好文但是管理起来有点混乱; 所以将前端主流技术做了一个书签整理,不求最多最全,但求最实用。 书签源码 书签导入浏览器效果截图showImg(https://segmentfault.com/img/bVbg41b?w=107...

    sshe 评论0 收藏0
  • webpack4 中最新 React全家桶实战使用配置指南!

    摘要:安装配置加载器配置配置文件配置支持自定义的预设或插件只有配置了这两个才能让生效,单独的安装是无意义的。 showImg(https://segmentfault.com/img/bVbjGNY?w=2847&h=1931); 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 最新React全家桶实战使用配置指南 这篇文档 是吕小明老师结合以往的项目经验 加上自己本身...

    Towers 评论0 收藏0

发表评论

0条评论

jayzou

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<