天天看點

react 按照一級路由 分包加載按需加載 方案 import() 按需加載路由 React Loadable 路由配置 線上優化效果 總結 異步加載js

按需加載

一開始整個項目隻有一個bundle.js,壓縮之後達到了4M。導緻首屏加載速度很慢,需要優化。

https://juejin.im/entry/5b364bcbe51d45589e7bef4b#%E6%96%B9%E6%A1%88 方案

優化包大小,從業務角度出發,抽離重複的業務元件,避免大規模的90%相似度代碼。需要對業務熟悉,一時間優化沒有那麼明顯。

從技術角度,項目裡面使用了react route,那能不能按照路由按需加載呢?

https://juejin.im/entry/5b364bcbe51d45589e7bef4b#import import()

之前

import { add } from './math';


console.log(add(16, 26));
複制代碼           

之後

import("./math").then(math => {

console.log(math.add(16, 26));

});
複制代碼           

利用import(), webpack會幫我們算依賴和局部更新。

https://juejin.im/entry/5b364bcbe51d45589e7bef4b#%E6%8C%89%E9%9C%80%E5%8A%A0%E8%BD%BD%E8%B7%AF%E7%94%B1 按需加載路由

https://juejin.im/entry/5b364bcbe51d45589e7bef4b#%E6%AD%A3%E5%B8%B8%E6%83%85%E5%86%B5 正常情況

一般是項目不大的情況下,會直接使用如下配置

import React, { Component } from 'react';

import { Route } from 'react-router-dom';


import a1 from '../components/a/a1/index.js';


const routes = (

<div className='content'>

<Route exact path="/a/a1" component={a1} />

</div>

)


export default routes;
複制代碼           

這個是路由提前預定好,webpack打包時候會将a1打包到bundle裡面。但是erp有140多個route,會導緻整個bundle無限變大。

https://juejin.im/entry/5b364bcbe51d45589e7bef4b#%E6%94%B9%E8%BF%9B 改進

route的component本質就是個react.component

結合import()構造一個加載component

class LoaderA extends Component {

constructor(props) {

super(props);

this.state = {

loaded: true,

loading: null,

A: null

}

}


componentDidMount() {

setTimeout(() => {

import('../components/a/a1/index.js').then((e) => {

this.setState({

A: e.default,

loaded: false

})

})

}, 2000)

}


render() {

let { A } = this.state;

if (this.state.loaded) {

console.log('loading');

return <div><span>loading...</span></div>;

}

return <A />

}


}
複制代碼           

LoaderA就是個元件,沒有加載到A之前,用占位符,import()之後,再setState渲染真正元件

在稍微改下route配置

<Route exact path="/a/a1" component={LoaderA} />
複制代碼           
檢視運作例子

https://juejin.im/entry/5b364bcbe51d45589e7bef4b#React-Loadable React Loadable

利用上面的原理,我們就可以自己優化異步加載了,而

正是這個方案的優秀的庫。

改寫上面的LoaderA

import Loadable from 'react-loadable';


const LoaderA = Loadable({

loader: () => import('../components/a/a1/index.js').then(a1=> a1.default),

loading() {

return <div>Loading...</div>

}

});


<Route exact path="/a/a1" component={LoaderA} />
複制代碼           

但是我們的正式環境的路由有150多個,不能每個都這麼去配置,造成顆粒化嚴重,維護也不好維護了。

那能不能考慮按照一級路由進行切分,把一個大的bundle會切割幾個小的bundle。

https://juejin.im/entry/5b364bcbe51d45589e7bef4b#%E8%B7%AF%E7%94%B1%E9%85%8D%E7%BD%AE 路由配置

在一級路由下面抽取route群組件之間關系

const config = [

{ 

bExact: true, 

hash: '/a/a1',

key: 'a1'

},

{ 

bExact: true,

hash: '/a/a2',

key: 'a2'

}

//....

];
複制代碼           

導出配置

export default config.map(val=> {

return (

<Route

exact = {val.bExact? true : false}

path={val.hash} key={val.key} component={

Loadable({

loader: val.import?() => val.import().then((e)=> {

return e[val.key]

}): () => import('./pages.js').then((e)=> {

return e[val.key]

}),

loading: Loading

})

} />

)

});
複制代碼           

這裡就通過提取cofig配置資訊,最核心的代碼是loader處理。

() => import('./pages.js').then((e)=> {

return e[val.key]

})
複制代碼           

這裡處理跟之前不一樣了,是個動态值了,那麼pages.js裡面的元件就需要跟config裡面的key值保持一緻。

import a1 from '../components/a/a1/index.js';

import a2 from '../components/a/a2/index.js';

export {

a1,

a2

}
複制代碼           

pages.js則是目前一級路由下面所有的元件。

而在總route裡面進行加載各個子路由

import a from 'components/a/routes';

import b from 'components/b/routes';

import c from 'components/c/routes';

import d from 'components/d/routes';


const routers = [

...a,

...b,

...c,

...d

];
複制代碼           

這樣就将整個bundle這麼細分到各個子路由裡面去了,後面如果某個一級變的臃腫了,可以繼續将其按照二級路由進行拆分。

檢視效果

https://juejin.im/entry/5b364bcbe51d45589e7bef4b#%E7%BA%BF%E4%B8%8A%E4%BC%98%E5%8C%96%E6%95%88%E6%9E%9C 線上優化效果

https://link.juejin.im/?target=http%3A%2F%2F7te8kr.com1.z0.glb.clouddn.com%2Fopt_codesplit.png

原先一個bundle.js 拆分成了10個bundle.js, 而且依托于webpack的強大,能夠做到局部更新bundle.

https://link.juejin.im/?target=http%3A%2F%2F7te8kr.com1.z0.glb.clouddn.com%2Fopt_split.gif

https://juejin.im/entry/5b364bcbe51d45589e7bef4b#%E6%80%BB%E7%BB%93 總結

回過頭來看,整個優化都是基于import(),這個來的。那麼import()背後發生了什麼呢?

https://juejin.im/entry/5b364bcbe51d45589e7bef4b#%E5%BC%82%E6%AD%A5%E5%8A%A0%E8%BD%BDjs 異步加載js

抛開webpack,如何異步加載js呢?很多人都會愣了一下,其實很簡單,動态建立一個script标簽。

var _ayncFunc = function(src, cb) {

var body = document.getElementsByTagName('body')[0];

var s = document.createElement('script');

s.src = src;

s.onload = function() {

cb && cb();

};

body.appendChild(s);

};
複制代碼           

這樣子去加載js,webpack内部将異步js預設為一個chunk,就相當于多了chunk。很好了解import()為啥能夠異步加載,而且能夠計算重複内容了。

webpack實作

本文位址 

xiaoqiang730730.github.io/2018/04/06/…

原文釋出時間為:2018年07月02日

作者:掘金

本文來源:

掘金

 如需轉載請聯系原作者

繼續閱讀