[翻译]基于Webpack4使用懒加载分离打包React代码

2018-03-11 admin

原文地址:https://engineering.innovid.com/code-splitting-using-lazy-loading-with-react-redux-typescript-and-webpack-4-3ec60140ec5a 作者:Aviv Shafir 摘要:Innovid网站使用Webpack4对一个React项目进行了优化改造。主要使用了新的optimization配置和动态注入功能。

Hey,这里是Innovid,一个领先的视频广告平台。我们每天处理130万小时的视频,而在我们的web项目中,经常会使用到Webpack。我们非常喜欢这个工具。

最近,我们将一个项目迁移到了最新的Webpack4。它给我们带来了一些开箱即用的新特性,比如在构建时间上进行了非常大的优化。

在本次迁移中,我们决定使用懒加载这一Webpack最吸引人的特性来分割app中的主要代码部分。

代码分割能够帮助你延迟加载用户当前需要的内容,同时也能显著地提升用户体验。尽管你没有减少app的总代码量,但你已经避免加载一些用户也许永远也用不到的代码了。而且还能够在初始加载时减少加载的代码数量。 ——React 文档

Webpack根据你的应用程序构建了一个依赖关系图。从你的入口文件开始,它递归遍历所有文件和它们的依赖文件,使用loader和plugin对你的文件施了点魔法,最后就输出了提供给用户的生成包。

我们现在将生成包分为app.js(我们的应用代码)和vendors.js(第三方库)。 我们使用webpack-bundle-analyzer插件来可视化两个生成包:

app.js大小116KB,vendors.js大小399KB

Webpack配置

app.js是我们程序的入口,所以自动打包成app.js。而第三方包vendors.js是使用了新的optimization配置,将从node_modules文件夹中引入的所有文件打包生成的。

mode: "production",
  entry: {
    app: path.join(__dirname, "index.tsx"),
  },
  output: {
    path: path.resolve(__dirname, "public/dist"),
    publicPath: "",
    chunkFilename: "[name].js",
    filename: "[name].js"
  },
  optimization: {
        runtimeChunk: {
            name: "manifest"
        },
        splitChunks: {
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name: "vendors",
                    priority: -20,
                    chunks: "all"
                }
            }
        }
   }

注意: 在Webpack4中,我们不再使用CommonChunkPlugin了,它被splitChunksruntimeChunk这两个新API所取代。

懒加载React组件

现在的vendors和app包都是用户在第一次打开页面室加载的。我们发现可以将一些“重量级”的组件懒加载来提升首屏体验,并且减少初始包的体积。

比如说:redux-form是一个管理react应用表单的库,它只在一个名为GenerateTags的大型组件中使用。由于它体积较大并且只在特定场景下被使用,所以用它来作为懒加载的实验对象是再好不过了。redux-form和GenerateTags组件可以被抽取到单独一个chunk中,这样我们在渲染首屏时请求的包体积更小。

让我们看看现在流行的动态导入工具库:react-loadable。它基础封装了未来JS的新语法import()

const GenerateTags = Loadable({
  loader: () =>
    import(/* webpackChunkName: "generateTags" */ "./GenerateTags"),
    loading: LoadingSpinner
});

使用之后,我们的包变成了下面这样:

GenerateTags已经被抽取到单独的一个chunk中,但redux-form仍然在vendor.js包里。

结果不尽如人意,因为redux-form仍然在vendors.js包中,但我们希望它跟GenerateTags都被抽取到一个不同的chunk中来实现按需加载。

之所以会出现这样的情况,是因为我们在别的文件中也引用了redux-form。比如说我们在combineReducers 中编写了下面的代码:

import { reducer as formReducer } from "redux-form";
const applicationReducer: Reducer<any> = combineReducers({
    user,
    sidenav,
    navigation,
    //...
    form: formReducer
});

这段代码顶部的静态导入语句导致redux-form库成了我们vendors包的一部分。也就是说,Webpack认为它已经被静态导入成我们的app入口依赖树的一部分,所以不能被懒加载。

为了解决这个问题,我们决定动态注入redux-form reducer。首先,我们移除了导入redux-form reducer的语句,并且加了下面的代码来实现动态注入redux reducer:

export function injectAsyncReducer(store, name, asyncReducer) {
  if (store.asyncReducers[name]) {
    return;
  }
  store.asyncReducers[name] = asyncReducer;
  store.replaceReducer(createReducer(store.asyncReducers));
}

export const configureStore = (initialState: AppState) => {
  const enhancer = compose(applyMiddleware(...getMiddleware()));
  const store: any = createStore(createReducer(), initialState, enhancer);
  store.asyncReducers = {};
  return store;
};

const createReducer = (asyncReducers = {}) => {
    return combineReducers({
        user,
        sidenav,
        navigation,
        //...
        ...asyncReducers
    });
};

最后,我们在GenerateTags组件的componentDidMount中调用injectAsyncReducer方法。

public componentDidMount() {
    const reduxFormReducer = require("redux-form").reducer;
    injectAsyncReducer(store, "form", reduxFormReducer);
  }

