Is there a lightweight version of F7 for ReactJS?

There two things that may be fixed within F7, as it rules now, it can be even much better if they were fixed.

  1. The size of the bundle and the build time of it for F7 V4 with Webpack is extremely too much.
    If I take just another cordova project and implement it Webpack, the build time is almost 5s. In F7 the build time can reach to 3 min. The size of the bundle is something like 13mb thats alot.
    How can we make this more efficient?
    Note: I am not complaining at F7 at all I really want to list this point to make this product even better than it is now.

  2. This line in the webpack entry: Framework7.use(Framework7React) causes the hot reload to not working - actually it fully refreshes the whole app.
    I have to comment it, then hot reload actually works, but when I face a syntax error or something I have to uncomment this again and force a full refresh. Then again, need to comment it for hot reload to function.
    Hot reload is one of the most important feature of webpack + react. Maybe the most important.
    If you ever created a bootstrap project based with webpack + react and enjoyed the hot reload you probably understand that this is extremely crucial feature. In addition to that said, when commenting this line, only changes in rendered files (.jsx) applies automatically, but if you update something in the class or any .js file or any service, it doesn’t hot reloads and you should again commit a full refresh - all the way down with uncommenting the line, refresh then comment it again.

F7 is probably 10x better than Ionic in any exam, but these two things must be solved ASAP.
How can we progress from here? any suggestions please?

You are doing something wrong then. The whole F7’s JS (with all components included) is about 1Mb. Maybe you are not using production ENV or embedding source maps? Check your build pipeline

Just a thought… You may want to delete .map files in your production build if you have them on, they’re about 2.5x your .js files.

But honestly 3 mins is a lot, unless you’re compiling a HUGE project I’d expect the build time to be much less than that even with a not-so-hot computer.

But I don’t talk only on production, I talk either on dev mode, the whole build and npm start takes long time.
These are the build configurations, tell me if I am doing something wrong:

webpack.common.js:
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');

function resolvePath(dir) {
  return path.join(__dirname, '../', dir);
}

const env = process.env.NODE_ENV || 'development';
const target = process.env.TARGET || 'web';
const isCordova =  target === 'cordova';

module.exports = {
  entry: [
    'babel-polyfill',
    './src/webpack.entry.js',
  ],
  output: {
    path: resolvePath(isCordova ? 'cordova/www' : 'www'),
    filename: 'app.js',
    publicPath: '',
  },
  resolve: {
    extensions: ['.js', '.jsx', '.json'],
    alias: {
      '@': resolvePath('src'),
      "@assets": resolvePath('src/assets'),
    },
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: 'babel-loader',
        include: [
          resolvePath('src'),
          resolvePath('node_modules/framework7'),
          resolvePath('node_modules/framework7-react'),
          resolvePath('node_modules/template7'),
          resolvePath('node_modules/dom7'),
          resolvePath('node_modules/ssr-window'),
        ],
      },
      {
        test: /\.css$/,
        use: [
          (env === 'development' ? 'style-loader' : {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../'
            }
          }),
          'css-loader',
          'postcss-loader',
        ],
      },
      {
        test: /\.styl(us)?$/,
        use: [
          (env === 'development' ? 'style-loader' : {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../'
            }
          }),
          'css-loader',
          'postcss-loader',
          'stylus-loader',
        ],
      },
      {
        test: /\.less$/,
        use: [
          (env === 'development' ? 'style-loader' : {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../'
            }
          }),
          'css-loader',
          'postcss-loader',
          'less-loader',
        ],
      },
      {
        test: /\.(sa|sc)ss$/,
        use: [
          (env === 'development' ? 'style-loader' : {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../'
            }
          }),
          'css-loader',
          'postcss-loader',
          'sass-loader',
        ],
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: 'images/[name].[ext]',
        },
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: 'media/[name].[ext]',
        },
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: 'fonts/[name].[ext]',
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: './index.html',
      template: './src/index.html',
      inject: true,
      minify: env === 'production' ? {
        collapseWhitespace: true,
        removeComments: true,
        removeRedundantAttributes: true,
        removeScriptTypeAttributes: true,
        removeStyleLinkTypeAttributes: true,
        useShortDoctype: true
      } : false,
    }),
    new MiniCssExtractPlugin({
      filename: 'app/app.css',
    }),
    new CopyWebpackPlugin([
      {
        from: resolvePath('static'),
        to: resolvePath(isCordova ? 'cordova/www/static' : 'www/static'),
      },
    ])
  ],
};

webpack.dev.js:
const webpack = require('webpack')
const merge = require('webpack-merge')
const common = require('./webpack.common')
const CleanPlugin = require('./webpack-plugins/clean-plugin/clean-plugin-class')

let target = process.env.TARGET || 'web'
let config = {
  ...require('../config/default.json'),
  ...require('../config/development.json')
}

