On-demand loading practice of react-router4 (based on create-react-app and Bundle components)

Recently, I have also seen a variety of on-demand loading methods for react-router4 on the Internet.

Portal: https://blog.csdn.net/foralienzhou/article/details/73437057

Although my project is not big, it is necessary to distinguish the foreground and the background. If users who visit the foreground also load the js code in the background, it will still affect the experience, so I chose a method of on-demand loading for practice (based on create -react-app and Bundle components).

import()

The import here is different from the import when the module is imported. It can be understood as a function-like function of a dynamically loaded module, and the parameters passed in are the corresponding modules. For example, import react from 'react' to the original module can be written as import('react'). But it should be noted that import() will return a Promise object . Therefore, it can be used as follows:

btn.addEventListener('click', e => {
    // 在这里加载chat组件相关资源 chat.js
    import('/components/chart').then(mod => {
        someOperate(mod);
    });
});

As you can see, the usage is very simple, and it is no different from the Promise we usually use. Of course, you can also add some exception handling:

btn.addEventListener('click', e => {
    import('/components/chart').then(mod => {
        someOperate(mod);
    }).catch(err => {
        console.log('failed');
    });
});

We first need an asynchronously loaded wrapper component Bundle. The main function of Bundle is to receive a method for asynchronous loading of a component and return the corresponding react component.

import React from 'react';

export default class Bundle extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            mod: null
        };
    }

    componentWillMount() {
        this.load(this.props)
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.load !== this.props.load) {
            this.load(nextProps)
        }
    }

    load(props) {
        this.setState({
            mod: null
        });
        props.load().then((mod) => {
            this.setState({
                mod: mod.default ? mod.default : mod
            });
        });
    }

    render() {
        return this.state.mod ? this.props.children(this.state.mod) : null;
    }
}

When importing a module, you need to use the Bundle component to package it

import Bundle from './Bundle'
const Dashboard = (props) => (
    <Bundle load={() => import('./Dashboard')}>
        {(Dashboard) => <Dashboard {...props}/>}
    </Bundle>
);

The routing part has not changed

<HashRouter>
    <Switch>
        <Route path='/' exact component={Index} />
        <Route path='/dashboard' component={Dashboard} />
    </Switch>
</Router>

At this time, execute npm start, you can see that the resources loaded when loading the initial page are as follows
image

And when the click triggers to the /dashboard path, you can see
image

Code splitting is very common in single-page applications, and it is helpful to improve the performance and experience of single-page applications. There is more than one way to load on demand. You can also use require.ensure() or some loaders to achieve this function.
If the loaded js is very large, or the user's network condition is not good, a loading effect needs to be added. Here I use the Spin component of antd. It can be added when the mod of the render function is not set.

render() {
    let spin = <div style={{textAlign: 'center', marginTop:50}}><Spin size="large"/><br/>正在玩命加载中。。。</div>;
    return  this.state.mod ? this.props.children(this.state.mod) : spin;
}

image

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326858304&siteId=291194637