Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide a way to lazy load AnalyticsBrowser without pulling in all the other dependencies #965

Open
gajus opened this issue Oct 6, 2023 · 4 comments
Labels
enhancement New feature or request

Comments

@gajus
Copy link

gajus commented Oct 6, 2023

It appears that at the moment, simply importing import { AnalyticsBrowser } from '@segment/analytics-next'; causes various dependencies get pulled into the main bundle. I can md5 and other things I would not expect.

@gajus
Copy link
Author

gajus commented Oct 6, 2023

It is not a dramatic difference, but it would be still nice to address it:

with @segment/analytics-next: 555 993 bytes (69% ununsed)
without @segment/analytics-next: 445 108 bytes (66% ununsed)

I would imagine that there should be an entry point such as:

import { AnalyticsBrowser } from '@segment/analytics-next/lazy';

Where the entry would be along the lines of:

const analytics = {
  identify: () => {},
  load: () => {
    // lazy load Segment and replace the instance
  },
  reset: () => {},
  track: () => {},
};

@silesky
Copy link
Contributor

silesky commented Oct 6, 2023

Thank @gajus, we are always looking to reduce our dependency footprint and I think there are ways we can optimize that in the coming years.

That said, most if not all of these dependencies you see are part of our the core logic, and so are not something that can tree shaken from the main bundle. For example, md5 is part of the core event queue. Loading involves instantiating the event queue, which is why you will see it there (though, side note: we have discussed ditching md5 for an alternative strategy 😉). It's possible that we can do further optimization with some significant architectural work, but something that needs to be done thoughtfully.

We do have quite a few parts of our app that are dynamically loaded / chunked to optimize the main bundle size, so this is definitely in line with our thought process.

@gajus
Copy link
Author

gajus commented Oct 6, 2023

I don't quite follow. All you need to do is move:

export class AnalyticsBrowser extends AnalyticsBuffered {
  private _resolveLoadStart: (
    settings: AnalyticsBrowserSettings,
    options: InitOptions
  ) => void

  constructor() {
    const { promise: loadStart, resolve: resolveLoadStart } =
      createDeferred<Parameters<AnalyticsBrowser['load']>>()

    super((buffer) =>
      loadStart.then(([settings, options]) =>
        loadAnalytics(settings, options, buffer)
      )
    )

    this._resolveLoadStart = (settings, options) =>
      resolveLoadStart([settings, options])
  }

  /**
   * Fully initialize an analytics instance, including:
   *
   * * Fetching settings from the segment CDN (by default).
   * * Fetching all remote destinations configured by the user (if applicable).
   * * Flushing buffered analytics events.
   * * Loading all middleware.
   *
   * Note:️  This method should only be called *once* in your application.
   *
   * @example
   * ```ts
   * export const analytics = new AnalyticsBrowser()
   * analytics.load({ writeKey: 'foo' })
   * ```
   */
  load(
    settings: AnalyticsBrowserSettings,
    options: InitOptions = {}
  ): AnalyticsBrowser {
    this._resolveLoadStart(settings, options)
    return this
  }

  /**
   * Instantiates an object exposing Analytics methods.
   *
   * @example
   * ```ts
   * const ajs = AnalyticsBrowser.load({ writeKey: '<YOUR_WRITE_KEY>' })
   *
   * ajs.track("foo")
   * ...
   * ```
   */
  static load(
    settings: AnalyticsBrowserSettings,
    options: InitOptions = {}
  ): AnalyticsBrowser {
    return new AnalyticsBrowser().load(settings, options)
  }

  static standalone(
    writeKey: string,
    options?: InitOptions
  ): Promise<Analytics> {
    return AnalyticsBrowser.load({ writeKey }, options).then((res) => res[0])
  }
}

to a separate file with a dedicated entry point, and then lazy load loadAnalytics function.

This will allow to initialize core logic without pulling in all dependencies into the main bundle.

@silesky
Copy link
Contributor

silesky commented Oct 6, 2023

Thanks @gajus, I misread what you were asking. Example was v. helpful. We will track / review this!

edit: if you're interested in contributing, a PoC PR could speed things up a bit.

@silesky silesky added the enhancement New feature or request label Oct 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants