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

推荐订阅源

F
Full Disclosure
Stack Overflow Blog
Stack Overflow Blog
H
Help Net Security
Microsoft Azure Blog
Microsoft Azure Blog
The GitHub Blog
The GitHub Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Recorded Future
Recorded Future
Y
Y Combinator Blog
Cloudbric
Cloudbric
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Schneier on Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
C
Cybersecurity and Infrastructure Security Agency CISA
TaoSecurity Blog
TaoSecurity Blog
Security Latest
Security Latest
N
News and Events Feed by Topic
Hacker News - Newest:
Hacker News - Newest: "LLM"
D
DataBreaches.Net
Security Archives - TechRepublic
Security Archives - TechRepublic
H
Hacker News: Front Page
C
Cisco Blogs
L
LangChain Blog
aimingoo的专栏
aimingoo的专栏
Recent Commits to openclaw:main
Recent Commits to openclaw:main
V
Vulnerabilities – Threatpost
L
LINUX DO - 最新话题
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Google Online Security Blog
Google Online Security Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
A
About on SuperTechFans
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Jina AI
Jina AI
C
CXSECURITY Database RSS Feed - CXSecurity.com
Schneier on Security
Schneier on Security
T
Tenable Blog
N
News and Events Feed by Topic
W
WeLiveSecurity
有赞技术团队
有赞技术团队
AI
AI
爱范儿
爱范儿
大猫的无限游戏
大猫的无限游戏
Latest news
Latest news
T
The Blog of Author Tim Ferriss
S
Security Affairs
Know Your Adversary
Know Your Adversary
Simon Willison's Weblog
Simon Willison's Weblog
G
GRAHAM CLULEY
Google DeepMind News
Google DeepMind News
The Cloudflare Blog

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()
4 Best Practices to Write Quality JavaScript Modules
Dmitri Pavlutin · 2020-02-26 · via Dmitri Pavlutin Blog

Using ES2015 modules you can chunk the application code into reusable, encapsulated, one-task focused modules.

That's all good, but how do you structure modules? How many functions, classes a module should have?

This post presents 4 best practices on how to organize better your JavaScript modules.

1. Prefer named exports

When I started using JavaScript modules, I had used the default syntax to export the single piece that my module defines, either a class or a function.

For example, here's a module greeter that exports the class Greeter as a default :


// greeter.js

export default class Greeter {

constructor(name) {

this.name = name;

}

greet() {

return `Hello, ${this.name}!`;

}

}


In time I had noticed the difficulty in refactoring the classes (or functions) that were default exported. When the original class was being renamed, the class name inside the consumer module didn't change.

Worse, the editor didn't provide autocomplete suggestions of the class name being imported.

I had concluded that the default export doesn't give visible benefits. Then I switched to named exports.

Let's make Greeter a named export, and see the benefits:


// greeter.js

export class Greeter {

constructor(name) {

this.name = name;

}

greet() {

return `Hello, ${this.name}!`;

}

}


With the usage of named exports, the editor does better renaming: every time you change the original class name, all consumer modules also change the class name.

The autocomplete also suggests the imported class:

JavaScript Named Import Autocomplete

So, here's my advice:

Favor named module exports to benefit from renaming refactoring and code autocomplete.

Note: when using 3rd party modules like React, Lodash, default import is ok. The default import name is a constant that doesn't change: React, _.

2. No work during import

The module-level scope defines functions, classes, light objects, and variables. The module can export some of these components. That's all.


// Module-level scope

export function myFunction() {

// myFunction Scope

}


The module-level scope shouldn't do heavy computation like parsing JSON, making HTTP requests, reading local storage, etc.

For example, the following module configuration parses the configuration from the global variable bigJsonString:


// configuration.js

export const configuration = {

// Bad

data: JSON.parse(bigJsonString)

};


That's a problem. Because the parsing of bigJsonString is performed at the module-level scope, a heavy operation is executed when configuration module is imported:


// Bad: parsing happens when the module is imported

import { configuration } from 'configuration';

export function AboutUs() {

return <p>{configuration.data.siteName}</p>;

}


At a higher level, the module-level scope's role is to define the module components, import dependencies, and export public components: dependencies resolution process. Separate it from the runtime: parsing JSON, making requests, handling events.

Let's refactor the configuration module to perform lazy parsing:


// configuration.js

let parsedData = null;

export const configuration = {

// Good

get data() {

if (parsedData === null) {

parsedData = JSON.parse(bigJsonString);

}

return parsedData;

}

};


Because data property is defined as a getter, the bigJsonString is parsed only when the consumer accesses configuration.data.


// Good: JSON parsing doesn't happen when the module is imported

import { configuration } from 'configuration';

