4

I am developing pretty big SPA (final ~30MB) and unfortunately one of requirements is that an app has to be released as one big html file. I use webpack to connect all pieces together.

Currently I am facing a problem with performance (some libraries are quite big ones). They "eat" a lot of ram and affects loading time due to code evaluation in browser. I would like to postpone it and evalute only these modules which are necessary at main screen of app.

My idea is to use the same mechanism like webpack does for sourcemaps:

https://webpack.js.org/configuration/devtool/ (eval-source-map)

Webpack simply puts code within eval("code of module") which prevents automatic evaluation by Javascript engine. Of course this code can't be minified and there is also sourcemap attached as base64 to the end. I would like to do same without sourcemaps and including uglification. Moreover I have an idea to reduce size of application by compressing sources so eventually it would be eval(gz.decompress("code of module")).

It will be a huge change in application so before I am going to reinvent a wheel I would like to ask you:

  1. Does it make sense from problem point of view?
  2. Do you know any existing solutions?
  3. Do you suggest to use any existing components from webpack like:

https://webpack.github.io/docs/code-splitting.html

or write own solution from scratch (loader/plugin).

Suprido
  • 525
  • 7
  • 17
  • did you monitor the amount of "JavaScript memory" in Chrome Task manager? I had some similar issue and i abandoned the idea of `gz.decompress` because, at the end, it was client-side much heavier as serving gzipped content. – deblocker May 29 '17 at 08:13

2 Answers2

5

Don't do that what you want!

If you do want to find a weird trick to get what you want, try including your big JS file dynamically. See here or google jquery getscript. No additional Webpack actions required.

If not, please, continue reading.


You're dealing with the problem from the wrong perspective.

First, make sure you are doing all the obvious HTML/HTTP stuff:

  1. You're downloading the gzip-ed version of the file (if not, google http script gzip)
  2. You're including the <script> tag at the end of the body. This will start downloading and parsing JS only after HTML has been rendered.

Then, the most important, try to figure out where is the 30MB coming from. It's unlikely a fair sum of all your big fat dependencies. Usually, it's a particular bloated library (or two). Make sure you use got instead of request because the least is bloated. Find alternatives for the out-sized dependencies.

No single SPA in the world should have a 30MB JS bundle. I'm assuming your project isn't very large because otherwise it would be business critical and you would invest into providing a decent back-end strategy (e.g. code splitting, dead code elimination, etc.).

Kirill Rogovoy
  • 583
  • 3
  • 11
  • Actually this project is huge, it contains multiple client side operations like parsing/linting xml, custom language validators like antler etc. You suggest to use got/request, still you don't get the point. I CAN'T download more than one file during whole app lifecycle. I know it is not good approach of building SPA, I am just dealing with existing architecture and its constraints. – Suprido May 29 '17 at 09:21
  • Then my general point is to reduce the size of that file you download if possible. My got/request suggestion is just a quick example of how one dependency may eat up megabytes of your bundle. If you can't download more than one script even dynamically (which is an odd limitation), then there isn't much you can do except working on that giant file to load it faster. – Kirill Rogovoy May 29 '17 at 09:47
  • 1
    @Suprido If you're seriously serving up a 30MB SPA, it's your duty to report back that attempting to squeeze it into a single HTML file is a terrible idea. There really isn't any real reason to butcher architecture like this merely because someone above thinks its a 'good idea'. Push back on impossible requirements. – Rob May 30 '17 at 03:42
2

1) The similar problem can be solved with Webpack code splitting functionality.

The idea is that you don't load route specific code and libraries until the user accesses the specific page.

2) Take a look at this: script-ext-html-webpack-plugin, looks very promising to do these kinds things. For example, defer options would be for modules or scripts that you want to delay the execution. Async would be for scripts that you want to execute as HTML gets executed. Be careful though about race conditions.

3) You mentioned that you use libraries that are so big, make sure you use Webpack with tree shaking. If you use the old Webpack (version 1.*) which does not have tree shaking, you should try to optimize imports manually. For example, instead of import _ from 'lodash' it would be import map from 'lodash/map'.

4) You also mentioned that it is the ram that is the problem, so how compression can help ram? compression can help the browser to retrieve it faster.

5) The other idea would be:

  1. Load the scripts that you need for the home page
  2. execute them. at this point, the user sees the functioning page
  3. then behind the scenes load other scripts slowly without the user to notice it.
  4. evaluate loaded code as it will become needed for the user.
Shota
  • 6,910
  • 9
  • 37
  • 67
  • It is obvious when your application is not released as one html file which containts everything. With code splitting I can use jsonp mechanism but not in this case. – Suprido May 22 '17 at 12:07
  • You mentioned that you use libraries that are so big, do you use Webpack with tree shaking (https://webpack.js.org/guides/tree-shaking/) ? – Shota May 22 '17 at 13:46
  • So you have an opportunity to not load any of the code if it is not needed, so why do you load it at all if you don't want to evaluate it? You also mentioned that it is the ram that is the problem, so how compression can help ram? compression can help the browser to retrieve it faster. The other idea would be: 1) Load the scripts that you need for the home page 2) execute them. at this point user sees the functioning page 3) then behind the scenes load other scripts slowly without the user to notice it. 4) evaluate loaded code as it will become needed for the user. – Shota May 22 '17 at 14:54
  • You forget about main constraint, this is a one file web application (it is a key requirement due to lack of resources on back-end). I just can't load more scripts behind the scenes, I already have all of them. All I want is to postpone evaluation by browser, JIT etc. This will speed up starting application up and will consume less memory at first. – Suprido May 22 '17 at 19:35