惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threatpost
博客园 - 叶小钗
T
The Blog of Author Tim Ferriss
Recent Announcements
Recent Announcements
D
DataBreaches.Net
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
罗磊的独立博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
N
Netflix TechBlog - Medium
Microsoft Azure Blog
Microsoft Azure Blog
Microsoft Security Blog
Microsoft Security Blog
B
Blog
U
Unit 42
有赞技术团队
有赞技术团队
博客园 - 聂微东
GbyAI
GbyAI
宝玉的分享
宝玉的分享
F
Full Disclosure
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
MyScale Blog
MyScale Blog
Jina AI
Jina AI
Martin Fowler
Martin Fowler
IT之家
IT之家
酷 壳 – CoolShell
酷 壳 – CoolShell
D
Docker
P
Proofpoint News Feed
A
About on SuperTechFans
I
InfoQ
博客园 - 【当耐特】
C
Check Point Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Privacy & Cybersecurity Law Blog
T
Threat Research - Cisco Blogs
Y
Y Combinator Blog
Project Zero
Project Zero
WordPress大学
WordPress大学
小众软件
小众软件
AWS News Blog
AWS News Blog
博客园 - 司徒正美
T
The Exploit Database - CXSecurity.com
L
LINUX DO - 热门话题
I
Intezer
Engineering at Meta
Engineering at Meta
C
CXSECURITY Database RSS Feed - CXSecurity.com
J
Java Code Geeks
T
Tenable Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes

Dmitri Pavlutin Blog

Pure Functions in JavaScript: A Beginner's Guide Record Type in TypeScript: A Quick Intro How to Write Comments in React: The Good, the Bad and the Ugly 4 Ways to Create an Enum in JavaScript React forwardRef(): How to Pass Refs to Child Components TypeScript Function Types: A Beginner's Guide How to Use v-model to Access Input Values in Vue Mastering Vue refs: From Zero to Hero Environment Variables in JavaScript: process.env 5 Must-Know Differences Between ref() and reactive() in Vue How to Destructure Props in Vue (Composition API) Triangulation in Test-Driven Development How to Use nextTick() in Vue Programming to Interface Vs to Implementation A Smarter JavaScript Mapper: array.flatMap() Array Grouping in JavaScript: Object.groupBy() How to Access ES Module Metadata using import.meta JSON Modules in JavaScript How to Trim Strings in JavaScript TypeScript Function Overloading How to Debounce and Throttle Callbacks in Vue How to Show/Hide Elements in Vue Sparse vs Dense Arrays in JavaScript How to Fill an Array with Initial Values in JavaScript Covariance and Contravariance in TypeScript What are Higher-Order Functions in JavaScript? How to Use TypeScript with React Components Index Signatures in TypeScript How to Use React useReducer() Hook unknown vs any in TypeScript A Guide to React Context and useContext() Hook How to Use Promise.any() 2 Ways to Remove a Property from an Object in JavaScript 'return await promise' vs 'return promise' in JavaScript How to Use Promise.allSettled() How to Use fetch() with JSON JavaScript Promises: then(f,f) vs then(f).catch(f) What is a Promise in JavaScript? How to Use Promise.all() A Simple Guide to Component Props in React Don't Stop Me Now: How to Use React useTransition() hook A Simple Explanation of JavaScript Variables: const, let, var ES Modules Dynamic Import How to Memoize with React.useMemo() How to Cleanup Async Effects in React Why Math.max() Without Arguments Returns -Infinity How to Debounce and Throttle Callbacks in React Don't Confuse Function Expressions and Function Declarations in JavaScript How to Use ES Modules in Node.js Solving a Mystery Behavior of parseInt() in JavaScript How to Use Array Reduce Method in JavaScript 3 Ways to Merge Arrays in JavaScript A Guide to Jotai: the Minimalist React State Management Library The Difference Between Values and References in JavaScript How to Implement a Queue in JavaScript A Helpful Algorithm to Determine "this" value in JavaScript React useRef() Hook Explained in 3 Steps 7 Interview Questions on "this" keyword in JavaScript. Can You Answer Them? How to Greatly Enhance fetch() with the Decorator Pattern 7 Interview Questions on JavaScript Closures. Can You Answer Them? What's a Method in JavaScript? array.sort() Does Not Simply Sort Numbers in JavaScript How to Solve the Infinite Loop of React.useEffect() The New Array Method You'll Enjoy: array.at(index) What's the Difference between DOM Node and Element? Why Promises Are Faster Than setTimeout()? Everything About Callback Functions in JavaScript How React Updates State 5 Mistakes to Avoid When Using React Hooks 5 Best Practices to Write Quality JavaScript Variables Type checking in JavaScript: typeof and instanceof operators 3 Ways to Check if a Variable is Defined in JavaScript React Forms Tutorial: Access Input Values, Validate, Submit Forms Prototypal Inheritance in JavaScript How to Timeout a fetch() Request How to Learn JavaScript If You're a Beginner A Simple Explanation of React.useEffect() A Simple Explanation of JavaScript Iterators How to Use React Controlled Inputs Everything about null in JavaScript How to Use Fetch with async/await Getting Started with Arrow Functions in JavaScript An Interesting Explanation of async/await in JavaScript Front-end Architecture: Stable and Volatile Dependencies Is it Safe to Compare JavaScript Strings? How to Access Object's Keys, Values, and Entries in JavaScript What Actually is a String in JavaScript? 3 Ways to Shallow Clone Objects in JavaScript (w/ bonuses) Checking if an Array Contains a Value in JavaScript JavaScript Event Delegation: A Beginner's Guide How to Parse URL in JavaScript: hostname, pathname, query, hash 3 Ways to Detect an Array in JavaScript How to Get the Screen, Window, and Web Page Sizes in JavaScript 3 Ways to Check If an Object Has a Property/Key in JavaScript How to Compare Objects in JavaScript Object.is() vs Strict Equality Operator in JavaScript Own and Inherited Properties in JavaScript 5 Differences Between Arrow and Regular Functions How to Use Object Destructuring in JavaScript Your Guide to React.useCallback()
How To Use Correctly JavaScript Utility Libraries
Dmitri Pavlutin · 2020-01-02 · via Dmitri Pavlutin Blog

