代碼拆分

原文來自個人github: https://github.com/Vibing/blog前端

代碼拆分與動態導入

當項目越作越大時,體積過大致使加載速度過慢,性能問題直接影響用戶體驗。webpack

這時咱們會考慮將代碼拆分git

拆分,顧名思義就是將一個大的東西拆分紅N個小的東西,用公式表示就是:Sum = n * Subgithub

代碼拆分基於動態導入

什麼是動態導入?就是我須要什麼,你給我什麼,我不須要的時候,你別給我,我嫌重。web

動態導入能夠將模塊分離成一個單獨的文件 在須要的時候加載進來。npm

對於動態導入,webpack 提供了兩個相似的技術。antd

  • 第一種,也是優先選擇的方式是,使用符合 ECMAScript 提案 的 import() 語法。
  • 第二種,則是使用 webpack 特定的 require.ensure

從webpack 2之後,通常使用第一種。async

async-loadable

因爲import()方法返回的是Promise對象,咱們爲了能方便的返回組件,
這裏推薦使用async-loadable插件組件化

例子代碼:性能

import loadable from 'async-loadable';
import Loading from './my-loading-component';

const LoadableComponent = loadable({
  loader: () => import('./my-component'),
  loading: status => <Loading {...status}/>,
});

export default class App extends React.Component {
  render() {
    return <LoadableComponent/>;
  }
}

代碼裏有熟悉的 import() 方法。async-loadable 使用 webpack 的動態導入,調用loadable方法能夠方便的返回要使用的組件。

下面我將以我本人的項目經歷,來說解代碼拆分(code splitting)

代碼拆分前

當初仍是小白的我,一開始哪知道有代碼拆分這個技術啊,就一我的負責一個小項目,一開始項目不大,跑起來也是嗖嗖的,這裏先貼一下路由代碼:

import Home from './home';
import Page1 from './page1';
import Page2 from './page2';

<Route exact path="/" component={Home}/>
<Route path="/page1" component={Page1}/>
<Route path="/page2" component={Page2}/>

這裏沒有使用動態導入,而是直接將全部頁面靜態引入進來,而後賦到對應路由上。
這麼作的壞處就是:打包時,整個項目全部的頁面都會打包到一個文件中,隨着頁面增多,這個文件也愈來愈大,最後我看了一下,達到了近25M(我嚇得打開度娘...)。

若是用一張圖來表示的話,這張圖在適合不過了:

41189925-0c8ecf3c-6c08-11e8-9bf8-2628aab82690.png

哈哈,整個一坨有沒有。全部路由在這一坨紅色裏,看着真特麼憋屈啊

基於路由的代碼拆分

打開度孃的我臉色漸漸有了好轉,經過搜索,看到了webpack有個code splitting功能(代碼拆分),
前面說過,代碼拆分其實就是使用動態導入的技術實現的,那麼咱們就使用動態導入來優化一把以前的路由:

import Loadable from 'async-loadable';
import Loading from './my-loading-component';

const Home = Loadable({
    loader: () => import(`./home`),
    loading: Loading
  });

const Page1 = Loadable({
    loader: () => import(`./page1`),
    loading: Loading
  });

const Paeg2 = Loadable({
    loader: () => import(`./page2`),
    loading: Loading
  });

<Route exact path="/" component={<Home />} />
<Route path="/page1" component={<Page1 /> } />
<Route path="/page2" component={<Page2 /> } />

咱們再也不使用 import module from 'url' 來靜態引入模塊,而是使用 loadComponent 來動態導入,它返回的是Loadable的結果,也就是咱們想要的組件,咱們把再把組件給對應的路由,這就完成了基於路由的代碼拆分。

使用之後,鄙人懷着激動的心情開始打包項目,當我看到控制檯的打包日誌時,個人表情是這樣的:
p4

咳咳,這種好事情固然要分享一下啦,你要的結果:

41190032-16b2d998-6c0a-11e8-88a0-079c7b1b5a15.png

