react-router4的按需載入實踐(基於create-react-app和Bundle元件)

楊小事er發表於2019-03-03

最近在網上也看到了react-router4的好多種按需載入的方法。

傳送門:https://blog.csdn.net/foralienzhou/article/details/73437057

雖然自己的專案不大,但是也要區分前臺和後臺,如果讓訪問前臺的使用者也載入了後臺的js程式碼,還是很影響體驗的,所以挑了一種按需載入的方法進行實踐(基於create-react-app和Bundle元件)。

import()

這裡的import不同於模組引入時的import,可以理解為一個動態載入的模組的函式(function-like),傳入其中的引數就是相應的模組。例如對於原有的模組引入import react from 'react'可以寫為import('react')。但是需要注意的是,import()會返回一個Promise物件。因此,可以通過如下方式使用:

btn.addEventListener('click', e => {
    // 在這裡載入chat元件相關資源 chat.js
    import('/components/chart').then(mod => {
        someOperate(mod);
    });
});
複製程式碼

可以看到,使用方式非常簡單,和平時我們使用的Promise並沒有區別。當然,也可以再加入一些異常處理:

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

我們首先需要一個非同步載入的包裝元件Bundle。Bundle的主要功能就是接收一個元件非同步載入的方法,並返回相應的react元件。

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;
    }
}

複製程式碼

引入模組的時候需要用Bundle元件包一下

import Bundle from './Bundle'
const Dashboard = (props) => (
    <Bundle load={() => import('./Dashboard')}>
        {(Dashboard) => <Dashboard {...props}/>}
    </Bundle>
);
複製程式碼

路由部分沒有變化

<HashRouter>
    <Switch>
        <Route path='/' exact component={Index} />
        <Route path='/dashboard' component={Dashboard} />
    </Switch>
</Router>
複製程式碼

這時候,執行npm start,可以看到在載入最初的頁面時載入的資源如下

image

而當點選觸發到/dashboard路徑時,可以看到

image

程式碼拆分在單頁應用中非常常見,對於提高單頁應用的效能與體驗具有一定的幫助。按需載入的方式還不止這一種,還可以使用require.ensure()或者一些loader也可以同樣實現這個功能。

如果載入的js很大,或者使用者的網路狀況不好的話,需要加上一個loading的效果,這裡我用的是antd的Spin元件。在render函式的mod沒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

相關文章