The big JavaScript bundle is a common performance problem in Frontend development. Nobody likes slow, heavy applications and looking at the loading spinner.

The utility libraries (collections of functions implementing common tasks like finding unique items of an array, formatting dates, etc.) impact heavily the bundle size. Examples of utility libraries in JavaScript are Lodash, Ramda, Date-fns, Math.js.

Let's discuss when it makes sense to use utility libraries. And importantly, how to integrate libraries to minimally affect the bundle size.

1. My rule of thumb

If the following is true for a particular problem:

Writing the solution by yourself would look similar to the solution provided by the utility library.

then it's ok to use the utility library.

In simple words, you want to avoid reinventing the wheel and leverage existing solutions.

Let's continue looking at some pros and cons of using utility libraries.

2. The pros

2.1 Prevents reinventing the wheel

The main benefit of utility libraries is code reuse. When facing a problem, research if there isn't a good solution already.

The common Frontend problems like:

  • Operations on arrays
  • Complex string manipulations
  • Formatting dates
  • URL parse and stringify
  • and alike

are already solved by utility libraries.

And let's be honest. Writing your own date formatting utility when developing a Frontend application is wasting of time.

2.2 Quality

Popular utility libraries provide more than just a solution to common problems.

When you're using a quality library, you inherit these nice benefits:

  • The well documented library lets you spend less time in understanding how to use it.
  • The well tested library demonstrates that it behaves correctly, even in edge cases.
  • Known libraries are performance checked too, so you can be sure there's no performance bottleneck.
  • Known libraries are used by hundreds of developers in hundreds of applications: is bullet-proofed by the community.

3. The cons

3.1 Bundle size increase

The usage of a utility comes with costs. The first one being the potential increase of the bundle size.

Bundle size increases too much when you include the whole library. You should always cherry pick only the functions that you use.

For example, importing lodash library such way:


// Not good!

import _ from 'lodash';

_.uniq([1, 1, 2, 3]); // => [1, 2, 3]


clutters the JavaScript bundle with all the functions of lodash library, while you use only _.uniq.

The sections 4.1 Cherry pick functions and 4.2 ES2015 modules enable tree shaking describe how to include only the used functions into the bundle.

3.2 Tight coupling

The code of your application becomes tightly coupled to the utility library. It puts you in a risk zone because you depend on the library API, bugs, maintenance.

Tight coupling to utility library

If the utility function has a bug, you'll have to wait until the library author fixes the problem.

