Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Huge component size #112

Closed
cristijora opened this issue Feb 12, 2017 · 38 comments
Closed

Huge component size #112

cristijora opened this issue Feb 12, 2017 · 38 comments

Comments

@cristijora
Copy link
Collaborator

cristijora commented Feb 12, 2017

Just noticed that the overall component has 400+ kb in size.
I must say that's pretty huge for a component that generates fields.
After taking a look at the source code I noticed that moment.js is used in several places for very basic functionalities. I think you can get rid of it to save some space.

Only by removing all moment js dependencies (4) the size shrinked from 400kb to 176kb.
There is also one component fieldVueMultiSelect which for some reason uses the whole Vue library. I don't think there is need to bring the whole library just to instantiate that component. There should be another way of doing it. By removing the vue dep the final file further reduces to 126kb.

Taking down lodash util function brings the file down under 100kb.
To sum it up, by removing only external dependencies (moment, vue and lodash) I managed to bring the library down from 400+ kb to 55kb and by removing external components it goes down to 29kb.

The size here is huge factor for people deciding whether to use such a component or not. I would say we could shrink this one down to 100-150kb very easily and then with some effort it can be brought under 100kb.

@icebob
Copy link
Member

icebob commented Feb 13, 2017

Thank you for your explain. Could you make a PR with this changes?

@cristijora
Copy link
Collaborator Author

Yes, I will make one today with some simple changes which should reduce the size in half :)

@icebob
Copy link
Member

icebob commented Feb 13, 2017

This is the current bundle stat:
image
The source is only 2.8%. The others: vue (28%), moment (35%), lodash (42%)

@cristijora
Copy link
Collaborator Author

Interesting. What did you use to get those stats?
image

These are the 2 files you get when installing the component via npm (402 kb)

@icebob
Copy link
Member

icebob commented Feb 13, 2017

I used this tool: https://chrisbateman.github.io/webpack-visualizer/
If you make a build, there will be a stats.json in root of repo. And you need to drop this file to the visualizer site.

@lionel-bijaoui
Copy link
Member

Hi, this issue has the same goal as my other one #67
I absolutely agree with @cristijora and at the time, I already got impressive result.

I think the generator should be on it's own and people should just pick and choose which one they want.
I don't know if each field should have it's own sub repo (eg. vfg-checkbox) or if we could provide the same flexibility with only one repo.

What are your thought ?

@cristijora
Copy link
Collaborator Author

Agree witht that @lionel-bijaoui . What's the impact of externals ? Don't quite understand it.

@lionel-bijaoui
Copy link
Member

lionel-bijaoui commented Feb 13, 2017

@cristijora with external, you need to add them globally (via a script tag) and webpack will use that reference. But it was a solution that fitted my need (my project use moment.js anyway), and I'm not sure this is the best for vfg.

At the time I envisioned that you could specify by field, what dependency it have and simply add it in a script tag (some fields do that). But I'm not sure that it would work if you import the lib. Also it force to use the lib in a global context.

Doc : https://webpack.github.io/docs/library-and-externals.html

Edit: Better doc (for webpack 2 but it is mainly the same process) : https://webpack.js.org/guides/author-libraries/

@cristijora
Copy link
Collaborator Author

Ah got that. Don't think that's a robust option. Ideally it would be nice to get rid of the external dependencies or use strictly what's necessary which is ok in the case of lodash, but for some reasons it imports a lot of code. Moment is used only in 4 places for some basic stuff. We can get rid of it I think.

cristijora pushed a commit to cristijora/vue-form-generator that referenced this issue Feb 13, 2017
Use moment.min to reduce build size
icebob added a commit that referenced this issue Feb 13, 2017
#112 Get rid of Vue dependency in the code.
@icebob
Copy link
Member

icebob commented Feb 13, 2017

Thanks @cristijora . Your PR merged. Now there is only lodash (75% of bundle).

@icebob
Copy link
Member

icebob commented Feb 13, 2017

Maybe webpack 2 tree-shaking can be remove the unused lodash functions?

@cristijora
Copy link
Collaborator Author

Could be an option. Will give it a read.

@lionel-bijaoui
Copy link
Member

lionel-bijaoui commented Feb 13, 2017

I would like we avoid changing webpack version for now.
There is actually an option to do that for webpack 1 : https://github.com/lodash/lodash-webpack-plugin with https://www.npmjs.com/package/babel-plugin-lodash

Also, can we make a decision about "modularizing" fields ?

@lionel-bijaoui lionel-bijaoui modified the milestone: v2.0.0 Feb 13, 2017
cristijora pushed a commit to cristijora/vue-form-generator that referenced this issue Feb 13, 2017
Use babel-plugin-lodash to support "tree-shaking" for lodash
@icebob
Copy link
Member

icebob commented Feb 14, 2017

@lionel-bijaoui I don't support modularizing fields, because the size of code of all fields is small. ~ 10kb. So we can decrease the size with modularizing. We need to solve the momentjs & lodash dependencies instead.

@lionel-bijaoui
Copy link
Member

lionel-bijaoui commented Feb 14, 2017 via email

@cristijora
Copy link
Collaborator Author

cristijora commented Feb 14, 2017

i actually partially agree with @lionel-bijaoui here. This would open some possibilities like adding some vue based components while not affecting the size of the whole thing. I don't have a good solution for it though. Maybe have something similar to the custom field implementation. Simply import the necessary external lib and declare it globally ? Ex:

