react-router4 實現按需載入

Molin發表於2017-04-21

按需載入的背景

React Router 是一個非常出色的路由解決方案,同時也非常容易上手。但是當網站規模越來越大的時候,首先出現的問題是 Javascript 檔案變得巨大,這導致首頁渲染的時間讓人難以忍受。實際上程式應當只載入當前渲染頁所需的 JavaScript,也就是大家說的“程式碼分拆" — 將所有的程式碼分拆成多個小包,在使用者瀏覽過程中按需載入。

老的方案

這裡所說的老是指使用react-router低於4的版本。

在低版本(小於4)的react-router中直接提供了按需載入的方案,示例如下:

const rootRoute = {
  path: '/',
  indexRoute: {
    getComponent(nextState, cb) {
      require.ensure([], (require) => {
        cb(null, require('components/layer/HomePage'))
      }, 'HomePage')
    },
  },
  getComponent(nextState, cb) {
    require.ensure([], (require) => {
      cb(null, require('components/Main'))
    }, 'Main')
  },
  childRoutes: [
    require('./routes/baidu'),
    require('./routes/404'),
    require('./routes/redirect')
  ]
}

ReactDOM.render(
  (
    <Router
      history={browserHistory}
      routes={rootRoute}
      />
  ), document.getElementById('app')
);複製程式碼

核心程式碼是router.getComponent,然而在react-router4中,沒有router.getComponent了,這樣我們該如何實現按需載入呢?

react-router4的實現方案

根據官網的介紹:

One great feature of the web is that we don’t have to make our visitors download the entire app before they can use it.
You can think of code splitting as incrementally downloading the app. While there are other tools for the job, we’ll use Webpack and the bundle loader in this guide.

我們要藉助bundle-loader來實現按需載入。

首先,新建一個bundle.js檔案:

import React, { Component } from 'react'

export default class Bundle extends React.Component {

    state = {
        // short for "module" but that's a keyword in js, so "mod"
        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((mod) => {
            this.setState({
                // handle both es imports and cjs
                mod: mod.default ? mod.default : mod
            })
        })
    }

    render() {
        if (!this.state.mod)
            return false
        return this.props.children(this.state.mod)
    }
}複製程式碼

然後,在入口處使用按需載入:


// ...

// bundle模型用來非同步載入元件
import Bundle from './bundle.js';

// 引入單個頁面(包括巢狀的子頁面)
// 同步引入
import Index from './app/index.js';
// 非同步引入
import ListContainer from 'bundle-loader?lazy&name=app-[name]!./app/list.js';

const List = () => (
    <Bundle load={ListContainer}>
        {(List) => <List />}
    </Bundle>
)

// ...

    <HashRouter>
        <Router basename="/">
            <div>
                <Route exact path="/" component={Index} />
                <Route path="/list" component={List} />
            </div>
        </Router>
    </HashRouter>

// ...複製程式碼

webpack.config.js檔案配置

output: {
    path: path.resolve(__dirname, './output'),
    filename: '[name].[chunkhash:8].bundle.js',
    chunkFilename: '[name]-[id].[chunkhash:8].bundle.js',
},複製程式碼

完整程式碼示例

bundle.js

react-molin

相關文章