首頁  >  文章  >  web前端  >  React和Redux的動態導入(附程式碼)

React和Redux的動態導入(附程式碼)

不言
不言轉載
2019-03-23 09:51:132458瀏覽

這篇文章帶給大家的內容是關於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=&#39;App&#39;>
        <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 元件將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.com。如有侵權,請聯絡admin@php.cn刪除