webpack v3 結合 react-router v4 做 dynamic import — 按需載入(懶載入)

CodeLittlePrince發表於2018-01-12

為什麼要做dynamic import?

dynamic import不知道為什麼有很多叫法,什麼按需載入,懶載入,Code Splitting,程式碼分頁等。 總之,就是在SPA,把JS程式碼分成N個頁面份數的檔案,不在使用者剛進來就全部引入,而是等使用者跳轉路由的時候,再載入對應的JS檔案。 這樣做的好處就是加速首屏顯示速度,同時也減少了資源的浪費。

為什麼選擇 webpack 3?

  • 更高的效能
  • 有scope hosting功能,不再需要rollup來處理程式碼冗餘
  • 可與react-router結合,更優雅的做dynamic import
  • 最重要的一點是,我正經學webpack的時候3已結出了- -

完整的 react spa 專案地址

GitHub專案地址

這個一個完整的專案,這節相關的內容可在router/routerMap.jsx中找到。

第一步:安裝 babel-plugin-syntax-dynamic-import

babel用的是babel-env,使用方法可以去babel官方學習,實踐可看我專案的原始碼。

npm i -D babel-plugin-syntax-dynamic-import 以後, 在.babelrc檔案的plungins中加上"syntax-dynamic-import"

第二步:安裝 react-loadable

npm i -S react-loadable 以後,我們就能愉快得做dynamic import了。

第三步: 編輯routerMap

import React from 'react';
import { HashRouter as Router, Route, Switch } from 'react-router-dom';
import createHistory from 'history/createBrowserHistory';
const history = createHistory();

import App from 'containers';

// 按路由拆分程式碼
import Loadable from 'react-loadable';
const MyLoadingComponent = ({ isLoading, error }) => {
    // Handle the loading state
    if (isLoading) {
        return <div>Loading...</div>;
    }
    // Handle the error state
    else if (error) {
        return <div>Sorry, there was a problem loading the page.</div>;
    }
    else {
        return null;
    }
};
const AsyncHome = Loadable({
    loader: () => import('../containers/Home'),
    loading: MyLoadingComponent
});
const AsyncCity = Loadable({
    loader: () => import('../containers/City'),
    loading: MyLoadingComponent
});
const AsyncDetail = Loadable({
    loader: () => import('../containers/Detail'),
    loading: MyLoadingComponent
});
const AsyncSearch = Loadable({
    loader: () => import('../containers/Search'),
    loading: MyLoadingComponent
});
const AsyncUser = Loadable({
    loader: () => import('../containers/User'),
    loading: MyLoadingComponent
});
const AsyncNotFound = Loadable({
    loader: () => import('../containers/404'),
    loading: MyLoadingComponent
});

// 路由配置
class RouteMap extends React.Component {
    render() {
        return (
            <Router history={history}>
                <App>
                    <Switch>
                        <Route path="/" exact component={AsyncHome} />
                        <Route path="/city" component={AsyncCity} />
                        <Route path="/search/:category/:keywords?" component={AsyncSearch} />
                        <Route path="/detail/:id" component={AsyncDetail} />
                        <Route path="/user" component={AsyncUser} />
                        <Route path="/empty" component={null} key="empty" />
                        <Route component={AsyncNotFound} />
                    </Switch>
                </App>
            </Router>
        );
        // 說明
        // empty Route
        // https://github.com/ReactTraining/react-router/issues/1982  解決人:PFight
        // 解決react-router v4改變查詢引數並不會重新整理或者說過載元件的問題 
    }
}

export default RouteMap;
複製程式碼

大功告成

我們可以執行webpack,然後就能看到效果(圖僅為dev環境,build才會再打包一個vendor.js,為什麼要有vendor.js,請見devDependencies和dependencies的區別 >>

clipboard.png

原文地址

github.com/CodeLittleP…

參考文章

Code Splitting in Create React App

Q&A

有同學表示,我的方法做頁面分離並沒有什麼好處,因為每個頁面都依賴了三方庫的程式碼,所以其實頁面有很多冗餘程式碼,能想到這點很棒,已經開始有架構思維了。不過,注意這個想法在dev環境下,這個同學是對的。

那到了build環境,或者說到了釋出環境,又是怎麼樣的呢?的確,這篇文章我沒有提到,請見我的另一篇文章devDependencies和dependencies的區別。這篇文章主要解釋了npm的package.json中devDependencies和dependencies區別是什麼。

看完以後,我們就可以知道,為什麼我之前說“注意這個想法在dev環境下,這個同學是對的”了。因為,我們npm run build以後,webpack會把三方包打包到vendor.js檔案,頁面邏輯程式碼不會牽涉其中,每個頁面都會引用vendor.js這個檔案,這樣的話,就不會出現重複引入冗餘程式碼的情況了。

相關文章