Framework: Putting Aurelia on a diet

Created on 15 Jan 2017  ยท  61Comments  ยท  Source: aurelia/framework

There is a lot of awareness around "modern" web app sizes and their libs recently. I am assuming this is obvious so won't repeat all here (download times, parsing/startup time, mobiles, etc.).
Some libraries make efforts to reduce size, for example jQuery has slimmed down by stopping supporting old browsers and removed deprecated apis. It also offers non-bundled sources so that you can import only the modules you actually use.

Some of Aurelia's competitors take pride in being very small: React is 45K gzipped, Vue.js 17K and Inferno only a tiny 9K!
Of course, the comparaison is never completely fair because it depends on supported targets and the feature set (for example none of the numbers above include a router). But it gives an idea what to aim for.

Aurelia is not lightweight. Here's an analysis of a basic, empty app. The only dependency in project.json is aurelia-bootstrapper. It was built with Webpack 2.2 and minified with default settings. Spoiler: the whole thing is 85Ko gzipped, almost 10 times Inferno.

Here's a picture of the whole thing, the treemap is proportional to gzipped size:
aurelia-size

And for reference, some modules:

Module | Raw | Minified | Gzipped |
---|---|---|---|
aurelia-binding | 166K | 91K | 20K |
aurelia-templating | 146K | 69K | 18K |
aurelia-templating-resources | 75K | 37K | 16K |
aurelia-router | 54K | 26K | 7K |
aurelia-hot-module-reload | 40K | 16K | 5K |
aurelia-polyfills | 22K | 10K | 4K |
Full bundle | 700K | 343K | 85K |

There is essentially two ways to improve: (1) enable people to only include what they actually use (granular modules and/or tree shaking). This is already discussed in other issues. (2) reduce the size of the code.

I noticed a few low-hanging fruits:

  • When using aurelia-webpack-loader, module aurelia-hot-module-reload is there even when building for prod. This is 5K (gzip) wasted, half the size of inferno!
  • aurelia-polyfills is 3.5K (gzip), even though the target platform might not need them or the build might include them externally anyway.

