Why are imported .js files duplicated in nested .vue components?

  • I have made a small app with about 10 custom components that are in .vue files. Some components however need to use a very big library (mapbox-gl). I did this by doing 'import * as mapboxgl from ‘mapbox-gl’ in these.vue files.

    Some other .vue files however need to use these mapbox-components.

    It looks to me that each .vue file ends up in its own .js file in ‘dist/js’, is that correct? All these files are huge, so it looks like each time the entire mapbox-gl.js is included for each .vue file that includes mapbox-gl directly or even uses a component that does!

    This seems very inefficient, is there a better way to do this ?

  • This would be a webpack issue. The expected behavior is the library being loaded and added to the bundle only once.

  • i didn’t change anything in the default webpack configuration. By using webpack-bundle-analyzer, I can see that indeed in each generated js file the entire mapbox-gl.js is included. When I disable the import of the component that uses mapbox-gl inside the other components, these files are MUCH smaller, so it’s entirely because of doing the import. Am I doing something wrong? Are vue components that are used by other components maybe not meant to import big libraries?

  • I think the question is better suited to stackoverflow. But do let us know if you find a solution.

  • I am also experiencing this problem, In my case it is happening with lodash.
    Did you found any solution to this ? All my lodash imports are using destucturing. This means that I do import {map} from 'lodash'
    I expected it to just include that particular function, and not the entire lodash.

    I didn’t change the webpack config at all.
    Any help will be appreciated.


  • Admin

    @danielo515 If you just need a few methods from lodash, it’s best if you npm install just those. Example with “map”: npm install --save lodash.map, then import map from 'lodash.map'

    But yet again, the browser support for lodash/underscore equivalents is top-notch, not to mention also way faster since the equivalents are native functions. I strongly suggest you go with the native support rather than bloat websites/apps.

    Example with plain JS on “map”: myArray.map(…). It’s that easy.

  • Hello @rstoenescu ,
    I’m a big fan and evangelist of ES6 features, and I know almost all the details about them, so it is not about simple functions/helpers.
    Lodash is a very good library with ton of useful and composable functions that you don’t want to re-implement yourself. For example,the code snippet you posted will work well for arrays, but map method of lodash also works with any kind of collection, not just arrays, not to mention that it is safer:

    _.map(null, _.identity) // => []
    x = null;
    x.map(x=>x) // cann not read property map of null

    Regarding installing just a sub-package a lodash is an anti-pattern for several reasons. The first one is that lodash is a very popular dependency, and it just requires a single of your dependencies to include it and you will end with duplicated code. But the most important one is that those packages has been deprecated by lodash maintainer. Yes, it was a big surprise to me too, but check this GH issue: https://github.com/lodash/lodash/issues/3565#issuecomment-353970667

    Also the functions that I’m using from lodash are utilities like pipe, capitalize, snakeCase, identity… small functions that you can implement yourself, but probably with less guarantees and less efficient.

    I have to say that the babel plugin of lodash makes a good job three-shaking the lodash library. My lodash bundled functions are just a few KBs (14 kb) vs the entire lodash library which is about 500 kb, so I’m very happy about it.

    My only problem is that I want to include that small bundles just once. I tried marking lodas as should being added to the vendor bundle, but that is not working. I suspect because instead of being reported as plain lodash, their are being cherry picked with their entire name.
    To illustrate what is happening, here is a small screenshot:

    0_1521620346466_Captura de pantalla 2018-03-21 a las 9.17.16.png

  • I made some research on the webpack-config, and I can see that the comparision is about containing the provided module name. Since what webpack is reporting is on the form of /Users/danielo/GIT/alert-tools/node_modules/lodash/isArray.js".indexOf('lodash') specifying lodash should work.
    So the problem may be at a different place. Seems that including certain library on the vendor bundle does not free it from being included on a different module too.

    My knowledge of webpack is limited, but I think I’m on the right track.

  • Inspecting this in more depth, and being a bit more open-minded I can understand that this is maybe a feature.
    Looking at the bundles on more detail I can see that they are not of the same exact size, and if you look closer you can see that they do not include the same files. Being each bundle a different route maybe this makes sense. Each route has just the files they need. Because we don’t know how routes will be loaded (which order)because can navigate to any random one we have to include all the possible files they may require.

    My only complain may be that some files are present both at the vendor bundle and the route bundle. Since the vendor bundle is loaded on every route, I don’t see why those files needs to be included on the route bundle too.

    In any case, this is just an unnecessary optimization concern, since page loading is already very fast.

Log in to reply