0

I am using angular 5 for my project, with webpack 3.10. When i try to build the application, the build has main bundle file of approximately 2.8 MB, which creates performance issues in the production. I am using "npm run build:prod --aot --build-optimizer" to build application. I went through other stack overflow articles to figure the solution, and realised that the build i am having does not contain vendor file. i tried using different ways suggested by various articles but all in vain. please help in this. for reference i am uploading my webpack.config.js, webpack.common.js, webpack.prod.js, main.browser.ts, polyfills.ts

webpack.config.js

switch (process.env.NODE_ENV) {
  case 'prod':
  case 'production':
    module.exports = require('./config/webpack.prod')({env: 'production'});
    break;
  case 'test':
  case 'testing':
    module.exports = require('./config/webpack.test')({env: 'test'});
    break;
  case 'dev':
  case 'development':
  default:
    module.exports = require('./config/webpack.dev')({env: 'development'});
}

webpack.common.js

const helpers = require('./helpers');

/**
 * Webpack Plugins
 *
 * problem with copy-webpack-plugin
 */
const DefinePlugin = require('webpack/lib/DefinePlugin');
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlElementsPlugin = require('./html-elements-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const InlineManifestWebpackPlugin = require('inline-manifest-webpack-plugin');
const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin');
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
const ngcWebpack = require('ngc-webpack');

const buildUtils = require('./build-utils');


/**
 * Webpack configuration
 *
 * See: http://webpack.github.io/docs/configuration.html#cli
 */
module.exports = function (options) {
  const isProd = options.env === 'production';
  const METADATA = Object.assign({}, buildUtils.DEFAULT_METADATA, options.metadata || {});
  const ngcWebpackConfig = buildUtils.ngcWebpackSetup(isProd, METADATA);
  const supportES2015 = buildUtils.supportES2015(METADATA.tsConfigPath);

  const entry = {
    polyfills: './src/polyfills.browser.ts',
    main:      './src/main.browser.ts'
  };

  Object.assign(ngcWebpackConfig.plugin, {
    tsConfigPath: METADATA.tsConfigPath,
    mainPath: entry.main
  });

  return {

    entry: entry,


    resolve: {
      mainFields: [ ...(supportES2015 ? ['es2015'] : []), 'browser', 'module', 'main' ],

      extensions: ['.ts', '.js', '.json'],


      modules: [helpers.root('src'), helpers.root('node_modules')],


      alias: buildUtils.rxjsAlias(supportES2015)
    },


    module: {

      rules: [
        ...ngcWebpackConfig.loaders,


        {
          test: /\.css$/,
          use: ['to-string-loader', 'css-loader'],
          exclude: [helpers.root('src', 'styles')]
        },


        {
          test: /\.scss$/,
          use: ['to-string-loader', 'css-loader', 'sass-loader'],
          exclude: [helpers.root('src', 'styles')]
        },

        {
          test: /\.html$/,
          use: 'raw-loader',
          exclude: [helpers.root('src/index.html')]
        },

        /**
         * File loader for supporting images, for example, in CSS files.
         */
        {
          test: /\.(jpg|png|gif)$/,
          use: 'file-loader'
        },

        /* File loader for supporting fonts, for example, in CSS files.
        */
        {
          test: /\.(eot|woff2?|svg|ttf)([\?]?.*)$/,
          use: 'file-loader'
        }

      ],

    },

    plugins: [

      new DefinePlugin({
        'ENV': JSON.stringify(METADATA.ENV),
        'HMR': METADATA.HMR,
        'AOT': METADATA.AOT,
        'process.env.ENV': JSON.stringify(METADATA.ENV),
        'process.env.NODE_ENV': JSON.stringify(METADATA.ENV),
        'process.env.HMR': METADATA.HMR
      }),


      new CommonsChunkPlugin({
        name: 'polyfills',
        chunks: ['polyfills']
      }),

      new CommonsChunkPlugin({
        minChunks: Infinity,
        name: 'inline'
      }),
      new CommonsChunkPlugin({
        name: 'main',
        async: 'common',
        children: true,
        minChunks: 2
      }),



      new CopyWebpackPlugin([
        { from: helpers.root('src/assets'), to: 'assets' },
        { from: helpers.root('src/assets/img'), to: 'assets/img' },
        { from: helpers.root('src/meta')},
        { from: helpers.root('node_modules/font-awesome'), to: 'font-awesome' },
        { from: helpers.root('node_modules/primeng/resources/primeng.min.css'), to: 'resources' },
        { from: helpers.root('node_modules/primeng/resources/themes/_theme.scss'), to: 'resources/themes' },
        { from: helpers.root('src/assets/css/themes/citi-prime'), to: 'resources/themes/citi-prime' }
      ],
        isProd ? { ignore: [ 'mock-data/**/*' ] } : undefined
      ),

      /*
      * Plugin: HtmlWebpackPlugin
      * Description: Simplifies creation of HTML files to serve your webpack bundles.
      * This is especially useful for webpack bundles that include a hash in the filename
      * which changes every compilation.
      *
      * See: https://github.com/ampedandwired/html-webpack-plugin
      */
      new HtmlWebpackPlugin({
        template: 'src/index.html',
        title: METADATA.title,
        chunksSortMode: function (a, b) {
          const entryPoints = ["inline","polyfills","sw-register","styles","vendor","main"];
          return entryPoints.indexOf(a.names[0]) - entryPoints.indexOf(b.names[0]);
        },
        metadata: METADATA,
        inject: 'body',
        xhtml: true,
        minify: isProd ? {
          caseSensitive: true,
          collapseWhitespace: true,
          keepClosingSlash: true
        } : false//,
        //baseUrl: isProd ? '.' : '/'
      }),


      new ScriptExtHtmlWebpackPlugin({
        sync: /inline|polyfills|vendor/,
        defaultAttribute: 'async',
        preload: [/polyfills|vendor|main/],
        prefetch: [/chunk/]
      }),

      new HtmlElementsPlugin({
        publicPath: '/',
        headTags: require('./head-config.common')
      }),


      new LoaderOptionsPlugin({}),

      new ngcWebpack.NgcWebpackPlugin(ngcWebpackConfig.plugin),


      new InlineManifestWebpackPlugin(),
    ],


    node: {
      global: true,
      crypto: 'empty',
      process: true,
      module: false,
      clearImmediate: false,
      setImmediate: false
    }

  };
}

wepack.prod.js


const helpers = require('./helpers');
const buildUtils = require('./build-utils');

/**
 * Used to merge webpack configs
*/
const webpackMerge = require('webpack-merge');
/**
 * The settings that are common to prod and dev
*/
const commonConfig = require('./webpack.common.js');

/**
 * Webpack Plugins
 */
const SourceMapDevToolPlugin = require('webpack/lib/SourceMapDevToolPlugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HashedModuleIdsPlugin = require('webpack/lib/HashedModuleIdsPlugin')
const PurifyPlugin = require('@angular-devkit/build-optimizer').PurifyPlugin;
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');



function getUglifyOptions (supportES2015) {
  const uglifyCompressOptions = {
    pure_getters: true, /* buildOptimizer */
    // PURE comments work best with 3 passes.
    // See https://github.com/webpack/webpack/issues/2899#issuecomment-317425926.
    passes: 3         /* buildOptimizer */
  };

  return {
    ecma: supportES2015 ? 6 : 5,
    warnings: false,    // TODO verbose based on option?
    ie8: false,
    mangle: true,
    compress: uglifyCompressOptions,
    output: {
      ascii_only: true,
      comments: false
    }
  };
}

module.exports = function (env) {
  const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
  const supportES2015 = buildUtils.supportES2015(buildUtils.DEFAULT_METADATA.tsConfigPath);
  const METADATA = Object.assign({}, buildUtils.DEFAULT_METADATA, {
    host: process.env.HOST || 'localhost',
    port: process.env.PORT || 8080,
    ENV: ENV,
    HMR: false
  });


  METADATA.envFileSuffix = METADATA.E2E ? 'e2e.prod' : 'prod';

  return webpackMerge(commonConfig({ env: ENV, metadata: METADATA }), {


    output: {


      path: helpers.root('dist'),


      filename: '[name].[chunkhash].bundle.js',


      sourceMapFilename: '[file].map',


      chunkFilename: '[name].[chunkhash].chunk.js'

    },

    module: {

      rules: [


        {
          test: /\.css$/,
          loader: ExtractTextPlugin.extract({
            fallback: 'style-loader',
            use: 'css-loader'
          }),
          include: [helpers.root('src', 'styles')]
        },


        {
          test: /\.scss$/,
          loader: ExtractTextPlugin.extract({
            fallback: 'style-loader',
            use: 'css-loader!sass-loader'
          }),
          include: [helpers.root('src', 'styles')]
        },

      ]

    },


    plugins: [

      new SourceMapDevToolPlugin({
        filename: '[file].map[query]',
        moduleFilenameTemplate: '[resource-path]',
        fallbackModuleFilenameTemplate: '[resource-path]?[hash]',
        sourceRoot: 'webpack:///'
      }),



      new ExtractTextPlugin('[name].[contenthash].css'),

      new PurifyPlugin(), /* buildOptimizer */

      new HashedModuleIdsPlugin(),
      new ModuleConcatenationPlugin(),


      new UglifyJsPlugin({
        sourceMap: true,
        uglifyOptions: getUglifyOptions(supportES2015)
      }),
     
    

    ],


    node: {
      global: true,
      crypto: 'empty',
      process: false,
      module: false,
      clearImmediate: false,
      setImmediate: false
    }

  });
}

s

main.browser.js

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { environment } from 'environments/environment';

//import '../node_modules/font-awesome/css/font-awesome.min.css';
//import '../node_modules/primeng/resources/themes/omega/theme.scss';
//import '../node_modules/primeng/resources/primeng.min.css';

import './styles/styles.scss'


import { AppModule } from './app';


export function main(): Promise<any> {
  return platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .then(environment.decorateModuleRef)
    .catch((err) => console.error(err));
}


switch (document.readyState) {
  case 'loading':
    document.addEventListener('DOMContentLoaded', _domReadyHandler, false);
    break;
  case 'interactive':
  case 'complete':
  default:
    main();
}

function _domReadyHandler() {
 document.removeEventListener('DOMContentLoaded', _domReadyHandler, false);
 main();
}

polyfills.js

 import 'core-js/es6/symbol';
 import 'core-js/es6/object';
 import 'core-js/es6/function';
 import 'core-js/es6/parse-int';
 import 'core-js/es6/parse-float';
 import 'core-js/es6/number';
 import 'core-js/es6/math';
 import 'core-js/es6/string';
 import 'core-js/es6/date';
 import 'core-js/es6/array';
 import 'core-js/es6/regexp';
 import 'core-js/es6/map';
 import 'core-js/es6/weak-map';
 import 'core-js/es6/set';


 import 'classlist.js';  // Run `npm install --save classlist.js`.


import 'core-js/es6/reflect';
import 'core-js/es7/reflect';



 import 'web-animations-js';  // Run `npm install --save web-animations-js`.

import 'zone.js/dist/zone';

if ('production' === ENV) {
  // Production

} else {

  // Development
}
Robby
  • 53
  • 1
  • 12
  • See [this](https://stackoverflow.com/a/48369076/5695162) – Vikas May 17 '18 at 12:24
  • what s the actual problem?? – Robin Dijkhof May 17 '18 at 12:29
  • The problem is that when i compile the app in prod mode, the main bundle produced is too heavy in size. And on deployment, it gives performance issues. The resources are loaded in like 1000ms, but then for next 6000-7000ms, the browser keeps on showing the loading sign and only after that the application is rendered. I am already using aot and preload. But this turnaround time comes out to be 8 seconds. – Robby May 17 '18 at 14:19
  • The application is not using angular cli. So i am nit that hopeful for --vendor-chunk flag working – Robby May 17 '18 at 14:22

3 Answers3

1

You might have components/directives/modules declared in one large module, not separately.

In our project we have this issue due the historical context. Then, our main chunk includes some of the other modules, which should be in separated chunk. It is because of bad structure.

Of course lazy loaded modules are your friends, as Phani Kumar mentioned.

0

main.bundle.js size can be reduced by lazy loading which creates modules that can be loaded on demand

example :

import { Routes, RouterModule } from "@angular/router";
import { ModuleWithProviders } from "@angular/core";



import { AuthenticateService } from '../authenticate/authenticate.module';
import { FacilitiesComponent } from './components/facilities.component';


const projectsRoutes: Routes = [

    {
        path: '',
        component: FacilitiesComponent,
        canActivate: [AuthenticateService],
        canActivateChild: [AuthenticateService],
        children: [
            {
                path: 'projectlist',
                loadChildren: './projects-list/projects-list.module#ProjectsListModule'
            },
            {
                path: 'project/:pid/docmng/:fid',
                loadChildren: '../facilities/project-document/project.module#ProjectModule'
            }
        ]
    }
];



export const FacilitiesRouting: ModuleWithProviders = RouterModule.forChild(projectsRoutes);


$ npm run build

> skysite-platform-client2@0.0.0 build E:\SkySite-new\skysite-platform-client
> ng build --prod

Date: 2018-05-17T12:40:26.949Z
Hash: 0b89e566c023ac3d2fb5
Time: 156588ms
chunk {8} 8.3421be99f16d735369c2.chunk.js () 1.67 kB  [rendered]
chunk {scripts} scripts.b23d003bdba35060d12d.bundle.js (scripts) 503 kB [initial] [rendered]
chunk {0} 0.e85cc76f90b3ae5b88a6.chunk.js (common) 123 kB  [rendered]
chunk {1} 1.31677c41f09d5a650d83.chunk.js () 2.47 MB  [rendered]
chunk {2} 2.52380e8925d7e658149e.chunk.js () 89.9 kB  [rendered]
chunk {3} 3.92474b63b4fbb521a866.chunk.js () 82.5 kB  [rendered]
chunk {4} 4.0faac9190c7b1e36d1e6.chunk.js () 151 kB  [rendered]
chunk {5} 5.f3296c5e15119655f3ce.chunk.js () 1.77 kB  [rendered]
chunk {6} 6.eb8af258a302bd9d066e.chunk.js () 131 kB  [rendered]
chunk {7} 7.0521fe6f5f3714081264.chunk.js () 115 kB  [rendered]
chunk {9} 9.92a24c750caada0e26c2.chunk.js () 9.35 kB  [rendered]
chunk {10} 10.3dd571f08fa3895c8730.chunk.js () 49 kB  [rendered]
chunk {11} polyfills.165ea84e5245184acc68.bundle.js (polyfills) 98.2 kB [initial] [rendered]
chunk {12} main.b02e6fabeb708e73d01f.bundle.js (main) 500 kB [initial] [rendered]
chunk {13} styles.cdbcc00089657e555f43.bundle.css (styles) 222 kB [initial] [rendered]
chunk {14} inline.3153333f01d40d57ce09.bundle.js (inline) 1.65 kB [entry] [rendered]
Phani Kumar
  • 171
  • 7
0

i see the package.json has script of aot- 'npm run build:aot:prod'. this will enable the aot while compling the code. the command you are running might not be enabling aot in the application. Also, just make sure for AOT to work, you need to move all the private variables and methods to public, and check for dead code inside html as well if it throws an error. Hope this works.

user3847870
  • 368
  • 1
  • 6
  • 21