Is using an ES6 import to load specific names faster than importing a namespace?
Asked Answered
B

3

20

I've found at least two ways to import functions in from a module like Ramda for example. There are probably a few more ways to do something very similar like const R = require('ramda');

Option 1 is to import certain functions:

import { cond, T, always, curry, compose } from 'ramda';

Option 2 is to import the whole module like:

import * as R from "ramda";

I would prefer to reference the module from which the function is being called like so:

R.T();

But if the 2nd option is used, does it bring in every Ramda function not just the ones used in a module I'm working in? Are there any impacts on actual memory use, or bandwidth use as far as what gets sent to the browser if option 2 is used? Is it possible to somehow do this:

// invalid syntax below:
import R { cond, T, always, curry, compose } from 'ramda';
R.T();

My question is kinda related to this one, but it's a bit different import R (ramda) into typescript .ts file

Buttocks answered 17/8, 2017 at 12:46 Comment(6)
I'm not sure the best practices, but I think it probably comes down to preference. I'd rather see your first option because it lets me know what is available from the imported module without having to dig through that module. It's still very clear where those functions/variables have come from as well.Feisty
import … from 'ramda' does by default always bring in the whole Ramda module, regardless whether you use named or namespaced imports. If there is any size optimisation (with guessing what parts of the module need to be evaluated and which not), then that happens in your module bundler (like Rollup). Contact its documentation. Usually it should be able to detect which imports you are using regardless of the import style, unless you are doing unusual things with the namespace object (like looping it or using dynamic property access).Schizophrenia
Now that the question is reopened I'll put it in an answerSchizophrenia
"There are definitely a few more ways." - actually, no. There's a default import syntax (which can be used in combination with either namespace or named imports as well), but Ramda doesn't use this afaik.Schizophrenia
@Bergi, thanks for all the answers and input. But as far as other ways to import Ramda or a module in general, wouldn't require do something very similar? const R = require('ramda');Buttocks
@Buttocks And so does eval(fs.readFile(…)) (more or less) :-) I was talking about the declarative ES6 module syntax only.Schizophrenia
S
21

TL;DR: It does not matter.


import * as … from 'ramda';
import { … } from 'ramda';

will both by default always bring in the complete Ramda module with all its dependencies. All code inside the module would be run, and which syntax was used to reference the exported bindings doesn't matter. Whether you use named or namespaced imports comes down to preference entirely.

What can reduce the file size to download and the used memory is static analysis. After having evaluated the module, the engine can garbage-collect those bindings that are referenced from nowhere. Module namespace objects might make this slightly harder, as anyone with access to the object can access all exports. But still those objects are specified in a way (as immutable) to allow static analysis on their usage and if the only thing you're doing with them is property access with constant names, engines are expected to utilise this fact.

Any size optimisation involves guessing which parts of the module need to be evaluated and which not, and happens in your module bundler (like Rollup or WebPack). This is known as Tree Shaking, dropping parts of the code and entire dependencies when not needed (used by anything that got imported). It should be able to detect which imports you are using regardless of the import style, although it might have to bail out when are doing unusual things with the namespace object (like looping it or using dynamic property access).

To learn about the exact guesses your bundler can make, contact its documentation.

Schizophrenia answered 18/8, 2017 at 0:56 Comment(2)
"it might have to bail out when are doing unusual things with the namespace object (like looping it or using dynamic property access)" Is dynamic property access that unusual? If dynamic property access prevents static analysis from dropping unused code, would that imply that named imports are a better pattern for avoiding accidental imports of unused branches of code?Vestiary
@JoSprague Yes, I think it is unusual. Can you give an example of what you are doing?Schizophrenia
R
4

@Bergi is right in his comment, which I think should be the answer. I would also like to point out you can always try things out in Babel to see what it compiles to: click here to see what an example destructuring actually does

So basically even if you destructure just one function from the module, the whole module will be required. In the Babel example I gave, I just extracted Component from the 'react' module, but the compiled code actually just required the whole thing. :)

Rufena answered 18/8, 2017 at 0:19 Comment(1)
The import is not a destructuring assignment.Schizophrenia
Q
0

Adding to @Bergi, Also just for future reference if you want to shed unused functions and import only the desired function, use selective import like below

import isEmpty from 'ramda/src/isEmpty';

This way you can complement it with Webpack and get a better tree shaking. Hope it helps

Quintuplet answered 1/2, 2023 at 13:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.