If you'd need a different behavior of some utility function, you have to contact the author for necessary changes. Often the author could refuse the proposed updates because that's only your use case.

Integrate only the libraries that are high quality, well tested, actively maintained, have an API that fits your current and potential future uses cases.

4. Tips

4.1 Cherry pick functions

To avoid the increase of bundle size when using the utility library, a good approach is to cherry-pick the utility functions.

For example, here's how you could cherry-pick uniq function out of lodash:


import uniq from 'lodash/uniq';

uniq([1, 1, 2, 3]); // => [1, 2, 3]


import uniq from 'lodash/uniq' picks only the uniq function from the library. It guarantees the minimal bundle size.

Cherry-picking functions become daunting when you'd like to include multiple functions. Each picked function requires an import statement, which is overwhelming:


import uniq from 'lodash/uniq';

import flatten from 'lodash/flatten';

uniq(flatten([[1, 2], [2]])) // => [1, 2]


The next approach using ES2015 named imports, even when importing multiple functions, requires just one import statement.

4.2 ES2015 modules enable tree shaking

ES2015 modules are static: what is imported and exported doesn't change during runtime. Bundlers like Webpack and Rollup eliminate the unused code by analyzing the static modules structure. This optimization is also called tree shaking.

Tree shacking

To enable tree shaking include the ES2015 modules build of the utility library into your application. The field module in the package.json file indicates the ES2015 modules build:


// package.json of the utility library

{

"name": "library",

"module": "es/index.js", // ES2015 modules build

"main": "index.js"

}


Some libraries like lodash publish a standalone package having ES2015 build: lodash-es.

For example, let's use lodash-es to import uniq and flatten functions:


import { uniq, flatten } from 'lodash-es';

uniq(flatten([[1, 2], [2]])) // => [1, 2]


import { uniq, flatten } from 'lodash-es' includes uniq and flatten functions from the library. Tree shacking optimization will add to the bundle the code of uniq and flatten functions only.

4.3 Small focused modules

Small focused modules practice suggests the use of standalone tiny npm packages for each function, instead of using an entire library of functions.

A small focused modules is easier to reason about, is loaded faster by the package manager. But there's a downside you should be aware of: the common code of tiny modules gets duplicated.

For example, let's use lodash.debounce and lodash.throttle packages:


// package.json of your application

{

"name": "my-application",

"dependencies": {

"lodash.debounce": "4.0.8",

"lodash.throttle": "4.1.1"

}

}


Then include separately these functions in your application:


import debounce from 'lodash.debounce';

import throttle from 'lodash.throttle';

// use debounce and throttle


lodash.debounce and lodash.throttle have some code in common. Because these are independent packages, the common code is duplicated into the final bundle. Even having tree shaking enabled, the duplication cannot be eliminated.

Don't use the tiny npm packages when integrating 2 or more functions from the same utility library. Use the entire library as a dependency, then cherry-pick the necessary functions to avoid the common code duplication problem:


import debounce from 'lodash/debounce';

import throttle from 'lodash/throttle';

// use debounce and throttle


or use ES2015 module named import:


import { debounce, throttle } from 'lodash-es';

// use debounce and throttle


The common code of debounce and throttle is reused when the functions are picked from the library. In the final bundle, only 1 copy of the common code is included.

4.4 Monitor bundle size

It's wise to periodically review what's included in the JavaScript bundle. A lot of useful plugins can help you monitor the bundle size.

Webpack Bundle Analyzer is a webpack plugin that visualizes what's included in the JavaScript bundle:

Webpack Bundle Analyzer

Another interesting tool is Webpack Size Plugin. The plugin prints the sizes of webpack assets and the changes since the last build.

5. Key takeaway

Utility libraries are good for code reuse and let you concentrate on creating the application, instead of reinventing the wheel.

As a general rule of thumb, if the function you're trying to write is already implemented by a utility library, and it has almost the same size and the same functionality, it's better to use the utility function.

When integrating utilities, care must be taken to include in the bundle only the used functions. There's almost never a good idea to include the whole utility into your application because it could significantly increase the bundle size of your application.

A good approach is to cherry-pick functions from the utility. This will guarantee a small impact on the bundle size.

Alternatively, you can include ES2015 modules build and let the bundler perform tree shaking to include in the final bundle only the actually used functions.

What's your opinion? Does it worth using utility libraries?