How to write JS module for exporting both default and non default functions / methods

43 Views Asked by At

I'm used to write classes in JS modules like the following:

class Auth{
  constructor() {
    init();        
  }

  init() {
    // ...
  }

  getUserData() {
    // ...
  }

  signin(username, password) {
     // ...
  }
}
export default new Auth();

My code is then optimized with Webpack. My goal is minimize the bundles size. For some applications, I don't need all the methods of my class. But most of the time, my methods use this to share variables.

With classes, tree shaking can't remove dead code. For example, if I never use the getUserData(), it will be imported anyway because the method is part of the class.

How could I rewrite my module to be able to import only some methods

import {init, getUserData} from "auth.js"

and also all the methods at once:

import auth from "auth.js"
1

There are 1 best solutions below

5
Jakub Kotrs On

A class in JS is a fancy-pants sugar but you can't use it with destructuring (and so the methods can't even be named exports for the same reason).

const person = new Human()

const { eat, drink } = person

// this would probably not work
eat()
drink()

The reason is that you will lose context (the value of this) when you're destructuring. And therefore if you tried to export individual methods as named exports.

So to achieve what you're asking about, you need to export a bunch of functions from a closure

// to share a variable, you need to put it into the top level scope
let something

export function eat() {
  something = true
}
export function drink() {
  if (something) {
    foo()
  } else {
    bar()
  }
}

both of those functions would be using state instead of this, the module itself acts as a closure.

Now, as for the imports, you can have it both ways if you approach it as a namespace:

import { eat, drink } from './person'
import * as person from './person' // this is the "import namespace" pattern

// both work
eat()
person.eat()

If you absolutely need to, you can then also make a default export

export function eat() {}
export function drink() {}
export default { eat, drink }

for importing as default:

import person from './person'