为什么要做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 stateif (isLoading) {return <div>Loading...</div>;}// Handle the error stateelse 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的区别 >>)
参考文章
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这个文件,这样的话,就不会出现重复引入冗余代码的情况了。