webpack使用优化(react篇)

前言

《React移动web极致优化》也提到了,构建工具是前端优化的重要一环。而React的推荐构建工具则是Webpack。这篇文章我们就来聊聊如何在Webpack构建的过程中如何针对React的应用做一些优化。

如果还没看过《webpack使用优化(基本篇)》这篇文章,建议去看看,因为针对React的优化往往也离不开Webpack那些最基本的优化点。此外,在这里将Webpack视作构建可能招来一些人的反对,他们会将Webpack定位成打包的工具。但实际项目中,除了合图以外,家校群项目已将Webpack将为最核心的构建工具。

本篇文章的成果最终都会转化成一个开源的boilerplate, steamer-react(暂时设为私有项目)。

目录结构

构建工具离不开目录的设计,我们需要安排号文件存放的位置才便于构建工程的开展。在src目录下一级的文件,除了page文件夹是react的主体逻辑文件之外,其它的像img, js, css, libs,都属于各个页面都会用到的公共文件,如utils, 上报等。

page目录下,common文件夹主要旋转跟React相关的一些公共的文件,如公共的component,中间件等。而其它的文件夹就是每个页面的主体逻辑和资源,另外就是页面对应的html文件。

由于家校群采用的是React+ Redux这套方案,我们文件夹的名字也很能体现这套方案的特色。首先组件Component方面,我们比较认同Redux推崇的smart component(container)和dump component,因此它们分别放置在container和component文件夹下。那container和component文件夹下面放在什么呢?我们放置了组件相关的逻辑js和样式scss文件。我们暂时没将图片放在组件这一层,而是放在页面这一层,是因为我们业务不同组件间共用了不少图片,因此放在更上一层更为合适。

而store, reducer, action, connect都是跟Redux这个数据处理框架相关。像root这样的文件夹则是项目的主入口,里面有root.dev.js和root.prod.js,用于区分开发环境与生产环境对应需要引入的组件。

如果你还用到React-Router,可能你还需要多加一个route的文件夹,里面用存放项目route的配置文件。

这套文件架构比较传统的gulp和grunt复杂,但却更符合React + Redux这套方案的开发思路。

针对React的优化点

需要维护两套构建配置

Webpack跟Gulp和Grunt不同,前者属于配置型构建(当然也可以通过插件去做一些流程),后两者属于任务型的构建。以前在用Gulp开发的时候,也会写一些任务专门针对开发或者生产的环境,分别再建两条任务流,分别去处理开发与生产环境的构建。同理Webpack也需要去处理开发与生产环境的构建,因此也需要两套配置去实现。

如果搞不清楚什么任务应该放在开发环境,什么应该放在生产环境,可以参考《性能优化三部曲之一——构建篇》,里有有详情参考;如果不知道如何去区分开发与生产环境,可以参考《webpack使用优化(基本篇)》(https://github.com/lcxfs1991/blog/issues/2)。

这里想提出来说的有2点:

  • 第一,是建议开发环境的配置是生产环境的子集。 这样顺着写下来比较流畅,毕竟我们是先考虑开发,等开发完之后才会去构思生产环境的部署。这时我们可以直接用Object.assign去复制开发环境写好的配置,进行修改便是。也可以添加一些方法方便处理更新配置,例如生产环境想去添加新的插件,我参考了之前看过的一个boilerplate:
devConfig.addPlugins = function(plugin, opt) {
    devConfig.plugins.push(new plugin(opt));
};
  • 第二,是React是否使用外链问题。在开发环境下,建议直接引入node_modules包的,因为里面有许多有用的报错和提示,方便开发时快速发现问题。而生产环境自然是建议外链,否则Webpack就会自作主线地把React和你的业务逻辑打包到一起,比分开打包要大得多。

React的ES2015编译

ES2015近2年很火热,我们也来尝尝鲜。用ES2015的最大好处就是可以使用许多方便的特性,但有一个小小的坏处就是,你可能忽略ES5的写法,而ES5的写法很多时候能够清楚地表示出React的实现方式,对理解框架和原理更有帮助。

另外就是,用这些新的特性,会有一些不稳定的因素,就是不知道转换之后会成什么样子,转换后的代码兼容性如何(具体可参《babel到底将代码转换成什么鸟样?》])。果不其然,我们发布列表页之后发现一个报错:

Uncaught TypeError: Cannot assign to read only property '__esModule' of #<Object>。

解决办法,就是babel编译使用ES2015-Loose而不是ES2015的preset。具体转换的代码如下:

具体在Webpack的loaders里可以这样写你的编译配置

{ 
    test: /\.js?$/,
    loader: 'babel',
    query: {
        plugins: ['transform-decorators-legacy'],   /// 使用decorator写法
        presets: [
            'es2015-loose',    // ES2015 loose mode
            'react',
        ]
    },
    exclude: /node_modules/,
}

除此之外,我们也发现每个Webpack打包的模块,最终编译都会生成一堆类似的代码:

    function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

    ......

这些是Babel对ES2015转义生成的代码片段。多的时候,每个模块生成的这些代码都接近1kb。如果你的component多,例如像家校群有超过20组件,后面的详情页的组件可能更多,这么多组件合起来重复的代码可能就近20kb。在PC端20kb没有所谓,但到移动端却对bundle的大小锱铢必较。建议写一个Webpack plugin/loader对这些代码进行去重(反正没看到Webpack有插件,机会就留给你们了)。

