• 技术文章 >web前端 >js教程

    React和Redux的动态导入(附代码)

    不言不言2019-03-23 09:51:13转载919

    本篇文章给大家带来的内容是关于React和Redux的动态导入(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

    代码分离与动态导入

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

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

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

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

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

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

    使用 React 处理延迟加载

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

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

    // my-module.js
    import * as React from 'react'
    
    export default {
        view: () => <p>My Modules View</p>
    }

    现在我们使用导入方法来加载这个文件,我们可以很容易地访问模块的 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 <p>Loading module...</p>;
        if (module.view) return React.createElement(module.view);
    
      }
    }

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

    // my-app.js
    
    import {LazyLoadModule} from './LazyLoadModule'
    
    const MyApp = () => (
        <p className='App'>
            <h1>Hello</h1>
            <LazyLoadModule resolve={() => import('./modules/my-module')} />
        </p>
    )
    
    ReactDOM.render(<MyApp />, 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}) => <p>{foo}</p>)
    
    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 = () => (
        <Provider store={store}>
            ...
        </Provider>
    )

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

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

    react-redux <Provider /> 组件将 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将按需加载。此外,我们还减少了包的大小和加载时间,这意味着每个模块都可以看作是一个单独的应用程序。

    本篇文章到这里就已经全部结束了,更多其他精彩内容可以关注PHP中文网的JavaScript视频教程栏目!

    以上就是React和Redux的动态导入(附代码)的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:segmentfault,如有侵犯,请联系admin@php.cn删除
    上一篇:如何在Javascript中创建自定义事件?(代码示例) 下一篇:react的setSate的异步问题的分析
    线上培训班

    相关文章推荐

    • vue 与 react 区别有多大• React组件模式的详细介绍(附示例)• react路由的简单用法(代码示例)• React高阶组件(装饰器)的介绍(代码示例)• react生命周期详解

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网