From 6e55b7d92571df34b8783fee3c4771af6a46f03f Mon Sep 17 00:00:00 2001 From: restrry Date: Mon, 6 Apr 2020 12:28:46 +0200 Subject: [PATCH 1/2] add lazy loading section for client bundles --- src/core/MIGRATION.md | 61 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md index 5d7b467052029e..a6ce97e4b8663b 100644 --- a/src/core/MIGRATION.md +++ b/src/core/MIGRATION.md @@ -24,6 +24,7 @@ - [7. Switch to new platform services](#7-switch-to-new-platform-services) - [8. Migrate to the new plugin system](#8-migrate-to-the-new-plugin-system) - [Bonus: Tips for complex migration scenarios](#bonus-tips-for-complex-migration-scenarios) + - [Keep Kibana fast](#keep-kibana-fast) - [Frequently asked questions](#frequently-asked-questions) - [Is migrating a plugin an all-or-nothing thing?](#is-migrating-a-plugin-an-all-or-nothing-thing) - [Do plugins need to be converted to TypeScript?](#do-plugins-need-to-be-converted-to-typescript) @@ -933,6 +934,66 @@ For a few plugins, some of these steps (such as angular removal) could be a mont One convention that is useful for this is creating a dedicated `public/np_ready` directory to house the code that is ready to migrate, and gradually move more and more code into it until the rest of your plugin is essentially empty. At that point, you'll be able to copy your `index.ts`, `plugin.ts`, and the contents of `./np_ready` over into your plugin in the new platform, leaving your legacy shim behind. This carries the added benefit of providing a way for us to introduce helpful tooling in the future, such as [custom eslint rules](https://github.com/elastic/kibana/pull/40537), which could be run against that specific directory to ensure your code is ready to migrate. +## Keep Kibana fast +**tl;dr**: Load as much code lazily as possible. +Everyone loves snappy applications with responsive UI and hates spinners. Users deserve the best user experiences regardless of whether they run Kibana locally or in the cloud, regardless of their hardware & environment. +There are 2 main aspects of the perceived speed of an application: loading time and responsiveness to user actions. +New platform loads and bootstraps **all** the plugins whenever a user lands on any page. It means that adding every new application affects overall **loading performance** in the new platform, as plugin code is loaded **eagerly** to initialize the plugin and provide plugin API to dependent plugins. +However, it's usually not necessary that the whole plugin code should be loaded and initialized at once. The plugin could keep on loading code covering API functionality on Kibana bootstrap but load UI related code lazily on-demand, when an application page or management section is mounted. +For this purpose, Kibana provides dedicated integration points: async `mount` functions. Use them to keep the initially loaded bundle as slim as possible without any loss in the functionality. + +```typescript +import { Plugin, CoreSetup, AppMountParameters } from 'src/core/public'; +export class MyPlugin implements Plugin { + setup(core: CoreSetup, plugins: SetupDeps){ + core.application.register({ + id: 'app', + title: 'My app', + async mount(params: AppMountParameters) { + const { mountApp } = await import('./app/mount_app'); + return mountApp(await core.getStartServices(), params); + }, + }); + plugins.management.sections.getSection('another').registerApp({ + id: 'app', + title: 'My app', + order: 1, + async mount(params) { + const { mountManagementSection } = await import('./app/mount_management_section'); + return mountManagementSection(coreSetup, params); + }, + }) + return { + doSomething(){} + } + } +} +``` + +#### How to understand how big the bundle size of my plugin is? +New platform plugins are distributed as a pre-built with `@kbn/optimizer` package artifacts. It allows us to get rid of the shipping of `optimizer` in the distributable version of Kibana. +Every NP plugin artifact contains all plugin dependencies required to run the plugin, except some stateful dependencies shared across plugin bundles via `@kbn/ui-shared-deps`. +It means that NP plugin artifacts tend to have a bigger size than the legacy platform version. +To understand the current size of your plugin artifact, run `@kbn/optimizer` as +```bash +node scripts/build_kibana_platform_plugins.js --dist --no-examples +``` +and check the output in the `target` sub-folder of your plugin folder +```bash +ls -lh plugins/my_plugin/target/public/ +# output +# an async chunk loaded on demand +... 262K 0.plugin.js +# eagerly loaded chunk +... 50K my_plugin.plugin.js +``` +you might see at least one js bundle - `my_plugin.plugin.js`. This is the only artifact loaded by the platform during bootstrap in the browser. The rule of thumb is to keep its size as small as possible. +Other lazily loaded parts of your plugin present in the same folder as separate chunks under `{number}.plugin.js` names. +If you want to investigate what your plugin bundle consists of you need to run `@kbn/optimizer` with `--profile` flag to get generated [webpack stats file](https://webpack.js.org/api/stats/). +Many OSS tools are allowing you to analyze generated stats file +- [an official tool](http://webpack.github.io/analyse/#modules) from webpack authors +- [webpack-visualizer](https://chrisbateman.github.io/webpack-visualizer/) + ## Frequently asked questions ### Is migrating a plugin an all-or-nothing thing? From af421f5588656d0af465ddd91ab9cf58e876d0ce Mon Sep 17 00:00:00 2001 From: restrry Date: Thu, 9 Apr 2020 18:45:29 +0200 Subject: [PATCH 2/2] add Pierres suggestion --- src/core/MIGRATION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md index a6ce97e4b8663b..368d1f47e9c3fa 100644 --- a/src/core/MIGRATION.md +++ b/src/core/MIGRATION.md @@ -940,7 +940,7 @@ Everyone loves snappy applications with responsive UI and hates spinners. Users There are 2 main aspects of the perceived speed of an application: loading time and responsiveness to user actions. New platform loads and bootstraps **all** the plugins whenever a user lands on any page. It means that adding every new application affects overall **loading performance** in the new platform, as plugin code is loaded **eagerly** to initialize the plugin and provide plugin API to dependent plugins. However, it's usually not necessary that the whole plugin code should be loaded and initialized at once. The plugin could keep on loading code covering API functionality on Kibana bootstrap but load UI related code lazily on-demand, when an application page or management section is mounted. -For this purpose, Kibana provides dedicated integration points: async `mount` functions. Use them to keep the initially loaded bundle as slim as possible without any loss in the functionality. +Always prefer to require UI root components lazily when possible (such as in mount handlers). Even if their size may seem negligible, they are likely using some heavy-weight libraries that will also be removed from the initial plugin bundle, therefore, reducing its size by a significant amount. ```typescript import { Plugin, CoreSetup, AppMountParameters } from 'src/core/public';