More efficient ES6 imports - javascript

I read somewhere (forgot the source, unfortunately), that it's more efficient somehow to do this:
import _find from 'lodash/find';
as opposed to:
import _ from 'lodash'; // just to use _.find
I understand how it could be more efficient to import a single function, but where is the efficiency/performance gain exactly? Is it webpack-related? If I'm not using webpack as my build engine, does it matter?

When you import a big library, you import the whole thing (at least with most bundlers), since there is no clear way to distinguish what is necessary from the object you imported.
I'm not too familiar with lodash but it's entry point should look something like this:
module.exports = {
find: require('./find.js')
filter: require('./filter.js')
// rest of the exposed functions
}
With the second import style you got this whole object, but you only need 1 function from all of this.
So if you do this:
import _ from 'lodash'
You will end up with the whole lodash library packed into your bundle, while if you do this:
import find from 'lodash/find'
You will only have find and it's dependencies, resulting in a considerably smaller bundle size.

Related

es6 modules: import specific-functions as aName

I'd like to import specific functions from another module. But I faced naming collision: I have similar function names in my importing file. I'd like to do something like this:
// exporterFile.js
export {
set,
get,
has,
foo,
bar,
}
// implementation code for set, get, etc.
// importerFile.js
import {get, set} as exporter from "exporterFile.js"
function get() {
// some code and call set from exporter
exporter.set(something)
}
I know I could import * as exporter from "exporterFile.js" but it lacks readability and does not show what methods (functions) I am using from the exporter file.
Also, I know I could import {get as exporterGet, set as exporterSet} from "exporterFile.js" but it is not so readable.
Is there an elegant, clear, readable way to import selected functions from a module without naming collision? If not, what is the best practice and approach and suggestion: readability is very important, tree-shaking is a consideration as well.
There are no alternatives to the two ways you already mentioned. Readability is subjective, but both exporterGet() and exporter.get() are good choices that are common. If you explicitly want to list all the used function in the import statement itself, you will have to give them individual aliases. Tree shaking tools will be able to handle both formats.

How to get a lodash custom (reduced) build suitable for es modules import?

I mean for example:
lodash include=each,find,filter,map,some,debounce,defer,delay,throttle,uniq,assign,extend,merge,omit,without,findIndex,compact,replace,groupBy,max,uniqueId
When I try to import in es modules, I get a warning like this:
Bundled and transpiled successfully, but with warnings: The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
Is there any option to have the same custom (reduced) build, but suitable to be imported in modules and then available through '_' as always?
Thank you
You could create your own aggregating module in a local file...such as tools/lodash.js:
export {
each, find, filter, map, some, debounce, defer, delay, throttle,
uniq, assign, extend, merge, omit, without, findIndex, compact,
replace, groupBy, max, uniqueId
} from 'lodash';
Then when you wanted these tools you could add
import * as _ from './tools/lodash`;
If you don't like the import * as _ syntax, you could do a straight import in your aggregating module and then do an export default { each, find, ... };. That would allow you to do import _ from './tools/lodash';.

ClassName/ClassName.js Module Resolution with Webpack & Node.js

Often times I see the following directory structure and import pattern in Javascript projects:
ClassName/index.js pattern
RepoRoot
|-src
|-index.js
|-ClassName
|-index.js
|-SupportingClass.js
src/ClassName/index.js
import { SupportingClass } from './SupportingClass';
export class ClassName {
// Implementation
}
src/index.js
import { ClassName } from './ClassName';
This is all fine and dandy. It allows for a much cleaner import of the ClassName class. However I'm not a fan of the name of the class file index.js not having the same name as the class's name. It:
Makes it harder to locate the file in a file name search (Command+P in VS Code)
You end up with just a very unhelpful index.js in the file tab in the IDE (yes I know some IDEs are smart enough to add a bit more context when you have more than one file of the same name open. That helps, but isn't a real solution imo)
Other files that aren't the indexes get much more descriptive names: SupportingClass.js, someUtils.js, etc.
ClassName/ClassName.js pattern
RepoRoot
|-src
|-index.js
|-ClassName
|-ClassName.js
|-SupportingClass.js
src/ClassName/ClassName.js
import { SupportingClass } from './SupportingClass';
export class ClassName {
// Implementation
}
src/index.js
import { ClassName } from './ClassName/ClassName';
This solves all of the problems I laid out, but makes the import of ClassName a bit duplicative. Probably not a big deal for most people especially with your IDE being able to auto import things for you nowadays.
However...
Just how webpack & node.js understand out of the box how to interpret the import path:
./ClassName as ./ClassName/index.js
Is there a setting or plugin that allows for it to interpret:
./ClassName as ./ClassName/ClassName.js?
I've been wondering for years if such a thing existed but could never really find any literature on it. Curious if anyone here knows of anything.
You are dealing with named imports and default imports, more details on this discussion can be found here:
How to import both default and named from an ES6 module
From your situation (I haven't tried with subfolders, but it should work), you may want to organize your files as such:
RepoRoot
|-src
|-index.js
|-ClassName
|-ClassName.js (named module)
|-SupportingClass.js (named module)
|-index.js (default modules)
And then you can easily import your stuff in src/index.js such as:
import ClassName, SupportingClass, { otherValues } from './ClassName'

Is it possible to have a truly dynamic import() in webpack?

I've been trying to use the import() function to import something which is dynamic at runtime. I would think that as long as I create an entry for the file, webpack could be smart enough to import() the correct module, but that doesn't seem to be the case.
Does anyone know of a way to chunk off an entry and use the import() syntax, feeding it a variable, and have it work at runtime?
A simple example of the root problem is as follows:
// works
import( './a.js' ).then(() => console.log('it worked'));
// something is a dynamic variable that changes at runtime
const something = './a.js';
// does not work, even with this simplistic example
import( something ).catch(() => console.log('it did not work'));
It does not work because, although it is called "dynamic import" it is does not follow what the word means. The idea on "dynamic" import is to be able to import something dynamically at runtime, but here it is caveat: the module to be imported has to be known.
Since webpack does static analysis to do the lazy loading on these import() statements, everything has to be known and predictable, otherwise webpack would not be able to create async chunks on the fly. That is why adding a variable to the import does not work.
Yeah, this bit of webpack is weird. Truly dynamic import doesn't work.
We can put string template inside the import statement, but putting variable name raises Dependency warning.
This is the closest I could achieve - put if conditions or switch case or a string template and then write the import in it.
Something like -
const getModule = (filename) => {return import(`directoryPath/${fileName}`);}
OR
const something = 'a.js';
if (something === 'a.js') {
return import(`./${something}`); // promise
} else if (something === 'b.js'){
.........
}
There's this thing with dynamic imports - webpack bundles all possible files in these conditions which is not cool.

Should I use both `import 'rxjs/Rx'` and `import { Observable } from '#rxjs/Observable'`

import { Injectable } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
import { Observable } from '#rxjs/Observable';
import 'rxjs/Rx';
import 'rxjs/add/observable/throw';
#Component({});
export shellModule{}
This is a piece of code form my Angular app that I copied from somewhere (I have removed the definitions in the exported module. I am using it to make a service to call APIs.
In the imports in this particular file, why is it that Observable is imported separately even though the entire rxjshas been imported. If a particular module is being imported in its entirety, why is a particular object from it imported separately? I tried asking this question at the forum from where I took it, but there was no answer. I want to understand if this somehow helps with optimization of code.
In general:
In Typescript, the way modules are handled would require you to either load in the entire library with the import * as rx from 'rxjs/Rx', or a specific exported module within the library to use it, so the the compiler loads in the types.
Reducing your imports to only the specific modules you need sets up your app to use tree shaking from Angular's AOT compilation. This is not done by the typescript compiler, but by a tool called rollup. So, it can help with optimizing code later, but it doesn't automatically do so.
As far as compilation overhead, bringing in the whole library might slow down the compiler a bit... but this isn't a very strong point except for massively complex libraries.
I, personally, prefer importing in specific modules because it makes the calling code a little cleaner since I don't need to use that global name to get to the specific name. rx.Observable vs Observable. A good example of this is the lodash library (rxjs is a bit more complex...)
Honestly, importing entire libraries like the line you have there: import 'rxjs/Rx' doesn't make sense to me. You should only import specific exported modules. Try removing it, seeing what errors you get, and then using the * as rx syntax instead.
As far as rxjs goes - it is a little wonky when you want to import specific operators like this question does - so the way to get specific operators is with: import 'rxjs/add/observable/from' - but that also requires a tinkering with your webpack set up as outlined in the referenced question's answer.
Let's see what the rxjs/Rx module exports:
export { Subject, AnonymousSubject } from './Subject';
export { Observable } from './Observable';
export { Operator } from './Operator';
export { Observer } from './Observer';
export { Subscription } from './Subscription';
export { Subscriber } from './Subscriber';
export { AsyncSubject } from './AsyncSubject';
export { ReplaySubject } from './ReplaySubject';
export { BehaviorSubject } from './BehaviorSubject';
...
import './add/observable/bindCallback';
import './add/observable/bindNodeCallback';
import './add/observable/combineLatest';
...
So it exports RxJs classes and also imports operators from the add folder. So as you can see it loads everything in the library. It doesn't export any global object though. So you need to use named export like this:
import * as Rx from 'rxjs/Rx'
to be able to use an exported class:
Rx.Observable.of(12, 3);
This emulates what you would have if you loaded the library using the bundle - a global Rx object:
<script src="rxjs/bundles/Rx.js">
If you want to use Observable without Rx global object, you need to import it separately:
import { Observable } from '#rxjs/Observable';
Observable.of(1);
Importing both
import { Observable } from '#rxjs/Observable';
import 'rxjs/Rx';
is not a good practice, but may be used if you don't want to import every operator separately.
Also see How to correctly import operators from the rxjs package.

Categories

Resources