import fieldMultiSelect from "vue-form-generator/src/fieldVueMultiSelect.vue"
Vue.component("fieldMultiSelect", fieldMultiSelect);

This would register the component as an abstract field and it will work since the component meets the requirements of the abstract field custom inputs.

@icebob
Copy link
Member

icebob commented Feb 14, 2017

In this case we can create two bundles. A minimal version (for only html5 field, but can be import the further fields) and a full version (as current bundle, which contains every built-in fields)

Opinion?

@lionel-bijaoui
Copy link
Member

@icebob That was exactly my initial proposal #67 and I would be very happy with it 😄

@icebob
Copy link
Member

icebob commented Feb 14, 2017

Sorry, I didn't remember :)

@lionel-bijaoui
Copy link
Member

Ahah, no problem !

@cristijora
Copy link
Collaborator Author

Agree. Seems like a simple approach. If the user doesn't care about size he just imports the default full version but if he does care then he imports the minimal version and declares the components himself.
I think we would need to add something similar to what I did in my #112 PR to each component that wouldn't be implicitly included in the minimal build.

if(!this.$root.$options.components["multiselect"])

Example:

if(!this.$root.$options.components["fieldDateTimePicker"])

@icebob
Copy link
Member

icebob commented Feb 14, 2017

And need to rewrite the dynamic field loader part

@cristijora
Copy link
Collaborator Author

cristijora commented Feb 14, 2017

Yeah. A simple option would be to place the optional fields in a separate folder 😄

@icebob
Copy link
Member

icebob commented Feb 14, 2017

Nice suggestion :)

@icebob
Copy link
Member

icebob commented Feb 14, 2017

So the tasks:

  • kill unused lodash codes with babel-plugin-lodash or lodash-webpack-plugin
  • retire momentjs and use date-fns or fecha
  • separate bundle to
    • core/base version which contains only html5 input + some fields
    • full/complete version which contains all built-in fields (current bundle)
    • separate field vue files to two directory
    • rewrite dynamic field loader code
    • modify webpack config to create two bundles

@cristijora
Copy link
Collaborator Author

The first 2 are in this PR
The second one needs adjustments because of a format error though.
The first task is done in the PR. (babel-plugin-lodash used)

@icebob
Copy link
Member

icebob commented Feb 14, 2017

And babel-plugin-lodash working properly?

@cristijora
Copy link
Collaborator Author

Yes it works fine. Reduced like 75kb with that only.
Before
before lodashplugin
After
after lodashplugin

@icebob
Copy link
Member

icebob commented Feb 14, 2017

Great! 🎉

@lionel-bijaoui
Copy link
Member

lionel-bijaoui commented Feb 14, 2017

I can help with the webpack config if needed.
Not sure what you mean by "separate field vue files to two directory".

@cristijora
Copy link
Collaborator Author

cristijora commented Feb 14, 2017

@lionel-bijaoui
This code basically looks in the fields folder and registers the components

let Fields = require.context("./fields/", false, /^\.\/field([\w-_]+)\.vue$/);
	let fieldComponents = {};
	each(Fields.keys(), (key) => {
		let compName = key.replace(/^\.\//, "").replace(/\.vue/, "");
		fieldComponents[compName] = Fields(key);
	});

//some other code
components: fieldComponents,

A very simple approach here would be to put the optional components which we don't want to include in a separate folder and have some webpack config at this point
let Fields = require.context("./fields/", false, /^\.\/field([\w-_]+)\.vue$/);
where we can replace ./fields/ with some array and have 2 configurations for the default build

foldersToInclude = ["./fields/","./optional"];

and for the minimal build

foldersToInclude = ["./fields"]

Instead of
let Fields = require.context("./fields/", false, /^\.\/field([\w-_]+)\.vue$/);
we would iterate the the array from above and add the components.

@icebob
Copy link
Member

icebob commented Feb 14, 2017

And you can use DefinePlugin and declare a variable in webpack config e.g. FULL_BUNDLE. And in the code you can use it

if (FULL_BUNDLE) {
  foldersToInclude = ["./fields/","./optional"];
} else {
  foldersToInclude = ["./fields"]
}

Maybe :)

@lionel-bijaoui
Copy link
Member

@cristijora thanks a lot for your help. You deserve the collaborator title more than I do 🥇
@icebob Ok I will do a PR when it is ready. I didn't know about DefinePlugin but it could be very helpfull for my own project too. Thanks !

@icebob icebob mentioned this issue Feb 14, 2017
@icebob
Copy link
Member

icebob commented Feb 15, 2017

@lionel-bijaoui I would like to discuss that we marked some fields as "deprecated", because we have a common "input" field. Are we remove them in v2?

@lionel-bijaoui
Copy link
Member

lionel-bijaoui commented Feb 15, 2017 via email

@icebob
Copy link
Member

icebob commented Feb 15, 2017

OK, new issue for this: #120

@icebob
Copy link
Member

icebob commented Feb 15, 2017

Full bundle: 75k
Core bundle: 39k

Great!

@cristijora
Copy link
Collaborator Author

Awesome. Nice to see going it down from 400+kb to 40kb.

@icebob icebob closed this as completed Mar 1, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants