import {fn1} from 'lib' vs import fn1 from 'lib' - javascript

I am importing a few functions from lodash and my coworker tells me that it's better to import each function separately than to import them as a group.
Current method:
import {fn1, fn2, fn3} from 'lodash';
Preferred method:
import fn1 from 'lodash/fn1';
import fn2 from 'lodash/fn2';
import fn3 from 'lodash/fn3';
Her reasoning is that latter imports less code, as it won't import the entire lodash library.
Is that the case?

What you want (and what is preferred) is called tree shaking:
Tree-shaking is the process of
removing unused code during the bundle process.
The correct way to do this and utilize the tree shaking is:
import foo from 'lodash/foo' // <-- only import `foo`
This will not tree-shake:
import { foo } from 'lodash'
nor will, obviously this:
import _ from 'lodash'
Support for this syntax was implemented in Lodash v4.
You can read more here

Based on the sources I can find, import x from y; imports the default export from y, calling it x in your file.
So your preferred method is importing the default export 3 times, with 3 different variable names...
Is the preferred method working in production?
Sources:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
https://medium.com/javascript-in-plain-english/javascript-modules-for-beginners-56939088f7d9

I do believe that import {fn1, fn2, fn3} from 'lodash'; is the correct way to import different exports of module, however, I think it could be better to use this way
import {
fn1,
fn2,
fn3
} from 'lodash';
Since it is easier to remove any of them if it no longer requires

Related

Lodash unable to use chain with cherry pick import

I'm trying to use lodash (v4.17.11) features with cherry picking import in my project. When I do this:
import {chain} from 'lodash';
and
chain([1,2,3]).take(1)
it works fine, however, if I change the import to:
import chain from 'lodash/chain';
the output is:
TypeError: (0 , _chain2.default)(...).take is not a function
Can someone please explain what's the mistake here
import chain from lodash/chain only works if there is a default export from the module.
If you want to import a particular named export - import {chain} from lodash/chain is the right way
See this - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
When you are using:
import {chain} from 'lodash/chain';
This treats the chain as named export not the default export from lodash/chain. But whenever you are point to a module lodash, in your case chain, module would be a default export rather than a named export. That is why when you use import chain from 'lodash/chain';, it brings the default export from the module.
chain would be a named export is you are importing it from the root of lodash as following:
import {chain} from 'lodash';
You should probably have a look at export and import in javascript and have a better understanding of it.

Why do npm modules, like material-ui, export es6 and es5 files?

In many npm modules I recently installed (eg. #material-ui/core) there are three ways to import the same React component:
import { AppBar } from '#material-ui/core'
import AppBar from '#material-ui/core/AppBar/AppBar'
import AppBar from '#material-ui/core/es/AppBar/AppBar'
In which scenario should I use variant 3 / es6 exported files?
If tree-shaking / dead code elimination works in webpack and the npm module. Should I rather use variant 1 (named import) instead of variant 2 (default export)?
There are two types of export:
1) Named export that is you export something like:
// exports a function declared earlier
export { myFunction };
// exports a constant
export const FOO = "foo";
if you want to import these, then syntax would be like:
import {FOO, myFunction} from './file';
2) Default export that is you export something like:
export default function() {}
you can rename your function, class to any name you want when you import, and its syntax would be like:
import myFunction from './file';
NOTE: You can have multiple named export in single file but you can not have multiple default export in single file.
For more detail check out this link: https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export
The main difference is how that library is exporting the modules.
When you do import AppBar from '#material-ui/core/AppBar/AppBar', this means that #material-ui/core/AppBar/AppBar is exporting a single object with export default AppBar.
And you're expected to imported as you did. However you're not limited to export a single default export from your module.
For example with React exposes the main object (i.e. React which is again being exported as default) that has all the properties you may want to use. But with the import syntax from ES6, you can import a specific property/function from that module(e.g. import { Component } from 'react'; which is exported as export class Component...)
I hope that's clear!

What is the difference between these ways of importing module with Webpack?

What is the difference between importing stuff from material-ui like this
import { Paper } from '#material-ui/core'
vs. like this, which works exactly the same way in my Webpack setup:
import Paper from '#material-ui/core/Paper'
Is any of these methods of importing, costly in terms of the resulting bundle size?
Note:
I am using this in a project that was bootstrapped with Create-React-App and the Create-Reac-App that I am using uses Webpack v3.5.1
import { something } from 'test-m' implies that, test-m has a named export on it, i.e:
module.exports = {
something: 'other string'
}
or even, on the es6 syntax:
export const something = 'other string'
--
import something from 'test-m' => implies that test-m has a default exports, i.e:
module.exports = 'other string'
or with es6 syntax export default 'other string'
How this affects bundling? Well, named exports is the way to go. Why?
Named exports imports only what is necessary from each module, so by using named exports, bundlers can tree-shake the module and take out from that module only what is necessary. This process decreases by a lot the size of the final module. In comparison to default exports, bundlers would pull to the chunk the whole module, despite you using one or all features that module provides.
TL;TR: named exports === lower bundle size.
In the case of import { Paper } from '#material-ui/core' you are importing the Paper named export from #material-ui/core module which contains other named exports.
In the case of import Paper from '#material-ui/core/Paper' you are importing the default export from #material-ui/core/Paper module which contains only Paper and exports it as default.
Some libraries have this approach of exposing both the main script with named exports and the individual modules for each function. For instance, Lodash. You can do both import { find } from 'lodash' and import find from 'lodash/find'. In both cases you will get the same find function.
Regarding pros, depending on the bundler configuration and the modules system used by the library, this: import { Paper } from '#material-ui/core' may not be tree-shaked and you will end up with the whole '#material-ui/core' in your bundle.
This: import Paper from '#material-ui/core/Paper' for sure will always only add Paper to your bundled code.
The first import will import the default class export. Whereas the second import imports only the exported function/object. This is quite a common difference when importing for Jest tests in react.
Take an example of a redux connected component:
export class ReduxConnect {
render(){
return (<h1> Some component </h1>);
}
}
export const mapStateToProps = state => ({
something: state.something
});
export default connect(mapStateToProps)(ReduxConnect);
Doing import ReduxConnect will import the default import, defined at the bottom which exports the redux connected component. Whereas import {ReduxConnect, mapStateToProps} would give you the option to export objects/functions individually from the class. In this case the difference would be between importing the redux connected component vs the pure component itself.

How do i write this require as an es6 import statement

Problem
I have this require statement
require('smoothscroll-polyfill').polyfill();
But I would like to write it as an es6 import statement
I have tried
import 'smoothscroll-polyfill';
and
import * as 'smoothscrollPolyfill' from 'smoothscroll-polyfill';
But cant get it to work correctly, so what is the correct way to import a package like this?
You'd do it in two parts, first the import, then the function call:
If polyfill itself is a named export and it doesn't care what this is when it's called:
import {polyfill} from 'smoothscroll-polyfill';
polyfill();
(And you've now confirmed that was the case.)
For completeness, before the above was confirmed, I also listed these other possibilities which may be useful for others in future:
If polyfill is a property on the default export (rather than its own named export), then we import the default (no {} in the import statement) and then use its property:
import smoothScrollPolyFill from 'smoothscroll-polyfill';
const polyfill = smoothScrollPolyFill.polyfill();
If the smoothScrollPolyFill part is a named export and polyfill is a property on it, then we'd use the {} on import:
import {smoothScrollPolyFill} from 'smoothscroll-polyfill';
const polyfill = smoothScrollPolyFill.polyfill();
using import {} from '...' instead.
import {polyfill} from 'smoothscroll-polyfill';//ref polyfill from 'smotthscroll-polyfill'
polyfill();//ref a method,so you must call it after imported.
Assuming you're using Babel or something similar to provide compatibility between CommonJS modules and ES6 modules, the syntax would look something like this:
import smoothscrollPolyfill from "smoothscroll-polyfill";
smoothscrollPolyfill.polyfill();

Correct way to import lodash

I had a pull request feedback below, just wondering which way is the correct way to import lodash?
You'd better do import has from 'lodash/has'.. For the earlier version
of lodash (v3) which by itself is pretty heavy, we should only import
a specidic module/function rather than importing the whole lodash
library. Not sure about the newer version (v4).
import has from 'lodash/has';
vs
import { has } from 'lodash';
Thanks
import has from 'lodash/has'; is better because lodash holds all it's functions in a single file, so rather than import the whole 'lodash' library at 100k, it's better to just import lodash's has function which is maybe 2k.
If you are using webpack 4, the following code is tree shakable.
import { has } from 'lodash-es';
The points to note;
CommonJS modules are not tree shakable so you should definitely use lodash-es, which is the Lodash library exported as ES Modules, rather than lodash (CommonJS).
lodash-es's package.json contains "sideEffects": false, which notifies webpack 4 that all the files inside the package are side effect free (see https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free).
This information is crucial for tree shaking since module bundlers do not tree shake files which possibly contain side effects even if their exported members are not used in anywhere.
Edit
As of version 1.9.0, Parcel also supports "sideEffects": false, threrefore import { has } from 'lodash-es'; is also tree shakable with Parcel.
It also supports tree shaking CommonJS modules, though it is likely tree shaking of ES Modules is more efficient than CommonJS according to my experiment.
Import specific methods inside of curly brackets
import { map, tail, times, uniq } from 'lodash';
Pros:
Only one import line(for a decent amount of functions)
More readable usage: map() instead of _.map() later in the javascript code.
Cons:
Every time we want to use a new function or stop using another - it needs to be maintained and managed
Copied from:The Correct Way to Import Lodash Libraries - A Benchmark article written by Alexander Chertkov.
You can import them as
import {concat, filter, orderBy} from 'lodash';
or as
import concat from 'lodash/concat';
import orderBy from 'lodash/orderBy';
import filter from 'lodash/filter';
the second one is much optimized than the first because it only loads the needed modules
then use like this
pendingArray: concat(
orderBy(
filter(payload, obj => obj.flag),
['flag'],
['desc'],
),
filter(payload, obj => !obj.flag),
If you are using babel, you should check out babel-plugin-lodash, it will cherry-pick the parts of lodash you are using for you, less hassle and a smaller bundle.
It has a few limitations:
You must use ES2015 imports to load Lodash
Babel < 6 & Node.js < 4 aren’t supported
Chain sequences aren’t supported. See this blog post for alternatives.
Modularized method packages aren’t supported
I just put them in their own file and export it for node and webpack:
// lodash-cherries.js
module.exports = {
defaults: require('lodash/defaults'),
isNil: require('lodash/isNil'),
isObject: require('lodash/isObject'),
isArray: require('lodash/isArray'),
isFunction: require('lodash/isFunction'),
isInteger: require('lodash/isInteger'),
isBoolean: require('lodash/isBoolean'),
keys: require('lodash/keys'),
set: require('lodash/set'),
get: require('lodash/get'),
}
I think this answer can be used in any project easily and brings the best result with less effort.
For Typescript users, use as following :
// lodash.utils.ts
export { default as get } from 'lodash/get';
export { default as isEmpty } from 'lodash/isEmpty';
export { default as isNil } from 'lodash/isNil';
...
And can be used the same way as importing lodash :
//some-code.ts
import { get } from './path/to/lodash.utils'
export static function getSomething(thing: any): any {
return get(thing, 'someSubField', 'someDefaultValue')
}
Or if you prefer to keep the _ to avoid conflicts (ex. map from rxjs vs lodash)
//some-other-code.ts
import * as _ from './path/to/lodash.utils'
export static function getSomething(thing: any): any {
return _.get(thing, 'someSubField', 'someDefaultValue')
}
UPDATE :
Seems like the right way to export is :
export * as get from 'lodash/get';
export * as isEmpty from 'lodash/isEmpty';
export * as isNil from 'lodash/isNil';
...
But there is a weird collision with #types/lodash, I've removed this type package because I would get this error :
Module '"/../project/node_modules/#types/lodash/cloneDeep"' uses
'export =' and cannot be used with 'export *'.ts(2498)
UPDATE :
After some digging, I've turned tsconfig.json feature esModuleInterop to true, and it allows me to do the following :
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
import isNil from 'lodash/isNil';
...
export { get, isEmpty, isNil, ... };
Note that this affects all your imports in your projects that has been defined as import * as lib from 'lib'. Follow the documentation to be sure it's suitable for you.
import { cloneDeep, groupBy } from 'lodash';
I think this is simpler when you don't need to convert array to lodash object by using _.
const groupData = groupBy(expandedData, (x) => x.room.name);
For those who want to keep using _ , then just import them like this:
import groupBy from 'lodash/groupBy';
import filter from 'lodash/filter';
import get from 'lodash/get';
window._ = {groupBy, filter, get};
I think the more cleaner way of importing lodash is just like this:-
import _ from 'lodash'
then you can use what ever you want just by using this underscore just like this:-
_.has()

Categories