Maayan Naveh
Maayan Naveh

Reputation: 370

Rails 6 + Webpacker Lazy Loading

I'm running rails assets:precompile and it outputs this:

WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
    This can impact web performance.
    Assets: 
      js/application-c4e0541a90e01b47fbfe.js (1.21 MiB)
      js/chartkick-b109b176a3de896848c7.js (657 KiB)
      js/datatables-fca8769ee16df57bdc4a.js (296 KiB)
      js/application-c4e0541a90e01b47fbfe.js.gz (305 KiB)
      js/datatables-fca8769ee16df57bdc4a.js.map.gz (482 KiB)
      js/chartkick-b109b176a3de896848c7.js.map.gz (493 KiB)
      js/application-c4e0541a90e01b47fbfe.js.map.gz (1.09 MiB)

WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
  application (1.21 MiB)
      js/application-c4e0541a90e01b47fbfe.js
  chartkick (657 KiB)
      js/chartkick-b109b176a3de896848c7.js
  datatables (296 KiB)
      js/datatables-fca8769ee16df57bdc4a.js


WARNING in webpack performance recommendations: 
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/

I've been looking at lazy loading but can't find anything about rails (I'm super new to webpacker, js, etc). This link, for example, explains about splitting code and lazy loading, but I can't figure out how to call that on my super simple code.

Here's an example of the datatables code I have:

//Global setting and initializer
// Add DataTables jQuery plugin
require('imports-loader?define=>false!datatables.net')(window, $)
require('imports-loader?define=>false!datatables.net-select')(window, $)
require('imports-loader?define=>false!datatables.net-bs4')(window, $)
require('imports-loader?define=>false!datatables.net-select-bs4')(window, $)

// Load datatables styles
 import 'datatables.net-bs4/css/dataTables.bootstrap4.css'
 import 'datatables.net-select-bs4/css/select.bootstrap4.css'

$(document).on('turbolinks:load', () => {
  $(document.body).on('click', '#check_all', () => {
    var checkBoxes = $('input[type="checkbox"]')
    checkBoxes.prop("checked", !checkBoxes.prop("checked"))
  })
})


// init on turbolinks load
$(document).on('turbolinks:load', function() {
  if (!$.fn.DataTable.isDataTable(".datatable")) {
    $(".datatable").DataTable();
  }
});

// turbolinks cache fix
$(document).on('turbolinks:before-cache', function() {
  var dataTable = $($.fn.dataTable.tables(true)).DataTable();
  if (dataTable !== null) {
    dataTable.destroy();
    return dataTable = null;
  }
});

Would love to get some pointers on this, it's clogging my app up and the app crashes. Thanks!

Upvotes: 0

Views: 2061

Answers (1)

rossta
rossta

Reputation: 11494

To add dynamic imports, you may need to make a few changes to your existing code:

  1. Extract a function for conditionally initializing instances of the plugin
  2. Use the import().then() dynamic import statement for each of your datatables modules or for a separate file that imports those modules.

Extract an initializer function

Since you'll be importing datatables asynchronously, you can't count on it being loaded yet. This is just an example, but one way to deal with this is a reusable function anywhere you want to do datatables initialization (and perhaps another for the "before-cache" hook), that will behave gracefully if datatables hasn't loaded yet.

function initializeDatatables() {
  if (!$.fn.DataTable) return;

  if (!$.fn.DataTable.isDataTable(".datatable")) {
    $(".datatable").DataTable();
  }
}

Use the dynamic import

Since you have four separate imports, you could try importing them as one "chunk" from a separate file:

// app/javascript/datatables.js
require('imports-loader?define=>false!datatables.net')(window, $)
require('imports-loader?define=>false!datatables.net-select')(window, $)
require('imports-loader?define=>false!datatables.net-bs4')(window, $)
require('imports-loader?define=>false!datatables.net-select-bs4')(window, $)

// your original initializer file
import('./datatables').then(initializeDatatables)

$(document).on('turbolinks:load', initializeDatatables);

You could also try using the splitChunks api so Webpack will take care of putting your JS into separate chunks, but that's a separate topic.

Upvotes: 1

Related Questions