There is definitely potential for improvement in the code but unless obvious wasted or duplicated code is found it will be made of many small wins. :( binding, templating and templating-resources are really the lion's share in the core packages.

enhancement

Most helpful comment

In addition to the original plans, we're also currently experimenting with a compile-time transform version of Aurelia that would obviate the need for huge parts of the framework, move certain runtime components into the build process and allow dropping all parts of the framework unused by the app. We're early on in the experimental process, but you can watch via the experiment repo.

All 61 comments

I actually don't think 85k Gzipped is bad at all for such a full-featured framework. None of the other competitors you mention come anywhere close to us on feature-richness. But, yes, we can probably do a few things to improve this. Here are some thoughts off the top of my head...

There are things we could pull out of binding, such as the Unparser and the SVG support. These could be put into separate modules. Unparser is usually only useful for debugging. Not everyone needs SVG binding either. This would be a breaking change, though it wouldn't affect most people and would be easy to migrate to.

Templating has some code duplication in the view compiler. There may be other things in there as well.

Across everything, if we move to TS and then centralize the helpers, we could probably reduce the amount of boilerplate the transpiler is generating on every library.

For templating-resources, I think it would be nice to have an easy way to include only what you want, since everyone doesn't use all of what is in there.

Polyfills could be replaced with an empty module, depending on what browsers need to be supported. Perhaps we could do something smart there and actually generate the polyfills module at build time based on what browsers the developer wants to support?

I'd like to see the CLI use tslib when TypeScript is selected.
Having an option of what browser support is needed when creating a project would also be great, and could then be used to optimize polyfills.

There are things we could pull out of binding, such as the Unparser and the SVG support. These could be put into separate modules. Unparser is usually only useful for debugging. Not everyone needs SVG binding either. This would be a breaking change, though it wouldn't affect most people and would be easy to migrate to.

A few unused things might be dropped by tree-shaking (I have yet to test that. When I do I'll report how much is actually dropped).

With webpack, another backward-compatible way to drop optional stuff could be to use defined variables. If we wrap SVG support in a big if (!NO_SVG) it won't change behavior but when someone builds with NO_SVG defined the code will be dropped.

Using similar techniques, I think the bootstrapper could be much smaller in a webpack build (it's not that big unfortunately).

Of course the clean way is to extract SVG in its own module and require an import in code that wants support. As you noted that would be a breaking change. BTW if we make several modules optional, a good way to help back-compat would be to reference them all in a new aurelia-compat module that could be imported when people migrate to the new version (or combine that with a single define to avoid the breaking change at all, yet easily let people opt in the leaner build).

I noticed that aurelia-pal-browser could easily be reduced. It contains unused variables, inefficient patterns and it tends to not assume anything at all. Why/how could global.console not be defined? Do we support a browser that doesn't have a console?

@AshleyGrant Thanks for the explanation! I could swear I've used the console but it turns out console only exists in old IE _after you've opened the F12 tools!_ That kind of shit is worthy of a comment in the code, though.

Yeah, it bit me back in the day. Unfortunately, IE9 is an important thing for us to support for the foreseeable future. But hey, we welcome a PR w/that comment :-P

@jods4 the whole story about sizes is pretty worthless if you can't compare a common set. E.g. out of fun I started building my own ViewOnly Library for a specific project and guess what, unminified code version is 4kb. If I'd remove even more error handling and additional helper-functions I could bring it down to 3kb for sure. This was enough for said specific use case but would never be something I'd propose to more general projects.

So the bottom line is its never about the size alone. If thats all you care about going VanillaJS is certainly the best option with 0 kb. A templated approach vs all JS like React and alike does clearly has to have differences, last but not least in size.

What we can do is clearly cut things down or as described by @EisenbergEffect make things even more optional (SVG and stuff). But I wouldn't ever expect Aurelia to be somewhere near to Inferno or Svelte size-wise, as the target audience/use cases are not the same for a Framework vs a View Library.

Btw. take a look at these size-comparisons. https://gist.github.com/Restuta/cda69e50a853aa64912d

@zewa666 I don't agree that looking at Aurelia size and trying to make it smaller worthless.

For example by doing this exercice I could spot that aurelia-hot-module-reload, aurelia-logging-console were there although they shouldn't. That's an easy 16+Ko (minified) that can be saved.

The smaller the framework, the better startup times, working sets, etc.
Some people advocate a 300K target size for mobile web apps, that's tight!
Like performance, small size doesn't happen by accident and some focus is needed.

I acknowledged that comparing frameworks size was not really doable because of different supported targets and features.

Yet it's not completely worthless either. _Just_ aurelia-templating, without the binding module, the PAL, the polyfills, the task queue or the templating resources, is already _twice the size of Inferno_. That doesn't compare favorably.

Hey everyone, this basket of puppies was hoping this won't turn in to an argument. Let's all be nice. #LeadingTheCommunity #NailedIt

image

It is an apples to oranges comparison in many cases, but I think we can definitely improve in this area. It's worth it to spend some time looking at what we could do to improve this in the future.

@jods4 I didn't say it's worthless to try to reduce the size, actually this is something we should do and your examples are good. What is worthless though is to compare apples to oranges. Use cases matter as well and Aurelia simply covers different ones then Inferno etc.

As such I just meant that you shouldn't expect Aurelia ever to compete with 9kb sized Libs.

@zewa666 of course I don't expect Aurelia to become 9Ko in size! ๐Ÿ˜†

I was looking at my webpack builds and I definitely think we can reduce the base size of Aurelia.

The comparisons were only for some context and I agree they are not 100% meaningful. Yet I don't think we should completely ignore them.

For instance I have previously looked at Vuejs. It's _very_ similar to Aurelia and supports a lot of identical features. Comparing its size with Aurelia's templating+binding is not so far fetched and Vuejs is only 17Ko.

React is interesting because it's one of the most popular alternatives to Aurelia (second to Angular I guess). So it's interesting to know where we stand, even if the philosophy is very different. I am well aware that React doesn't really have a "templating" engine as everything is compiled code that produces fragments.
I also find it amazing how Inferno can do pretty much the same thing as React (when you include inferno-compat), in 9Ko vs 45Ko...

Someone cited Svelte above. It has no runtime, 0B. It's still interesting because we can copy those ideas. Compiling views at build time would improve perf and if an application has all its views pre-compiled, then we could remove the view-compiler from the output and reduce the size!

Some news, I finally managed to get tree shaking to work properly with Aurelia.

| modules | raw | min | gzip |
|---|---|---|---|
| commonjs, no tree shaking | 700K | 343K | 85K |
| ES, tree shaking | 669K | 322K | 80K |

My comments:

  • it's great to have tree shaking in the build, because it can make huge differences with some libraries that are optimized so that you only include what you use.
  • it was an easy win of 21K at runtime. Of course if the app actually uses the removed parts they would come back.
  • 21K is good but is far from great given that the app uses (almost) nothing. The main problem is that pretty much all parts of Aurelia are included by way of aurelia-loader and conservatively all of that is considered "used". I already had to do some instrumentation to at least drop some of aurelia-framework...

Important current tree-shaking limitation, for reference: webpack/webpack#2899
Underlying cause: mishoo/UglifyJS2#1261
This is a huge hindrance to remove dead code from Aurelia as it's heavily based on classes.
Side-note: this is without even considering decorators, which are going to make matters worse.

@jods4 Would you mind sharing your webpack configuration where you enabled tree shaking for Aurelia?

@lu4nm3 it's based on the new set of plugins I've made. I am holding off pushing the last (important) changes to jods4/aurelia-webkit because they need a few PR in core Aurelia packages to work out-of-the-box.
Don't worry, in time they will be the official webpack plugins to build Aurelia projects.

Webpack 2.2 always does tree-shaking, but:

  • it only works if you use ES import style.
  • Uglify will then drop dead code but it doesn't support ES6 yet.

So to take advantage of that you must use ES6 import with ES5 code. This is the native-modules aurelia distribution and is also achievable in recent TS releases with { target: "es5", modules: "es6" }.

The tricky part with Aurelia is that without my plugins anything loaded by aurelia-loader is not subject to tree shaking (it's not import) and this includes aurelia-framework at bootstrap time so nothing is dropped in Aurelia :/

I have submitted a PR to remove hot-module-reload (which is fat) from builds that don't use --hot, typically production builds.

Nice results, the size of the package is now 307K (min) 77K (gzip).

As far as easy build wins are concerned, there's not much left:

  • There's an issue in uglify that prevents removing unused classes in ES5, not much we can do about that.
  • If you can afford to target ES6, code should be smaller. But note that for now uglify doesn't support ES6 ๐Ÿ˜ž

The remaining wins need code changes:

  • Optimize code for size (from what I have seen in my build experiments there are many opportunities);
  • Some unused dependencies can't be removed because of the underlying design, e.g. all the templating resources.

FYI, here's the updated treemap of how the 77K are spent, wrt gzip size:
size

@jods4 perhaps we could try using the original src Aurelia files and compile them using Webpack via TS (allowJs: true) instead of the precompiled native-modules (just for testing). Would be interesting to see how much we could gain without the babel helpers duplicated everywhere.

@jods4 is Babili maybe something we could use instead for minification?

Another option would be Closure Compiler. It supports ES6, while Uglify still doesn't. I saw there was a TypeScript fork which emits Closure Compiler annotations that the compiler can then work with to minify and do its own tree-shaking. Not sure how stable / feature complete it is though.

@niieani I think duplicate helpers will be removed with the current plan to move all sources to TS?
I could experiment by pointing webpack to src, except that it needs a loader that I am not familiar with (since the sources are annotated JS currently). If someone wants to try once the new plugins are out, you're welcome.

@zewa666 webpack is very extensible. Uglify is just a built-in plugin, you can substitue anything you want.

@niieani ES6 is undeniably smaller (if only because of class and =>).
I am using ES5 as the baseline because I think this is what most users will want for the time being (for compatibility).
Uglify is getting ES6 support but it takes time: mishoo/UglifyJS2#448.
Another advantage of ES6 is that Uglify currently has a hard time removing unused classes. It will probably need some compiler annotations, similar to what you mention.

Some ideas that may slightly reduce code size:

  • Babel outputs uninitialised variable declarations as initialised to undefined (Babel repl, search results). This is unnecessary since uninitialised variables are automatically initialised to undefined (standard). Babel should be instructed to leave the variables uninitialised or the code can be changed to not use uninitialised variables.
  • There are numerous checks for undefined and null that could be extracted into utility functions (isUndefined(), isNull()) which may be futher minified.

Regarding polyfils, https://polyfill.io/ is a service which serves only the needed polyfills for each browser (MutationObserver was added recently). It could be mentioned in the documentation alongside instructions on how to replace aurelia-polyfills with an empty module for each build system (I saw a few issues asking for how to do it and I had trouble doing it myself for the CLI).

I remember that I opened a ticket back then to make polyfills optional but it was rejected.

In our apps for example we use corejs to build exactly the set of polyfills that we use and need given our minimum browser target. This is redundant with the polyfills shipped with Aurelia.

I lurked around the code and here are a few observations:

  • There are some ES5 helpers repeated a few times across the whole codebase. -> the plans to migrate to TS + use a single helpers module should help a little bit.
  • The current codegen names all functions, like: var frob = function frob() { }, I guess to preserve Function.name. This results in quite some duplicated names. TS doesn't do that, so again the migration should shrink size a bit.
  • Some modules have potential for smaller code size, but nothing really significant. One problem is that pretty much any change is a breaking change. For example: I think making BehaviorInstruction and TargetInstruction interfaces (i.e. use plain objects) instead of classes could reduce code size. But that is a breaking change as they are exported. Opportunities to make _significant_ size reductions while keeping exactly the same public API are hard to find.
  • Another direction could be to try harder to remove unused parts from the build output. I think many users use just a fraction of Aurelia and there is a lot of dead code carried around.

The best example of last bullet is svg.js in aurelia-binding: there is nearly 20K of attributes names there, which can't be minified as it's all strings. That's an easy, large reduction for people who don't use SVG bindings (probably the majority).

@EisenbergEffect Would you mind if I try adding some if (!NO_SVG) in a few places? This adds zero overhead but build tools/minifiers, which are able to define constants and remove dead code could then drop large parts of code on an opt-out basis.
My idea is that you could then do a webpack build with new AureliaPlugin({ noSvg: true }) and get a bundle that is 20K smaller.

Go for it!

On Feb 2, 2017 5:23 PM, "jods" notifications@github.com wrote:

I lurked around the code and here are a few observations:

  • There are some ES5 helpers repeated a few times across the whole
    codebase. -> the plans to migrate to TS + use a single helpers module
    should help a little bit.
  • The current codegen names all functions, like: var frob = function
    frob() { }, I guess to preserve Function.name. This results in quite
    some duplicated names. TS doesn't do that, so again the migration should
    shrink size a bit.
  • Some modules have potential for smaller code size, but nothing
    really significant. One problem is that pretty much any change is a
    breaking change. For example: I think making BehaviorInstruction and
    TargetInstruction interfaces (i.e. use plain objects) instead of
    classes could reduce code size. But that is a breaking change as they are
    exported. Opportunities to make significant size reductions while
    keeping exactly the same public API are hard to find.
  • Another direction could be to try harder to remove unused parts from
    the build output. I think many users use just a fraction of Aurelia and
    there is a lot of dead code carried around.

The best example of last bullet is svg.js in aurelia-binding: there is
nearly 20K of attributes names there, which can't be minified as it's all
strings. That's an easy, large reduction for people who don't use SVG
bindings (probably the majority).

@EisenbergEffect https://github.com/EisenbergEffect Would you mind if I
try adding some if (!NO_SVG) in a few places? This adds zero overhead but
build tools/minifiers, which are able to define constants and remove dead
code could then drop large parts of code on an opt-out basis.
My idea is that you could then do a webpack build with new
AureliaPlugin({ noSvg: true }) and get a bundle that is 20K smaller.

โ€”
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/aurelia/framework/issues/692#issuecomment-277138410,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAIBndHwgUm0rGHCbJIMEykQGl6RaGLYks5rYoF3gaJpZM4LjyJ6
.

I noticed that webpack style-loader, which lets you import "file.css" from your code and automatically add it to the DOM when the module is executed is really doing the same thing as Aurelia already can with <require from="file.css"> in your view.

So removing style-loader reduced my bundle to 298K minified, which is 9K down from previous build.

There are two drawbacks, though:

  • Webpack hot reloads CSS fine, it doesn't seem to work with Aurelia. Do we plan to support that @niieani ?
  • The Aurelia CSS loader only works with .css, I had to hack a bit to get .less to work. We probably need to support more extensions out of the box and/or make it configurable.

I am putting SVG support behind a global variable FEATURE_NO_SVG in PR aurelia/binding#569.
Of course at this point nothing changes for users.

But defining that variable in my webpack build brings the bundle down to 279K, which is 19K down from previous build. (But I lost SVG support, obviously.) I started this experiment at 343K so I went already quite a long way.

๐Ÿ“ข If anyone is aware of other large-ish features that are often unused let me know. It can be quite easy to put them behind a flag.

Ideally, it would have been better to make this an opt-in import or something like that, but compatibility...

You could potentially remove the Unparser in binding as well. That's generally only needed during development.

@EisenbergEffect This re-creates string expressions from parsed one, right? What actually uses it?

It's not nearly as heavy as SVG was and minifies a lot better; it's just over 2K in my build. Might still be an easy removal, so I'll try to look into it whenever I can.

Yes, that's correct. It's only used if you toString an AST I believe. (It's been a while since I looked at that code.) Ideally, this would be removed and put in the testing lib, but that would be a breaking change. You should be able to build it away safely for pretty much any app.

Webpack hot reloads CSS fine, it doesn't seem to work with Aurelia. Do we plan to support that @niieani ?

@jods4 I did work on that, and CSS loaded by Aurelia hot reloads just fine for me. It doesn't hot-unload when you remove the <require> from the .html template though. Not sure what was the behavior of the style-loader here?
See this file, which adds reference tracking for required CSS.
And here, where the hook is being added (or rather replaced) in the constructor of the HmrContext.

@niieani I see, this is related to aurelia/templating-resources#272.
It is hard-wired for .css:
https://github.com/aurelia/hot-module-reload/blob/master/src/aurelia-hot-module-reload.ts#L46

It didn't work for me because I used <require from="style.less"> (the magic of webpack loaders). I had to hack templating-resources to make it work and I didn't think that HMR would require the same hacking.

Not sure what the best way to make this work is, if you have ideas please comment the issue above.

Yeah, the HMR CSS hot reloading is hacky that way. See my comment here about a possible solution.

One thing that could potentially reduce a lot of fat is to do something like this in class methods across Aurelia src:

  method() {
    var _this = this;
    // replace all direct _this with this; to help minifier throw some fat
  }

A quick sum through the minified code shows me this could help. Maybe this job can be delegated to build tool.

Another 1 is to have class transformation more efficient:

// src
class ABC {
  xyz1() {}
  xyz2() {}
}
// unoptimized
function ABC() {}
ABC.prototype.xyz1 = function() {}
ABC.prototype.xyz2 = function() {}

// optimized
function ABC() {}
var ABC_proto = ABC.prototype
ABC_proto.xyz1 = function() {}
ABC_proto.xyz2 = function() {}

I believe the latter can have good impact on production bundles, but it seems harder to achieve atm.

@bigopon Yes using local variables/parameters rather than global names/properties results in smaller code because the former can easily be mangled by minifiers, while the latter can't.

In my opinion this comes at the expense of source code readability, which I see as more important -- unless we talk about large wins in specific areas. I don't know what others think about it. Moreover it tends to gzip well, although minified size is also important.

If there is a minifier that supports such transformations in a safe way, that would be interesting!

The second point is really up to the ES6 transpiler. Currently Babel, but there's a move towards TS.

I did a build using ES6 distribution + Babili minifier (based on Babel, supports ES6).
It's quite easy you can choose the distribution with new AureliaPlugin({ dist: 'es2015' }) and use babili-webpack-plugin.

The result (with default Babili options) seems to work.
Because the syntax is often shorter (e.g. => for functions) and we don't need ES5 helpers, the result is more compact by a fair amount.

The ES6 bundle is 251K minified, which is 28K less than my previous ES5 bundle (at 279K). Gzipped ~72K.
_Of course this is only doable if you target modern browsers that fully support ES6._

Another easy win I have in mind is providing an opt-out for the polyfills. If you target modern browsers, that should be an easy ~10K removal.

Excellent work investigating that @jods4 My thought from the beginning is that Aurelia would get smaller over time by basing things on standards. So, smaller syntax and no polyfills as a nice side-effect of the progress of the web.

I just cut 2K from the minified build by removing Unparser, see aurelia/binding#573.
Because aurelia/validation still relies on it (see aurelia/validation#412) I include it by default (opt-out).

So starting an ES6 project with the core of Aurelia is now under 250K minified.

aurelia/polyfills#46 adds support for selectively removing polyfills, based on es2015, es2016, esnext or none.

This is acceptable if you target only modern browsers (in which case you probably want esnext, which includes the Reflect.metadata stuff which is not even close to being a standard yet).

Another situation is that you might include a specific list of polyfills (probably from corejs) in your build anyway to match what your application uses. In that case, your polyfills and Aurelia's are really duplicates and using none will get rid of all of Aurelia's.

In this second case, I should point out that the main page of aurelia/polyfills contains the list of features required for Aurelia to work.

This goes hand-in-hand with the ES6 minified build. Assuming we target modern browsers, removing ES2015 and ES2016 polyfills brings the size just under 240K minified, or 68K gzipped. This is 9K down from the 249K build.

๐ŸŽ‰ If you can target a modern browser, this build configuration is more than 100K (minified) smaller than what I started with one month ago (343K)!

I will probably look at aurelia-pal-browser next. There are a few DOM polyfills lurking over there, like that missing console in old IE... Pretty sure we can cut a few more Ko when we remove support for obsolete browsers.

A element.classList polyfill is included in aurelia-pal-browser. Should it be moved to aurelia-polyfills or removed and handled like the other polyfills required for IE9 support (MutationObserver). Removing it might be considered a breaking change.

@Gheoan all those changes are opt-in for compatibility purposes. The default build still includes everything. You must set flags to remove parts that you don't need, which can be a breaking change, indeed.

@jods4 I was under the impression that they would be removed from the default build. Sorry for the confusion.

These flags can be used for other build systems other than webpack?

@Gheoan Surely.
In code, optional parts are wrapped by global variables (that don't exist) for example:

if (typeof FEATURE_NO_SVG === 'undefined') {
  // svg support is here
}

And likewise for a bunch of FEATURE_NO_XXX that I have documented in my wiki (look in the docs for the feature option).

What you need is a build that is able to statically evaluate typeof FEATURE_NO_XXX === 'undefined' as false and then drop the dead code.

Webpack has all the required tools to perform that but I'm sure it's not unique.

I think requirejs can use this too. I'll check.

On Feb 14, 2017 11:56 AM, "jods" notifications@github.com wrote:

Reopened #692 https://github.com/aurelia/framework/issues/692.

โ€”
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/aurelia/framework/issues/692#event-961994079, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAIBnbxhm5_Fr2zobpo2FT6NQ6EMu6Epks5rcgb-gaJpZM4LjyJ6
.

The journey continues...
I made IE support optional in aurelia-pal-browser. If you opt-out of it the minified build is around 4K smaller.

The numbers are not directly comparable with the previous build because there has been a release in between and some unrelated changes but my small build is now at 237K minified, ~67K gzipped.

Keep up the great work!

@jods4 Random, but are you documenting all this switches (and how to flip them) somewhere?

@jods4 I've been having some issues with the plugin regarding the features object as part of the constructor options for AureliaPlugin.

I've tried creating a fresh webpack & Aurelia project and it seems that defining features as part of the plugin options has absolutely no effect in the webpack build/compilation and output file size. To verify this I checked out your repository and tried exactly the same with one of your demos;

  1. I cloned out your aurelia-webpack-build repository, moved into 04-Small_ES6_build and did an npm install.
  2. Performed a webpack build, this created bundle.js @247K minified.
  3. Next I removed the features key (features: { ie: false, svg: false, unparser: false, polyfills: "esnext" }) from webpack.config.js and saved it.
  4. Run webpack again, this time webpack produces bundle.js @247K minified, exactly the same size as before.

The same effect is noticed even if I define features as: features: { ie: true, svg: true, unparser: true }. (247K minified)

EDIT: Scrub that, I stupidly assumed that by using the BabiliPlugin(), I would not need to use the --optimize-minimize flag on webpack. I didn't realise that the Aurelia features only get shaked with the --optimize-minimize switch.

I am a little bit surprised: if you use the BabiliPlugin directly I would expect it to work even without the --optimize-minimize flag, which I believe adds the Uglify plugin for you.

I am using it like so;

    plugins: [
        new AureliaPlugin({
            aureliaConfig: ["basic"],
            features: { ie: false, svg: false, unparser: false, polyfills: "es2015" }
        }),
        new BabiliPlugin()
    ]

My deps are;

    "dependencies": {
        "aurelia-bootstrapper": "^2.1.1"
    },
    "devDependencies": {
        "aurelia-webpack-plugin": "^2.0.0-rc.1",
        "babili": "^0.0.12",
        "babili-webpack-plugin": "^0.0.11",
        "compression-webpack-plugin": "^0.3.2",
        "html-loader": "^0.4.5",
        "html-webpack-plugin": "^2.28.0",
        "source-map-loader": "^0.2.0",
        "ts-loader": "^2.0.3",
        "typescript": "^2.2.2",
        "webpack": "^2.3.2",
        "webpack-dev-server": "^2.4.2"
    }

Calling webpack produces 245K. Calling webpack --optimize-minimize produces 233K.

You have latest Aurelia libs? All those flags were added very recently.
I use my "small ES6" demo to test this and I can definitely see the difference.

I have just deleted by node_modules and npm-cache folders and did a fresh npm install from the dependencies listed above and I am seeing the same result.

D:\Dev\Repo\Aurelia-AutoComplete>webpack
ts-loader: Using [email protected] and D:\Dev\Repo\Aurelia-AutoComplete\tsconfig.json
Hash: 6e18e99bfe895f7fb6c2
Version: webpack 2.3.2
Time: 25288ms
        Asset    Size  Chunks                    Chunk Names
app.bundle.js  250 kB       0  [emitted]  [big]  app
D:\Dev\Repo\Aurelia-AutoComplete>webpack --optimize-minimize
ts-loader: Using [email protected] and D:\Dev\Repo\Aurelia-AutoComplete\tsconfig.json
Hash: 6e18e99bfe895f7fb6c2
Version: webpack 2.3.2
Time: 30332ms
        Asset    Size  Chunks             Chunk Names
app.bundle.js  238 kB       0  [emitted]  app

I'm personally not worried about it as I now know to pass --optimize-minimize if building for production. Also this is probably either a webpack@2 or babili-webpack-plugin specific issue, not Aurelia.

@Robula I'm not sure what is going with your project.

I did an experiment with my ES6 Small Build demo.
Using exactly what is committed, yarn install followed by yarn run webpack produces a bundle that is 277,934 bytes.

If I comment out the feature line then the resulting bundle is 309,674 bytes.

So there's approx. a 30K win by removing unused features. Looking at the bundle contents I can see the polyfills being there or missing.

@jods4

How did you create this treemap of the project?

image

I upgraded everything to Webpack 3 today.

Without any change, I was happy to see it provided a 13% minified size reduction. ๐Ÿ˜„

Then I tried the new scope hoisting, aka ModuleConcatenationPlugin, aka rollup.
And that saved 194 bytes. Yeah, I know...

Don't get me wrong, scope hoisting is great and provides faster load times, and smaller sizes if you use tons of small modules.

There are a couple reasons why it isn't efficient on this application:

  • Any module that is loaded by aurelia-loader cannot be merged into another module. And that's many modules... E.g. none of the templating resources in the treemap above can be merged.
    In fact, only modules that are statically loaded with ES6 import can be merged.
  • The small application I'm building is not made up of tons of small modules. Notably because Aurelia modules are already distributed as merged modules and the application itself is rather empty.

Your mileage may vary. In a large application, the more files and the more libraries you use the better the results.

Anyway, new build is 242'310 bytes minified, 64Kb gzipped.

Note from Webpack 4 upgrade, the same build as above but using built-in mode: "production" of Webpack 4 is 237'314 bytes. Less than before, nothing spectacular.

I expect some larger wins if we start to use the new side-effects: false metadata in Aurelia packages where we can.

In addition to the original plans, we're also currently experimenting with a compile-time transform version of Aurelia that would obviate the need for huge parts of the framework, move certain runtime components into the build process and allow dropping all parts of the framework unused by the app. We're early on in the experimental process, but you can watch via the experiment repo.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ghost picture ghost  ยท  6Comments

gbreeze picture gbreeze  ยท  4Comments

arnonuem picture arnonuem  ยท  5Comments

avrahamcool picture avrahamcool  ยท  5Comments

ghost picture ghost  ยท  4Comments