preface

As mentioned in React Mobile Web Extreme Optimization, build tools are an important part of front-end optimization. React’s recommended build tool is Webpack. In this article we will discuss how to optimize the React application during the Webpack build.

If you haven’t seen webPack Use Optimizations (Basics), check it out, because optimizations for React often rely on webPack’s most basic optimizations. Furthermore, treating Webpack as a build here may raise objections from those who would position Webpack as a packaged tool. But in the actual project, in addition to the combined graph, the home-school group project has taken Webpack as the most core construction tool.

The results of this article will eventually be transformed into an open source Boilerplate, steams-React.

The directory structure

Building tools are inseparable from the design of the directory, we need to arrange the location of the number file to facilitate the construction of the project. The files at the lower level of the SRC directory, except the page folder is the main logical file of react, other files such as IMG, JS, CSS and libs are all public files used by various pages, such as utils and report, etc.

In the React directory, the common folder rotates public React files, such as public Component, middleware, etc. The other folders are the body logic and resources for each page, and the corresponding HTML files for the page.

As the React+ Redux solution is adopted by the home and school groups, the name of our folder also reflects the characteristics of this solution. First of all, in terms of components, we prefer Redux’s smart Components (Container) and dump components, so they are placed under the Container and Component folders respectively. What goes under the Container and Component folders? We place the component-related logical JS and style SCSS files. Instead of putting images in the component layer, we put them in the page layer because we share a lot of images between different components of our business, so it’s more appropriate to put them in a higher layer.

Store, Reducer, Action and connect are all related to Redux, the data processing framework. A folder such as root is the main entry point of the project and contains root.dev.js and root.prod.js, which are used to distinguish between the components that need to be introduced in the development environment and the production environment.

If you also use react-Router, you may need to add a route folder that contains the route configuration files for your project.

This file architecture is more complex than traditional gulp and Grunt, but it is more in line with the development idea of React + Redux.

Optimization points for React

Two sets of build configurations need to be maintained

Webpack is different from Gulp and Grunt in that the former is a configuration build (although plugins can do some of the process) and the latter are task-based builds. In the past, when using Gulp, we would write some tasks specifically for development or production environment, and then create two task streams to deal with the construction of development and production environment respectively. Webpack also has to deal with building development and production environments, so there are two sets of configurations to implement.

If you are not sure which tasks should be placed in development and which in production, you can refer to the Performance Optimization Trilogy: Building for details. If you don’t know how to distinguish between development and production environments, see WebPack Usage Optimization (Basics) (github.com/lcxfs1991/b…

There are two points I want to make here:

  • First, it is recommended that the configuration of the development environment be a subset of the production environment. After all, we think about development first, and then we think about production deployment after development. In this case, we can directly copy the configuration of the development environment with object. assign. You can also add methods to handle configuration updates. For example, if the production environment wants to add new plugins, I refer to the boilerplate I saw earlier:
devConfig.addPlugins = function(plugin, opt) {
    devConfig.plugins.push(new plugin(opt));
};
Copy the code
  • The second issue is whether React uses external chains. Node_modules is recommended for development, because it has many useful errors and prompts to quickly find problems during development. The production environment, of course, is off-chain, otherwise Webpack will pack React and your business logic together on its own, much larger than it would be separately.

React builds for ES2015

ES2015 has been hot for the past two years. Let’s have a taste. The biggest advantage of using ES2015 is that you can use a lot of convenient features, but the minor disadvantage is that you may ignore the ES5 script, which in many cases clearly shows how React is implemented and is more helpful in understanding the framework and principles.

On the other hand, with these new features, there is a bit of uncertainty about what the conversion will look like and how compatible the code will be (see “What does Babel Actually convert the code to?” for details). ]). Sure enough, we found an error after publishing the list page:

Uncaught TypeError: Cannot assign to read only property '__esModule' of #.

The solution is to use ES2015-loose for Babel compilation instead of ES2015 preset. The specific conversion code is as follows:

You can write your build configuration like this in Webpack loaders

{ test: /\.js? $/, loader: 'Babel ', query: {plugins: [' transformer-decorators-legacy '], // [ 'es2015-loose', // ES2015 loose mode 'react', ] }, exclude: /node_modules/, }Copy the code

In addition, we also found that every Webpack module, when compiled, produces a bunch of similar code:

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"); }}...Copy the code

These are snippets of code generated by Babel escaping ES2015. At times, each module generates close to 1KB of this code. If you have a large number of components, such as a home school with more than 20 components, the details page may have more components, and the total number of components may be closer to 20KB of repetitive code. On the PC, 20KB is fine, but on the mobile, bundle size is a bit of a bargain. It is recommended to write a Webpack plugin/loader to remove this code.

How do I hotreplace the CSS

When packaging CSS, the ExtractTextPlugin is used to make CSS generate a separate file. But if you want CSS to be hot-substitutable, remove the plugin and make styles inline when developing your environment.

Webpack uses DevTools's inline-source-map mode with caution

In this mode, a large string is inlined to locate bugs. You can enable it when checking errors, but you are advised to disable it when not checking errors. Otherwise, the package loaded during development will be very large.

If you don't use DevTools, you'll see the bundle synthesized, with tens of thousands of lines of code, and no idea which file it is:


.

Once you use it, you'll know exactly which file and line you're in:


If you cannot use the server to build, please ask your partner to develop the same path

Webpack bugs cause md5 compilations to be inconsistent if the local development directory paths are inconsistent. So server builds are recommended.

Composite view of the React project

During the construction of the project, I tried using Webpack as a composite plug-in, but abandoned it because it was not mature enough and considered switching to gulp's composite plug-in instead. Since the home school group function page is a multi-page project, each page will have a combined picture, so we choose gulp.spritesmith-multi.

In previous gulp build projects, the CSS was placed in one layer, and the paths to reference images were placed in one CSS. For the React project, each component has its own index.js and index. SCSS. Since the composite plugin can only mark a single image path, if the composite reference occurs at different levels of the Component, The gulpfile file will not be found, so we will put the reference in the container style file to solve the problem.

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);
});
Copy the code

Here's how to reference styles:

@import".. /.. /.. /css/sprites/list_s"; . .info-right.tiku-status3 { @include sprite($remark); }Copy the code

Copy the code