能夠看到,webpack打包時已經將以前的一個臃腫文件按路由拆分紅了三個文件,當咱們點擊一個路由時,會動態加載對應的文件。

好比我點擊home頁面的路由時:

41190065-9a4d4c2a-6c0a-11e8-89b4-c5fceb95a74b.png

我再點擊page1時:

41190067-ac9155d4-6c0a-11e8-87de-cc66bb24d62b.png

嗯,是按照路由來拆分的代碼,完美~

這樣看來,咱們須要將以前的那張圖改爲這樣的:

41190144-18bd004a-6c0c-11e8-9662-6b65b115a035.png

看着項目加載速度變快了,內心真特麼高興 p8

基於模塊拆分

其實基於路由的代碼拆分已經能夠知足絕大多數項目了,再大的項目也能知足。

但隨着項目作的多了,慢慢的發現了一個問題:代碼浪費

好比我要作一個Tab切換的功能,像醬紫的:

41190199-d574ad46-6c0c-11e8-8550-e6f729af12bd.png

對應的代碼大概是醬紫的:

import { Tabs } from 'antd';
import TabOne from './component/tab1';
import TabTwo from './component/tab2';
import TabThree from './component/tab3';

const TabPane = Tabs.TabPane;

export default class Home extends Component {
  render() {
    return (
      <Tabs defaultActiveKey="1">
        <TabPane tab="Tab 1" key="1">
          <TabOne />
        </TabPane>
        <TabPane tab="Tab 2" key="2">
          <TabTwo />
        </TabPane>
        <TabPane tab="Tab 3" key="3">
          <TabThree />
        </TabPane>
      </Tabs>
    );
  }
}

Tab切換,每一個前端小夥伴都作過,其實說白了,就是顯示隱藏的效果。

可是在這個頁面中,已經把每一個Tab裏的代碼都加載進來了,若是用戶只看第一個Tab,其餘Tab不點擊,就形成了代碼浪費

如何解決這個問題呢?仍是那句話:我須要什麼,你給我什麼,我不須要的時候,你別給我,我嫌重。

咱們使用動態導入的方式改造一下代碼:

import { Tabs } from 'antd';
import Loadable from 'async-loadable';
import TabOne from './component/tab1';
import Loading from './component/loading';

const TabPane = Tabs.TabPane;

const loadComponent = path =>
  Loadable({
    loader: () => import(`${path}`),
    loading: Loading
  });

const Tab2 = loadComponent('./component/tab2.tsx');
const Tab3 = loadComponent('./component/tab3.tsx');

export default class Home extends Component {
  render() {
    return (
      <Tabs defaultActiveKey="1">
        <TabPane tab="Tab 1" key="1">
          <TabOne />
        </TabPane>
        <TabPane tab="Tab 2" key="2">
          <Tab2 />
        </TabPane>
        <TabPane tab="Tab 3" key="3">
          <Tab3 />
        </TabPane>
      </Tabs>
    );
  }
}

一樣 咱們再也不使用import module from 'url'的方式,而是使用 loadComponent 方法動態導入。

因爲TabOne是第一個默認顯示的,因此不必動態導入。

如今咱們來點擊Tab 2看看效果:

41211203-56b60466-6d68-11e8-828a-207280129062.png

很是棒,正是咱們想要的。

再點擊Tab 3 :

41211224-719527a8-6d68-11e8-9c31-bc9b699ad8c1.png

簡直完美!😄

到目前爲止,咱們基於模塊的代碼拆分就完成了,咱們把以前的拆分圖再改一下:

41211252-a2ebacbe-6d68-11e8-8fa9-21948ba234dd.png

看上去爽朗了不少啊!

總結

基於路由的代碼拆分能夠很大程度上減輕代碼的臃腫,但依然會存在不會被使用的組件被import進來,致使代碼浪費。

本人認爲,既然是組件化時代,那麼就應以組件爲核心,將動態導入顆粒化到組件而不是路由,將會帶來更合理,性能更高的項目優化。

相關文章
相關標籤/搜索