choojs / choo

:steam_locomotive::train: - sturdy 4kb frontend framework
https://choo.io/
MIT License
6.78k stars 595 forks source link

Code splitting and Es module dynamic import #622

Closed ansarizafar closed 6 years ago

ansarizafar commented 6 years ago

Is there any plan to support code splitting and Es module dynamic import.

marcbachmann commented 6 years ago

Thanks for bringing that up. I've also experimented a bit and I'm in interested in that. Basically code splitting isn't necessary related to choo at all. It already supports the registration of routes after rendering a view, so it will work as soon as you have the dynamic imports working. You'll just need to decide how you want to structure the application.

Here's some code extracted from my prototype. Sadly I was only able to get it to run using webpack. I wasn't able to get the imports running in bankai/browserify. Bankai just throws weird errors.

npm init -y
npm install --save-dev babel-core choo-devtools babel-plugin-syntax-dynamic-import
// .babelrc
{
  "plugins": ["syntax-dynamic-import"]
}
// index.js
const choo = require('choo')
const html = require('choo/html')
const app = choo()

if (typeof window !== 'undefined') window.app = app
app.use(require('choo-devtools')())
app.route('/', (state, emit) => html`<body>root</body>`)
app.route('/some-route', (state, emit) => html`<body>some-route</body>`)

subapp(app, {
  path: '/subroute',
  getModule () { return import('./subroute') }
})

app.mount('body')

function subapp (app, {path, getModule}) {
  const subApp = {
    isLoaded: false,
    isLoading: false,
    loadingView: (state, emit) => html`<body>Loading...</body>`,
    indexView: (state, emit) => html`<body>${path} App: Not Found</body>`,
    fallbackView: (state, emit) => html`<body>${path} App: Not Found</body>`
  }

  app.route(path, loadSubApp)
  app.route(path + '/*', loadSubApp)

  function loadSubApp (state, emit) {
    if (subApp.isLoaded && state.href === path) return subApp.indexView(state, emit)
    else if (subApp.isLoaded) return subApp.fallbackView(state, emit)
    if (subApp.isLoading) return subApp.loadingView(state, emit)

    subApp.isLoading = true
    getModule().then(function (subAppModule) {
      subApp.isLoading = false
      subApp.isLoaded = true
      subAppModule({
        use: app.use.bind(app),
        state: app.state,
        route (route, handler) {
          if (/^[/]?$/.test(route)) {
            subApp.indexView = handler
            return
          } else if (route === '/*') {
            subApp.fallbackView = handler
            return
          }
          app.route(`${path}${route}`, handler)
        }
      })
      emit('replaceState', state.href)
    })

    return subApp.loadingView(state, emit)
  }
}
// subroute.js
const html = require('choo/html')

module.exports = function (choo) {
  choo.route('/something-else', (state, emit) => html`<body>something else</body>`)
  choo.route('/*', (state, emit) => html`<body>subroute wildcard</body>`)
}

Sorry if this doesn't work right away, I've just extracted it without an additional test. This could use some iterations and some cleanup/simplification. It already works in almost all cases; there's only one small glitch with a view sometimes.

ansarizafar commented 6 years ago

Unfortunately Bankai is using browserify. Code splitting is very important specially for PWAs. Bankai must support code splitting/Dynamic Es module import.

tornqvist commented 6 years ago

There's split-require authored with choo and bankai in mind for just this use case. I haven't tried it out myself but looks to be what you're looking for.

goto-bus-stop commented 6 years ago

split-require also comes with bankai by default, so you can use it today :) you still have to do some manual work like in @marcbachmann's example, but with require('split-require') instead of import() syntax.

(i'm hoping to make import() syntax compile to split-require calls in bankai, but some hairy babel things need to be hashed out first, so it might take a while)

ansarizafar commented 6 years ago

Why not just use webpack, parceljs or rollupjs for bundling, tree shaking, code splitting and dynamic es module imports like so many other projects.

goto-bus-stop commented 6 years ago

You can use those if you want! Bankai supports all those things except ES modules, because they are not yet finished in node, and Browserify/bankai aim to match node.js. Tools like webpack and parcel take nonstandard source code, which is fine for them, but not in line with bankai goals.

yoshuawuyts commented 6 years ago

Yep, what @goto-bus-stop says. A big requirement of Bankai was that it works natively in Node. That said, you're free to use any tool that works for you - Choo works with (almost) all build tools! :sparkles:

goto-bus-stop commented 6 years ago

I don't think there's anything actionable here—if you have a feature request or further questions in this area feel free to open a new issue tho.