12

为什么要做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 state
    if (isLoading) {
        return <div>Loading...</div>;
    }
    // Handle the error state
    else 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的区别 >>

clipboard.png

参考文章

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这个文件,这样的话,就不会出现重复引入冗余代码的情况了。

你可能感兴趣的

31 条评论
幻丶 · 2017年09月12日

为什么要先赋给一个变量呢,直接这样写不行吗?
component={Loadable({

loader: () => import('../containers/Home'),
loading: MyLoadingComponent

}); }

+5 回复

0

可读性

岁月是把杀猪刀 作者 · 2017年09月12日
0

需要设置{"modules":false}吗?

幻丶 · 2017年09月12日
2

@幻丶 不需要,详细的看我的github项目吧,webpack部分,相信你能看得懂

岁月是把杀猪刀 作者 · 2017年09月12日
幻丶 · 2017年09月12日

感觉这样的分割意义不大,因为分割的时候提取了很多公共模块,首次加载依然很大,还需要进一步优化才有意义

+3 回复

1

为什么这么认为??

岁月是把杀猪刀 作者 · 2017年09月12日
1

我大概明白你的疑问是什么了,我更新了文章,在Q&A部分,应该是你想要的答案

岁月是把杀猪刀 作者 · 2017年09月12日
1

我的意思某些项目是头重脚轻,本身的逻辑很少,但是公共部分的使用了框架如antd ,半数页面中都有图表如echarts,这样提取出来的vendor很大,怎么提升首次加载速度是个问题。

幻丶 · 2017年09月12日
zhujun24 · 1月26日

按照作者的思路去实现的时候,发现一个问题,对于多级路由 /detail/:id ,在跳转加载 detail 这个页面的 js 的时候,路径是http://localhost:xxxx/detail/bundle.js,然后就 404 ,在
https://github.com/CodeLittle... 里,把 mock 数据的 https://bcy.net/zhipin/detail... 换成 /detail/xxx 可以复现这个问题。

回复

0

GitHub上回复你啦

岁月是把杀猪刀 作者 · 1月26日
0

我也是打包后的文件 404,请问怎么解决

任振鹏 · 5月30日
殇心法西斯ls · 2月23日

组件能正确加载,但是组件里面的css没加载出来怎么办

回复

0

我当时那个项目的设计师scss抽离出来的,会合并成一个css。听你的描述意思应该是你把样式写在jsx里面?

岁月是把杀猪刀 作者 · 2月23日
任振鹏 · 5月30日

是用了这个包 以后, 会发生 404的问题,延迟打包的文件 路径不对。。。

回复

Arthur · 8月7日

你好 ,请问如何将每个route所引用的Css单独抽取成一个.css文件然后按需加载呢?就像antd按需加载的那样;
我按照你的方法确实实现了js代码分割与按需加载,但是css被当成字符串对象引入到了js中

回复

Mr_wu · 10月18日

我用的是react+typescript开发的 在ts语法了用react-loadable的方法会报错,这怎么解决?

回复

面条 · 10月18日

按需加载只使用react-loadable就可以了,为什么还要使用babel-plugin-syntax-dynamic-import?不懂

回复

0

你试过了可以用??
https://segmentfault.com/q/10...

岁月是把杀猪刀 作者 · 10月18日
0

之前试过了,react-loadable这个在npm上也没说要依赖其他的

面条 · 10月18日
0

@面条 我用的是babel-env,这个和react-loadable实质上没关系,是es语法的问题,估计你用了不一样的babel插件

岁月是把杀猪刀 作者 · 10月19日
面条 · 10月19日

有可能

回复

载入中...