代碼分離知多少

1、爲何要進行代碼拆分

在以往的性能優化清單中,減小HTTP請求一直是其中的一項。事實上,也作到了。在目前的大多數應用開發中都會用到 webpack 進行應用的構建打包,從而將咱們應用以及應用所需的模塊打包成一個大 bundle。這樣也確實是減小了HTTP的請求。可是這樣作有幾個明顯的劣勢:react

  1. 首屏加載時間過長,致使白屏,移動端網絡不穩定尤甚
  2. 當你修改了業務代碼之後,整個大的 bundle 要從新下載。

2、怎樣對代碼進行拆分

先看一下webpack進行代碼分離的幾種方法:webpack

  • 在入口處配置entry進行分離
  • 防止重複,使用CommonsChunkPlugin去重和分離chunk
  • 動態導入:使用模塊的內聯函數

一、在入口處配置進行分離

entry: {
        app: './src/entry.js',
        vendor: ['react', 'react-dom']
    },

對於一些不常變更的庫,好比框架,第三方依賴等等,能夠直接在入口就進行代碼的分離,就比如這裏的reactreact-domgit

二、防止重複,使用CommonsChunkPlugin

這個插件能夠將公共模塊依賴提取到已有的chunk中,好比下面這樣github

new webpack.optimize.CommonsChunkPlugin({
    name: 'common',
}),

固然,這個插件在webpack4.0以上的版本被廢棄掉了,具體的能夠查看SplitChunksPluginweb

三、使用import()來進行動態導入

import()的相關文檔能夠查看import()相關文檔redux

固然,使用import()須要用到babel-plugin-syntax-dynamic-import插件api

import()的使用以下:性能優化

import('lodash').then(_ => {
    // do something
})

但這樣寫的話,每次都很麻煩,這裏有一個高階組件來處理這個事情babel

import React from 'react';

export default function asyncComponent(importComponent) {
  class AsyncComponent extends React.Component {
    constructor(props){
      super(props);
      this.state = {
        Component: null,
      }
    }
    componentDidMount() {
       importComponent().then(module => {
        // const { default: component } = res;
         this.setState({ Component: module.default });
       });
    }
    render(){
      const { Component } = this.state;
      return Component ? <Component { ...this.props } /> : null;
    }
  }
  return AsyncComponent;
}

有了這個高階組件,使用就方便了:網絡

const Result = asyncComponent(() => import('./container/result/result'))

import規範不容許控制模塊的名稱或其餘的屬性,由於chunkswebpack的概念。若是像上面那樣直接使用的話,輸出的就像下面這樣

圖片描述

好在webpack能夠註釋接受一些特殊的參數,但還要在配置文件的output加上chunkFilename

// 第一處修改
 const Result = asyncComponent(() => import(/* webpackChunkName: "result" */ './container/result/result'))
// 第二處修改
output: {
        path: '/',
        publicPath: '/',
        chunkFilename: '[name].bundle.js',
        filename: '[name].[hash].js',
    },

這樣,上面的模塊就會被命名爲result.bundle.js,而不是[id].bundle.js

其實作到這一步,就已經作到了代碼拆分。可是有一個比較好的庫更適合作這個事情,這個庫就是react-loadable,使用以下:

const Home = Loadable({
    loader: () => import(/* webpackChunkName: "Home" */ './container/Home/home'),
    loading: () => {
        return <div>loading</div>
    },
})

有的組件加載實在過快(<200ms),那麼這個咱們能夠給一Loading組件來設置延遲時間

function Loading(props) {
  if (props.error) {
    return <div>Error! <button onClick={ props.retry }>Retry</button></div>;
  } else if (props.pastDelay) {
    return <div>Loading...</div>;
  } else {
    return null;
  }
}

接着使用react-loadable

const Home = Loadable({
    loader: () => import(/* webpackChunkName: "Home" */ './container/Home/home'),
    loading: Loading,
    delay: 200, // 200ms內加載出來的話,不顯示加載狀態
})

想了解更多react-loadable,能夠點擊react-loadable

完整代碼

import React from 'react';
import { Route, Router, BrowserRouter, HashRouter, Switch, Redirect } from 'react-router-dom';
import { ConnectedRouter } from 'react-router-redux';
import createHistory from 'history/createHashHistory';
import App from './container/app';
import Loadable from 'react-loadable';

const Home = Loadable({
    loader: () => import(/* webpackChunkName: "Home" */ './container/Home/home'),
    loading: () => {
        return <div>loading</div>
    }
})

const Result = Loadable({
    loader: () => import(/* webpackChunkName: "result" */ './container/result/result'),
    loading: () => {
        return <div>loading...</div>
    }
})

const history = createHistory();

class Routers extends React.Component {
    render() {
        return (
            <HashRouter>
                <App>
                    <Switch>
                        <Route exact path="/" render={() => (<Redirect to="/home" />)} />
                        <Route path="/home" component={Home} />
                        <Route path="/second" component={Result} />
                    </Switch>
                </App>
            </HashRouter>
        )
    }
}
export default Routers;

最後

代碼拆分有兩種思路,一種是基於路由拆分,一種是基於組件拆分

圖片描述

左邊這幅圖是基於路由拆分的示意圖,右邊的是基於組件拆分的示意圖,能夠看到基於組件進行拆分的粒度更細一點。

考慮這樣一個場景,你有多個tab,可能有一些tab你從進入應用到離開應用都不會去點擊它,那麼每一個tab頁面下的組件就很適合進行代碼拆分。這徹底根據你的應用場景來決定的。

若是你想對你的一些組件進行拆分,也是一樣的使用react-loadable

相關文章
相關標籤/搜索