module.exports = merge(common, {
  mode: 'development',
  devServer: {
    hot: true,
    open: true,
    compress: true,
    contentBase: '/www/',
    disableHostCheck: true,
    watchOptions: {
      poll: true,
    },
  },
  devtool: 'eval-source-map',
  plugins: [
    new webpack.DefinePlugin({
      "process.env.NODE_ENV": JSON.stringify("development"),
      "process.env.TARGET": JSON.stringify(target),
      config: JSON.stringify(config)
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(),
    new CleanPlugin(target)
  ]
})

webpack.prod.js:
const webpack = require('webpack');
const merge = require('webpack-merge');
const common = require('./webpack.common');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin');

let target = process.env.TARGET || 'web'
let config = {
  ...require('../config/default.json')
}
if (process.env.NODE_ENV=="staging") {
  config = {
    ...config,
    ...require('../config/staging.json')
  }
}
if (process.env.NODE_ENV=="production") {
  config = {
    ...config,
    ...require('../config/production.json')
  }
}

module.exports = merge(common, {
  mode: 'production',
  devtool: 'eval-source-map',
  plugins: [
    new webpack.DefinePlugin({
      "process.env.NODE_ENV": JSON.stringify("production"),
      "process.env.TARGET": JSON.stringify(target),
      config: JSON.stringify(config)
    }),
    new UglifyJsPlugin({
      uglifyOptions: {
        compress: {
          warnings: false,
        },
      },
      sourceMap: true,
      parallel: true,
    }),
    new OptimizeCSSPlugin({
      cssProcessorOptions: {
        safe: true,
        map: { inline: false },
      },
    }),
    new webpack.optimize.ModuleConcatenationPlugin(),
  ]
});

Note that in the prod file I use eval source map since when I remove it, or do anything else, on iOS the app is crashes after the first launch - so if I shut down the app and reopen or just minize and resume, the app stucks on the splash screen due to CORS and CSP violations.
I have tried to do many things in order to fix this issue on iOS but really nothing helped.

Anyway, even when it doesn’t use source map at all, the size of the bundle and its time are very very slow.
They even make my whole pc to running slow.

As I said I have just opened a blank new project with Webpack and React.js and everything runs smooth - the build times are really fast.

Maybe things have been changed since V4 to what is the current one?

For my second question, should I post it in a different thread? I don’t see any relations to it… This is also extremely important feature of webpack, if not the most.

Still… 17.5mb of bundle, how come?

You are still bundling something wrong, can’t even imagine how can it be 17.5 MB. Maybe you are inlining images into your bundle?

Here, made with F7-React https://f7appstore.netlify.app/ total JS code is ~200KB (minified and gzipped)

Okay, I will try to give how much information as I can:

This is my webpack.common.js:

const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');

function resolvePath(dir) {
  return path.join(__dirname, '../', dir);
}

const env = process.env.NODE_ENV || 'development';
const target = process.env.TARGET || 'web';
const isCordova =  target === 'cordova';

module.exports = {
  entry: [
    'babel-polyfill',
    './src/webpack.entry.js',
  ],
  output: {
    path: resolvePath(isCordova ? 'cordova/www' : 'www'),
    filename: 'app.js',
    publicPath: '',
  },
  resolve: {
    extensions: ['.js', '.jsx', '.json'],
    alias: {
      '@': resolvePath('src'),
      '@app': resolvePath('src/app'),
      '@locales': resolvePath('src/app/locales'),
      '@properties': resolvePath('src/app/properties'),
      '@services': resolvePath('src/app/services'),
      '@helpers': resolvePath('src/app/services/helpers'),
      "@cordova": resolvePath('src/app/cordova'),
      '@pages': resolvePath('src/pages'),
      "@shared": resolvePath('src/shared'),
      "@forms": resolvePath('src/shared/forms'),
      "@structure": resolvePath('src/structure'),
      "@assets": resolvePath('src/assets'),
    },
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: 'babel-loader',
        include: [
          resolvePath('src'),
          resolvePath('node_modules/framework7'),
          resolvePath('node_modules/framework7-react'),
          resolvePath('node_modules/template7'),
          resolvePath('node_modules/dom7'),
          resolvePath('node_modules/ssr-window'),
        ],
      },
      {
        test: /\.css$/,
        use: [
          (env === 'development' ? 'style-loader' : {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../'
            }
          }),
          'css-loader',
          'postcss-loader',
        ],
      },
      {
        test: /\.styl(us)?$/,
        use: [
          (env === 'development' ? 'style-loader' : {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../'
            }
          }),
          'css-loader',
          'postcss-loader',
          'stylus-loader',
        ],
      },
      {
        test: /\.less$/,
        use: [
          (env === 'development' ? 'style-loader' : {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../'
            }
          }),
          'css-loader',
          'postcss-loader',
          'less-loader',
        ],
      },
      {
        test: /\.(sa|sc)ss$/,
        use: [
          (env === 'development' ? 'style-loader' : {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../'
            }
          }),
          'css-loader',
          'postcss-loader',
          'sass-loader',
        ],
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: 'images/[name].[ext]',
        },
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: 'media/[name].[ext]',
        },
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: 'fonts/[name].[ext]',
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: './index.html',
      template: './src/index.html',
      inject: true,
      minify: env === 'production' ? {
        collapseWhitespace: true,
        removeComments: true,
        removeRedundantAttributes: true,
        removeScriptTypeAttributes: true,
        removeStyleLinkTypeAttributes: true,
        useShortDoctype: true
      } : false,
    }),
    new MiniCssExtractPlugin({
      filename: 'app/app.css',
    }),
    new CopyWebpackPlugin([
      {
        from: resolvePath('static'),
        to: resolvePath(isCordova ? 'cordova/www/static' : 'www/static'),
      },
    ])
  ],
};