注意,不推荐从组件直接获取一个store的引用,因为这样会导致你在做服务端渲染时出现一些问题。 在这里你可以阅读更多注入异步代码和使用HOC的知识。

TypeScript配置

我们在项目中使用了typescript。我们必须在tsconfig.json中更新esnext的module配置,以及设置removeCommentsfalse(要支持动态注入,TS的版本必须高于2.4)。这样,之前的动态注入才会起作用。通过“告诉”typescript编译器避开我们的import语句,并且不要对它们进行转码来让Webpack正常工作。

{
  "compilerOptions": {
    "target": "es5",
    "sourceMap": false,
    "inlineSourceMap": true,
    "module": "esnext",
    "moduleResolution": "node",
    "jsx": "react",
    "preserveConstEnums": true,
    "removeComments": false,
    "lib": ["es6", "dom"]
  },
  "types": ["node"]
}

最后的结果就像下面这样:

vendors.js 314 KiB, app.js 96.6 KiB, generateTags.js 23.2 KiB, vendors~generateTags.js 90.2 KiB

最后我们成功了,GenerateTags和它的依赖文件redux-form被提取出vendor.js并且能够被按需加载。

总结

我们推荐你阅读这个文章来优化Webpack。

  • 使用动态注入可以减少最终包的体积。还能疼痛感异步加载提供更快的首屏加载速度。
  • typescript从2.4版本开始支持动态注入,你只需要记住修改一部分配置就能使用这个功能。
  • 迁移到Webpack4并不不复杂,但是目前还没有关于新配置和API的介绍文档。但我相信很快它们都会有的。
  • 动态注入redux reducer是一个很有用的小技巧,它能够帮助我们的app在使用redux reducer时延迟加载一些库。

查看更多我翻译的Medium文章请访问: 项目地址:https://github.com/WhiteYin/translation SF专栏:https://segmentfault.com/blog/yin-translation

[转载]原文链接:https://segmentfault.com/a/1190000013654180

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-9869.html

文章标题:[翻译]基于Webpack4使用懒加载分离打包React代码

相关文章
React Native v0.13.3 发布,Facebook开源框架
React is a JavaScript library for building user interfaces. Just the UI: Lots of people use React as the V in MVC. Since...
2015-11-12
2014年最流行前端开发框架对比评测
如今,各种开发框架层出不穷,各有千秋。哪些是去年较受开发者关注的呢?前不久,云适配根据Github上的流行程度整理了2014年最受欢迎的6个前端开发框架,并进行对比说明,希望帮助有需要的朋友选择合适自己的前端框架。 1. Bootstrap...
2015-11-12
使用jspdf生成pdf报表
由于前台html已经动态生成报表,而且,前台有一个功能,一个date range组件,当你拖动的时候,报表会在不提交到后台的情况下动态变化。 因此需要用到js生成生报表: 用到的组件: jquery.js jspdf.js canvg.js...
2017-03-25
javaScript+turn.js实现图书翻页效果实例代码
为了实现图书翻页的效果我们在网上可以看到很多教程 在这里推荐turn.js 网上的turn.js 有api 不过是英文的  很多人看起来不方便 .关于代码也是奇形怪状在这里我将详细讲解如何使用turn.js实现翻页效果 ,本篇文章只是讲解 ...
2017-03-16
Facebook发布React Native!
React.js Conf 2015会议上,Facebook发布了React Native。 React.js 是 Facebook 推出的一个用来构建用户界面的 JavaScript 库。 React中,把一切东西都看成组件,而且所有组件...
2015-11-12
jQuery中DOM树操作之使用反向插入方法实例分析
本文实例讲述了jQuery中DOM树操作之使用反向插入方法。分享给大家供大家参考。具体分析如下: 使用反向插入方法 这里我们先把创建的内容插人到元素前面,然后再把同一个元素插人到文档 中的另一个位置。通常,当在jQuery中操作元素时,利用...
2015-11-13
JavaScript短路原理精简代码
js中||和&amp;&amp;的特性帮我们精简了代码的同时,也带来了代码可读性的降低,虽然高效,但请灵活使用。 在js逻辑运算中,0、&quot;&quot;、null、false、undefined、NaN都会判为false,其他都为t...
2015-11-12
jsdom 中文文档(纯翻译)
jsdom是一个纯粹由 javascript 实现的一系列 web标准,特别是 WHATWG 组织制定的DOM和 HTML 标准,用于在 nodejs 中使用。大体上来说,该项目的目标是模拟足够的Web浏览器子集,以便用于测试和挖掘真实世界...
2018-05-14
React Native 用JavaScript编写原生ios应用
ReactNative 可以基于目前大热的开源JavaScript库React.js来开发iOS和Android原生App。而且React Native已经用于生产环境——Facebook Groups iOS 应用就是基于它开发的。 Re...
2015-11-12
如何编写干净高效的CSS代码
其实CSS的学习并不困难,但在一些较为大型的项目中就显得杂乱无章,变得很难管理,尤其是不同的人编写CSS的风格总会略有不同,从团队合作的层面上来说,就更加难以沟通,所以,我们为此总结了一些如何实现高效整洁的CSS代码原则: 使用Reset但...
2015-11-12
回到顶部