A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://docs.gitlab.com/development/fe_guide/performance/ below:

Performance | GitLab Docs

Performance is an essential part and one of the main areas of concern for any modern application.

Monitoring

We have a performance dashboard available in one of our Grafana instances. This dashboard automatically aggregates metric data from sitespeed.io every 4 hours. These changes are displayed after a set number of pages are aggregated.

These pages can be found inside text files in the sitespeed-measurement-setup repository called gitlab Any frontend engineer can contribute to this dashboard. They can contribute by adding or removing URLs of pages to the text files. The changes are pushed live on the next scheduled run after the changes are merged into main.

There are 3 recommended high impact metrics (core web vitals) to review on each page:

For these metrics, lower numbers are better as it means that the website is more performant.

User Timing API

User Timing API is a web API available in all modern browsers. It allows measuring custom times and durations in your applications by placing special marks in your code. You can use the User Timing API in GitLab to measure any timing, regardless of the framework, including Rails, Vue, or vanilla JavaScript environments. For consistency and convenience of adoption, GitLab offers several ways to enable custom user timing metrics in your code.

User Timing API introduces two important paradigms: mark and measure.

Mark is the timestamp on the performance timeline. For example, performance.mark('my-component-start'); makes a browser note the time this code is met. Then, you can obtain information about this mark by querying the global performance object again. For example, in your DevTools console:

performance.getEntriesByName('my-component-start')

Measure is the duration between either:

It takes several arguments of which the measurement’s name is the only one required. Examples:

To query a particular measure, You can use the same API, as for mark:

performance.getEntriesByName('My component')

You can also query for all captured marks and measurements:

performance.getEntriesByType('mark');
performance.getEntriesByType('measure');

Using getEntriesByName() or getEntriesByType() returns an Array of the PerformanceMeasure objects which contain information about the measurement’s start time and duration.

User Timing API utility

You can use the performanceMarkAndMeasure utility anywhere in GitLab, as it’s not tied to any particular environment.

performanceMarkAndMeasure takes an object as an argument, where:

Attribute Type Required Description mark String no The name for the mark to set. Used for retrieving the mark later. If not specified, the mark is not set. measures Array no The list of the measurements to take at this point.

In return, the entries in the measures array are objects with the following API:

Attribute Type Required Description name String yes The name for the measurement. Used for retrieving the mark later. Must be specified for every measure object, otherwise JavaScript fails. start String no The name of a mark from which the measurement should be taken. end String no The name of a mark to which the measurement should be taken.

Example:

import { performanceMarkAndMeasure } from '~/performance/utils';
...
performanceMarkAndMeasure({
  mark: MR_DIFFS_MARK_DIFF_FILES_END,
  measures: [
    {
      name: MR_DIFFS_MEASURE_DIFF_FILES_DONE,
      start: MR_DIFFS_MARK_DIFF_FILES_START,
      end: MR_DIFFS_MARK_DIFF_FILES_END,
    },
  ],
});
Vue performance plugin

The plugin captures and measures the performance of the specified Vue components automatically leveraging the Vue lifecycle and the User Timing API.

To use the Vue performance plugin:

  1. Import the plugin:

    import PerformancePlugin from '~/performance/vue_performance_plugin';
  2. Use it before initializing your Vue application:

    Vue.use(PerformancePlugin, {
      components: [
        'IdeTreeList',
        'FileTree',
        'RepoEditor',
      ]
    });

The plugin accepts the list of components, performance of which should be measured. The components should be specified by their name option.

You might need to explicitly set this option on the needed components, as most components in the codebase don’t have this option set:

export default {
  name: 'IdeTreeList',
  components: {
    ...
  ...
}

The plugin captures and stores the following:

Access stored measurements

To access stored measurements, you can use either:

Naming convention

All the marks and measures should be instantiated with the constants from app/assets/javascripts/performance/constants.js. When you’re ready to add a new mark’s or measurement’s label, you can follow the pattern.

This pattern is a recommendation and not a hard rule.

app-*-start // for a start 'mark'
app-*-end   // for an end 'mark'
app-*       // for 'measure'

For example, 'webide-init-editor-start, mr-diffs-mark-file-tree-end, and so on. We do it to help identify marks and measures coming from the different apps on the same page.

Best Practices Real-time Components

When writing code for real-time features we have to keep a couple of things in mind:

  1. Do not overload the server with requests.
  2. It should feel real-time.

Thus, we must strike a balance between sending requests and the feeling of real-time. Use the following rules when creating real-time solutions.

  1. The server tells you how much to poll by sending Poll-Interval in the header. Use that as your polling interval. This enables system administrators to change the polling rate. A Poll-Interval: -1 means you should disable polling, and this must be implemented.
  2. A response with HTTP status different from 2XX should disable polling as well.
  3. Use a common library for polling.
  4. Poll on active tabs only. Use Visibility.
  5. Use regular polling intervals, do not use backoff polling or jitter, as the interval is controlled by the server.
  6. The backend code is likely to be using ETags. You do not and should not check for status 304 Not Modified. The browser transforms it for you.
Lazy Loading Images

To improve the time to first render we are using lazy loading for images. This works by setting the actual image source on the data-src attribute. After the HTML is rendered and JavaScript is loaded, the value of data-src is moved to src automatically if the image is in the current viewport.

When asynchronously adding content which contains lazy images, call the function gl.lazyLoader.searchLazyImages() which searches for lazy images and loads them if needed. In general, it should be handled automatically through a MutationObserver in the lazy loading function.

Animations

Only animate opacity & transform properties. Other properties (such as top, left, margin, and padding) all cause Layout to be recalculated, which is much more expensive. For details on this, see High Performance Animations.

If you do need to change layout (for example, a sidebar that pushes main content over), prefer FLIP. FLIP allows you to change expensive properties once, and handle the actual animation with transforms.

Prefetching assets

In addition to prefetching data from the API we allow prefetching the named JavaScript “chunks” as defined in the Webpack configuration. We support two types of prefetching for the chunks:

Both prefetch and preload links bring the loading performance benefit to the pages. Both are fetched asynchronously, but contrary to deferring the loading of the assets which is used for other JavaScript resources in the product by default, prefetch and preload neither parse nor execute the fetched script unless explicitly imported in any JavaScript module. This allows to cache the fetched resources without blocking the execution of the remaining page resources.

To prefetch a JavaScript chunk in a HAML view, :prefetch_asset_tags with the combination of the webpack_preload_asset_tag helper is provided:

- content_for :prefetch_asset_tags do
  - webpack_preload_asset_tag('monaco')

This snippet will add a new <link rel="preload"> element into the resulting HTML page:

<link rel="preload" href="/assets/webpack/monaco.chunk.js" as="script" type="text/javascript">

By default, webpack_preload_asset_tag will preload the chunk. You don’t need to worry about as and type attributes for preloading the JavaScript chunks. However, when a chunk is not critical, for the current navigation, one has to explicitly request prefetch:

- content_for :prefetch_asset_tags do
  - webpack_preload_asset_tag('monaco', prefetch: true)

This snippet will add a new <link rel="prefetch"> element into the resulting HTML page:

<link rel="prefetch" href="/assets/webpack/monaco.chunk.js">
Universal code

Code that is contained in main.js and commons/index.js is loaded and run on all pages. Do not add anything to these files unless it is truly needed everywhere. These bundles include ubiquitous libraries like vue, axios, and jQuery, as well as code for the main navigation and sidebar. Where possible we should aim to remove modules from these bundles to reduce our code footprint.

Page-specific JavaScript

Webpack has been configured to automatically generate entry point bundles based on the file structure in app/assets/javascripts/pages/*. The directories in the pages directory correspond to Rails controllers and actions. These auto-generated bundles are automatically included on the corresponding pages.

For example, if you were to visit https://gitlab.com/gitlab-org/gitlab/-/issues, you would be accessing the app/controllers/projects/issues_controller.rb controller with the index action. If a corresponding file exists at pages/projects/issues/index/index.js, it is compiled into a webpack bundle and included on the page.

Previously, GitLab encouraged the use of content_for :page_specific_javascripts in HAML files, along with manually generated webpack bundles. However under this new system you should not ever need to manually add an entry point to the webpack.config.js file.

When unsure what controller and action corresponds to a page, inspect document.body.dataset.page in your browser’s developer console from any page in GitLab.

TROUBLESHOOTING: If using Vite, keep in mind that support for it is new and you may encounter unexpected effects from time to time. If the entrypoint is correctly configured but the JavaScript is not loading, try clearing the Vite cache and restarting the service: rm -rf tmp/cache/vite && gdk restart vite

Alternatively, you can opt to use Webpack instead. Follow these instructions for disabling Vite and using Webpack.

Important Considerations Code Splitting

Code that does not need to be run immediately upon page load (for example, modals, dropdowns, and other behaviors that can be lazy-loaded) should be split into asynchronous chunks with dynamic import statements. These imports return a Promise which is resolved after the script has loaded:

import(/* webpackChunkName: 'emoji' */ '~/emoji')
  .then(/* do something */)
  .catch(/* report error */)

Use webpackChunkName when generating dynamic imports as it provides a deterministic filename for the chunk which can then be cached in the browser across GitLab versions.

More information is available in the webpack code splitting documentation and the Vue dynamic component documentation.

Minimizing page size

A smaller page size means the page loads faster, especially on mobile and poor connections. The page is parsed more quickly by the browser, and less data is used for users with capped data plans.

General tips:

Additional Resources

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4