This is my webpack.dev.js:

const webpack = require('webpack')
const merge = require('webpack-merge')
const common = require('./webpack.common')
const CleanPlugin = require('./webpack-plugins/clean-plugin/clean-plugin-class')

let target = process.env.TARGET || 'web'
let config = {
  ...require('../config/default.json'),
  ...require('../config/development.json')
}

module.exports = merge(common, {
  mode: 'development',
  devServer: {
    hot: true,
    open: true,
    compress: true,
    contentBase: '/www/',
    disableHostCheck: true,
    watchOptions: {
      poll: true,
    },
  },
  // devtool: 'eval-source-map',
  plugins: [
    new webpack.DefinePlugin({
      "process.env.NODE_ENV": JSON.stringify("development"),
      "process.env.TARGET": JSON.stringify(target),
      config: JSON.stringify(config)
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(),
    new CleanPlugin(target)
  ]
})

This is my webpack.prod.js file:

const webpack = require('webpack');
const merge = require('webpack-merge');
const common = require('./webpack.common');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin');

let target = process.env.TARGET || 'web'
let config = {
  ...require('../config/default.json')
}
if (process.env.NODE_ENV=="staging") {
  config = {
    ...config,
    ...require('../config/staging.json')
  }
}
if (process.env.NODE_ENV=="production") {
  config = {
    ...config,
    ...require('../config/production.json')
  }
}

module.exports = merge(common, {
  mode: 'production',
  devtool: 'eval-source-map',
  plugins: [
    new webpack.DefinePlugin({
      "process.env.NODE_ENV": JSON.stringify("production"),
      "process.env.TARGET": JSON.stringify(target),
      config: JSON.stringify(config)
    }),
    new UglifyJsPlugin({
      uglifyOptions: {
        compress: {
          warnings: false,
        },
      },
      sourceMap: true,
      parallel: true,
    }),
    new OptimizeCSSPlugin({
      cssProcessorOptions: {
        safe: true,
        map: { inline: false },
      },
    }),
    new webpack.optimize.ModuleConcatenationPlugin(),
  ]
});

This is my webpack.entry.js file in the src dir:

// Import React and ReactDOM
import React from 'react'
import ReactDOM from 'react-dom'

// Import Framework7
import Framework7 from 'framework7/framework7.esm.bundle.js'

// Import Framework7-React Plugin
import Framework7React from 'framework7-react'

// Import App Component
import App from '@app/app-class'

// Icons
import '@assets/css/icons.css'
import '@fortawesome/fontawesome-free/css/all.css'

// RootScope 
import RootScope from '@properties/rootScope'

// Init F7 React Plugin
Framework7.use(Framework7React)

RootScope.language = window.localStorage.getItem("language") || navigator.language
if (RootScope.language!="he-IL" && RootScope.language!="en-US") RootScope.language = "he-IL"
localStorage.setItem("language", RootScope.language)

// RootScope.language = "he-IL"

if (RootScope.language=="he-IL") {
  import('framework7/css/framework7.bundle.rtl.css').then(() => {
    import('@locales/he-IL/translations.json').then(module => {
      import('@app/app.scss').then(() => {
        RootScope.t = module.default
        initReactApp()
      })
    })
  })
}
if (RootScope.language=="en-US") {
  import('framework7/css/framework7.bundle.css').then(() => {
    import('@locales/en-US/translations.json').then(module => {
      import('@app/app.scss').then(() => {
        RootScope.t = module.default
        initReactApp()
      })
    })
  })
}

function initReactApp() {

  // Mount React App
  ReactDOM.render(
    React.createElement(App),
    document.getElementById('app'),
  )

}

if (process.env.TARGET!="cordova" && process.env.NODE_ENV=="development") {
  module.hot.accept()
}

Now thats the files that have been generated to the www dir:

Before I commented the devtool in the webpack config files, it was 17mb now its 6mb which is still alot.

There are the consumptions:

import React from 'react' // 100kb
import ReactDOM from 'react-dom' // 800kb
import Framework7 from 'framework7/framework7.esm.bundle.js' // 2.2mb
import Framework7React from 'framework7-react' // 1.3mb

// both take arround 2mb:
import '@assets/css/icons.css' 
import '@fortawesome/fontawesome-free/css/all.css'

There no any heavy images or media
What do I miss in here?

Try to install this plugin and see what is actually in your bundle https://github.com/webpack-contrib/webpack-bundle-analyzer