How to do async components in browserify #620

Closed
taoeffect opened this Issue Nov 30, 2016 · 2 comments

Projects

None yet

2 participants

@taoeffect
taoeffect commented Nov 30, 2016 edited

Currently this page claims that this is "not possible":

If you're a Browserify user that would like to use async components, it's unfortunately not possible and probably never will be, as its creator has made it clear that async loading "is not something that Browserify will ever support." If this is a feature that's important to you, we recommend using Webpack instead.

It is in fact possible to do code splitting just fine with browserify. We're doing it in our project.

For example, when using grunt-browserify, as part of its config you can specify which components you want to create that are standalone, as I do here using a custom browserifyCfg function to generate the config:

    browserify: browserifyCfg({
      straight: [{ 'dist/simple/app.js': ['frontend/simple/main.js'] }],
      lazy: [{ 'dist/simple/js/UserGroupView.js': ['frontend/simple/views/UserGroupView.vue'] }]
    }),

Thus UserGroupView is created as a separate file from the rest of the bundle. Here's the (simplified) code for browserifyCfg:

function browserifyCfg ({straight, lazy}, cfg = {}) {
  var globalize = x => // views/UserGroupView.vue -> UserGroupView
    S(path.parse(x).name).dasherize().capitalize().camelize().s
  var keyify = x => // views/UserGroupView.vue -> userGroupView
    S(path.parse(x).name).dasherize().chompLeft('-').camelize().s
  var p = (s, ...v) => _.flatten(_.zip(s, v)).join('').replace('/', path.sep)

  function gencfg (out, paths, isLazy) {
    var c = {
      options: {
        transform: ['vueify', 'babelify']
      },
      files: _.fromPairs([[out, paths]])
    }
    if (isLazy) {
      c.options.browserifyOptions.standalone = globalize(out) // <-- this creates a standalone file
      c.options.exclude = ['vue', 'vue-hot-reload-api']
    }
    return c
  }
  for (let map of straight) {
    for (let out in map) {
      cfg[keyify(out)] = gencfg(out, map[out], false)
    }
  }
  for (let map of lazy) {
    for (let out in map) {
      cfg[keyify(out)] = gencfg(out, map[out], true)
    }
  }
  return cfg
}

And then you lazily load the component using a library like VueScript2, e.g.:

import VS2 from 'vue-script2'
Vue.use(VS2)

function lazyLoadVue (component, base = '/js') {
  return function (resolve, reject) {
    VS2.load(`${base}/${component}.js`)
    .then(() => resolve(window[component]))
    .catch((err) => reject(err))
  }
}

// lazy load the component
lazyLoadVue('UserGroupView')

Alternatively, there's also Yahoo's extractify browserify transform, which is a:

Browserify plugin to extract code to be lazy loaded into separate bundles

Not sure if that's relevant/useful tho.

@taoeffect

I can submit a PR to address this, but am not sure what form it should take. It could just be a link to this issue as an example of how to do it. Or w/e.

@chrisvfritz
Member

Thanks for sharing this workaround @taoeffect! 🎉 I just updated that note with a link to this thread, which I'll close now, but still welcome people to share any techniques they've found helpful to get past this limitation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment