代码拆分

原文来自个人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进来,致使代码浪费。

本人认为,既然是组件化时代,那么就应以组件为核心,将动态导入颗粒化到组件而不是路由,将会带来更合理,性能更高的项目优化。

相关文章
相关标签/搜索