如何热替换css

打包css的时候,我们习惯使用ExtractTextPlugin让css单独生成一个文件。但如果你想让css也能够热替换,在开发环境的时候请去掉这个插件让样式内联。

Webpack慎用devtools的inline-source-map模式

使用此模式会内联一大段便于定位bug的字符串,查错时可以开启,不是查错时建议 关闭,否则开发时加载的包会非常大。

如果不使用devtools查错,你看到的会是合成之后的bundle,上万行代码,也不知道是哪个文件:

使用了之后,你就很很清晰是在哪个文件,哪一行了:

如果无法使用服务器构建,开发时请让小伙伴统一开发路径

webpack的bug导致如果本地开发目录路径不一致,编译出来的md5会不一致。所以推荐使用服务器构建。

React项目的合图

在搭项目构建的时候,曾经尝试过用Webpack一个合图插件,但因不够成熟而弃用,转而考虑转投向gulp的合图插件的怀抱。由于家校群功能页面是一个多页项目,每个页面都会有合图,因此我们选用了gulp.spritesmith-multi

以前使用gulp的构建项目,css都同一放在一层,引用图片的路径都放在一个css里面。但面对React的项目,我们每一个component都有自己对应的index.js(处理逻辑)和index.scss(处理样式),由于这个合图插件只能标出一个图片路径,因此如果合图的引用发生在不同层次的component,绝对会发生找不到文件的报错,因此我们统一将引用放在container的样式文件中,权宜地解决这个问题,以下是大致gulpfile写法:

gulp.task('sprites', function (cb) {
    var spriteData = gulp.src(Config.filePath.src + 'img/sprites/**/*.png')
                        .pipe(spritesmith({
                            spritesmith: function(options) {
                                options.imgPath = Config.sprites.imgPath + options.imgName;

                                options.cssName = options.cssName.replace('.css', '.scss');
                                // customized generated css template
                                options.cssTemplate = './config/scss.template.handlebars';
                            }
                        }));

    // Pipe image stream through image optimizer and onto disk
    var imgStream = spriteData.img
    // DEV: We must buffer our stream into a Buffer for `imagemin`
    .pipe(gulp.dest(Config.sprites.imgDest));

    // Pipe CSS stream through CSS optimizer and onto disk
    var cssStream = spriteData.css
    .pipe(gulp.dest(Config.sprites.cssDest));

    // Return a merged stream to handle both `end` events
    return merge(imgStream, cssStream);
});

以下是样式引用办法:

@import"../../../css/sprites/list_s";

......

.info-right.tiku-status3 {
    @include sprite($remark);
}

如有错误,恳请斧正!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏YouMeek

一个Java程序员眼中的Mac OS(系列二:系统使用)

本文初衷 整理自己脑袋中、收藏中的那些资料,来一次清空,让自己重新开始。 帮助 Mac 后来者,减少他/她入门成本 先总结 Mac 系统你值得拥有。 本文所有的...

3296
来自专栏青枫的专栏

day49_BOS项目_01

其余步骤参考如下链接: https://www.cnblogs.com/chenmingjun/p/9513143.html#_label0 右键项目 -->...

702
来自专栏文大师的新世界

3. react-redux

react-redux是reactjs官方推荐的state管理器。具体的定义我就不说了,因为有很多地方比我说的好,大家可以Google或参照:redux、中文文...

1492
来自专栏嵌入式程序猿

小猿送你freeRTOS移植详细笔记

本篇笔记主要介绍如何移植freeRTOS嵌入式实时操作系统到恩智浦kinetis k64系列MCU上,以NXP的塔式系统TWR-MK64F为硬件平台,移植fr...

3668
来自专栏前端儿

前端代码乱糟糟?是时候引入代码质量检查工具了

为了统一团队的代码规范,除了一纸规范说明之外,还需要引入工具进行限制。虽说工具并不能完全实现规范中的规则,但至少能够在一定程度上缓解代码不统一的局面。

1411
来自专栏Albert陈凯

手把手教你用python抓取网页导入模块 urllib2随便查询一篇文章,比如On random graph。对每一个查询googlescholar都有一个url,这个url形成的规则是要自己分析的。

http://www.1point3acres.com/bbs/thread-83337-1-1.html **前言: ** 数据科学越来越火了,网页是数据...

2747
来自专栏刘望舒

这一次彻底弄明白Gradle相关配置

3232
来自专栏安恒网络空间安全讲武堂

用css绕过同源策略跨域窃取数据

用css绕过同源策略跨域窃取数据 序言 如果你和我一样无聊,你可能遇到过这种潜在的攻击 -》https://www.w3.org/TR/CSP2/#securi...

2129
来自专栏北京马哥教育

手把手教你从无到有写一个运维APP

? 由于自己现在无业游民,所以没有什么现成的环境,环境就随便找个公网的。再者当下的完成度应该算不上一个完整的 APP,但是作为参考,依瓢画葫芦绝对足够了,如果...

3446
来自专栏日常学python

教你用python登陆豆瓣并爬取影评

这是我的第二篇原创文章 在上篇文章爬取豆瓣电影top250后,想想既然爬了电影,干脆就连影评也爬了,这样可以看看人们评价的电影,再加上刚出不久的移动迷官3好像挺...

33510

扫码关注云+社区