export function AboutUs() {

// JSON parsing happens now

return <p>{configuration.data.companyDescription}</p>;

}


The consumer knows better when to perform a heavy operation. The consumer might decide to perform the operation when the browser is idle. Or the consumer might import the module, but for some reason never use it.

This opens the opportunity for deeper performance optimizations: decreasing time to interactive, minimize main thread work.

When imported, the module shouldn't execute any heavy work. Rather, the consumer should decide when to perform runtime operations.

3. Favor high cohesion modules

Cohesion describes the degree to which the components inside a module belong together.

The functions, classes or variables of a high cohesion module are closely related. They are focused on a single task.

The module formatDate is high cohesive because its functions are closely related and focus on date formatting:


// formatDate.js

const MONTHS = [

'January', 'February', 'March','April', 'May',

'June', 'July', 'August', 'September', 'October',

'November', 'December'

];

function ensureDateInstance(date) {

if (typeof date === 'string') {

return new Date(date);

}

return date;

}

export function formatDate(date) {

date = ensureDateInstance(date);

const monthName = MONTHS[date.getMonth())];

return `${monthName} ${date.getDate()}, ${date.getFullYear()}`;

}


formatDate(), ensureDateInstance() and MONTHS are closely-related to each other.

Deleting either MONTHS or ensureDateInstance() would break formatDate(): that's the sign of high cohesion.

3.1 The problem of low cohesion modules

On the other side, there are low cohesion modules. Those that contain components that are unrelated to each other.

The following utils module has 3 functions that perform different tasks:


// utils.js

import cookies from 'cookies';

export function getRandomInRange(start, end) {

return start + Math.floor((end - start) * Math.random());

}

export function pluralize(itemName, count) {

return count > 1 ? `${itemName}s` : itemName;

}

export function cookieExists(cookieName) {

const cookiesObject = cookie.parse(document.cookie);

return cookieName in cookiesObject;

}


getRandomInRange(), pluralize() and cookieExists() perform different tasks: generate a random number, format a string and check the existence of a cookie. Deleting any of these functions doesn't affect the functionality of the remaining ones: that's the sign of low cohesion.

Because the low cohesion module focuses on multiple mostly unrelated tasks, it's difficult to reason about such a module.

Plus, the low cohesion module forces the consumer to depend on modules that it doesn't always need, which creates unneeded transitive dependencies.

For example, the component ShoppingCartCount imports pluralize() function from utils module:


// ShoppingCartCount.jsx

import { pluralize } from 'utils';

export function ShoppingCartCount({ count }) {

return (

<div>

Shopping cart has {count} {pluralize('product', count)}

</div>

);

}


While ShoppingCartCount module uses only the pluralize() function out of the utils module, it has a transitive dependency on the cookies module (which is imported inside utils).

The good solution is to split the low cohesion module utils into several high cohesive ones: utils/random, utils/stringFormat and utils/cookies.

Now, if ShoppingCart module imports utils/stringFormat, it wouldn't have a transitive dependency on cookies:


// ShoppingCartCount.jsx

import { pluralize } from 'utils/stringFormat';

export function ShoppingCartCount({ count }) {

// ...

}


The best examples of high cohesion modules are Node built-in modules, like fs, path, assert.

Favor high cohesion modules whose functions, classes, variables are closely related and perform a common task. Refactor big low cohesion modules by splitting them into multiple high cohesion ones.

4. Avoid long relative paths

I find difficult to understand the path of a module that contains one, or even more parent folders:


import { compareDates } from '../../date/compare';

import { formatDate } from '../../date/format';

// Use compareDates and formatDate


While having one parent selector ../ is usually not a problem, having 2 or more is generally difficult to grasp.

That's why I'd recommend to avoid the parent folders in favor of absolute paths:


import { compareDates } from 'utils/date/compare';

import { formatDate } from 'utils/date/format';

// Use compareDates and formatDate


While the absolute paths are sometimes longer to write, using them makes it clear the location of the imported module.

To mitigate the long absolute paths, you can introduce new root directories. This is possible using babel-plugin-module-resolver, for example.

Use absolute paths instead of the long relative paths.

5. Conclusion

The JavaScript modules are great to split the logic of your application into small, self-contained chunks.

By using named exports instead of default exports, you could benefit from easier renaming refactoring and editor autocomplete assistance when importing the named component.

The sole purpose of import { myFunc } from 'myModule' is to import myFunc component, and nothing more. The module-level scope of myModule should only define classes, functions, or variables with light content.

How many functions or classes a component should have, and how do these components should relate to each one? Favor modules of high cohesion: its components should be closely related and perform a common task.

Long relative paths containing many parent folders ../ are difficult to understand. Refactor them to absolute paths.

What JavaScript modules best practices do you use?