This article was originally published at: github.com/bigo-fronte… Welcome to follow and reprint.

The background,

LikeeLive held an annual online event for the first time, and both user and anchor gameplay content was 2-3 times of the monthly activity. Four people were assigned to the front end for development, and the code was merged after the completion of their own development. In the self-test stage, it was found that the page of the test environment was too slow to load, and the performance score failed to meet the requirements of online.

Ii. Problem analysis

Enlarge figure firstThe Performance score is only 32, a very poor score for user experience. The loading time of the first screen is 6.2 seconds, and the blocking time is 480ms. The maximum size of CSS files exceeds 1.7m, and the maximum size of js files exceeds 800kb+.

Third, the problem plan

First of all, the loading time is related to the size of the resource file, so the 1.7m CSS file and 800KB JS file should be slim first.

CSS File AnalysisDuring the construction of the project, pictures less than 10KB will be converted into Base64 strings and stored in CSS files. The number of local pictures used in this activity is 700+, and icon small ICONS 100+. After checking the compiled CSS files, there are a large number of Base64 encoded pictures, so we consider adding Sprite images for construction. Merge small ICONS to reduce the number of images transferred to Base64 encoding and network requests.

At present, the icon icon is called through mixin, and the calling method is as follows:

@mixin Ricon($width.$height.$url.$important: ' ') {
  @include background(#{$baseURL#} {$url}.png.$important);
  display: inline-block;
  width: $width;
  height: $height;
  background-size: 100% auto;
  background-position: center center;
}

@mixin background($url: ' '.$important: ' ') {
  @if $url! =' ' {
    @if ($important! =' ') {
      background-image: url($url) ! important;
    }
    @else {
      background-image: url($url); }}background-repeat: no-repeat;
  background-size: 100% auto;
}
Copy the code

Considering the cost of transformation and the cost of accessing Sprite images, the solution is to integrate Webpack-Spritesmith in the construction process, which has the advantage of

  1. Sprite and SCSS files can be automatically generated and updated according to the specified directory during the construction process
  2. The automatically generated SCSS file template allows customization
  3. Instead of manually importing the generated SCSS file, you can configure automatic import in the WebPack configuration

The build configuration is as follows:

  // Configure the code
  new SpritesmithPlugin({
    src: {
      // icon Small icon directory
      cwd: './src/like/act_30083/assets/img/icon/'.// Composite image format
      glob: '*.png'
    },
    target: {
      // The local file address of the composite Sprite image
      image: path.resolve(__dirname, './assets/img/sprite-ignore.png'),
      css: [[// Generate the Sprite image style file address
          path.resolve(__dirname, './styles/mixins/_sprite-ignore.scss'),
          {
            // SCSS file template
            format: 'function_based_template'}}]].customTemplates: {
      // Customize the template
      'function_based_template':  templateFunction,
    },
    apiOptions: {
      // Sprite map reference address
      cssImageRef: '~@assets/img/sprite-ignore.png',},spritesmithOptions: {
      // The composition rule
      algorithm: 'binary-tree'.// Distance between ICONS
      padding: 10,}});Copy the code

Considering the cost of adapting mixins and the cost of impacting existing code, templates are defined as follows:

// Template code
function templateFunction(data) {
  const spritesheet = data.spritesheet // Sprite object
  const unit = 'px' / / unit
  const w = spritesheet.width // Sprite map width
  const h  = spritesheet.height // The Sprite is tall

  // Loop Sprite and set the width and height of the corresponding icon style
  // data.sprites: Sprite small icon traversal
  const perSprite = data.sprites.map(function (sprite) {
    const offsetX = sprite.offset_x // The x distance from the top left corner, which is negative, is calculated by default
    const offsetY = sprite.offset_y // The y distance from the top left corner, which is negative, is calculated by default
    // icon small icon class names will start with an icon- prefix. If the image name does not contain an icon- prefix, the prefix will be automatically added
    const prefix = sprite.name.indexOf('icon-') > -1 ? ' ' : 'icon-'
    // You need to return and return a default style
      return `.${sprite.name} {
                background-image: url(${spritesheet.image});
                background-size: ${w}${w === 0 ? ' ' : unit} ${h}${h === 0 ? ' ' : unit};
                background-repeat: no-repeat;
                width: ${sprite.width + 2}${unit};
                height: ${sprite.height + 2}${unit};
                background-position: ${offsetX ! = =0 ? offsetX - 1 : offsetX }${offsetX === 0 ? ' ' : unit} ${offsetY ! = =0 ? offsetY - 1 : offsetY }${offsetY === 0 ? ' ' : unit};
                display: inline-block;
              }
              `
  }).join('\n')

  return perSprite
}
Copy the code

Modifications to existing mixins are as follows:

@mixin Ricon($width.$height.$url.$important: ' ') {
  @extend. # {$url};
}
Copy the code

Modify the construction method, introduce the construction of Sprite image, ensure that the rule generated by Sprite image is consistent with the naming rule of small icon image, to ensure that the code does not need to be modified in a large area, realize the code optimization achieved by the smallest granularity code change, after the modification, the CSS file is reduced from 1.7m to 300KB +

The next step is to process the JS file, review the js file code of the home page, and find that due to too much activity content, the home page component has 20+, and the entry file has loaded all the third-party component libraries. The solution is as follows:

  1. The first-screen visual area component is loaded synchronously, and the first-screen non-visual area component is loaded asynchronously() => import()In the webpack construction process, the component code loaded asynchronously is separated from the JS file loaded on the first screen and loaded separately, and the loading and rendering of the first screen non-visual area component is set after the loading and rendering of the first screen visual area component is completed.
  2. The third-party component repository is divided into global reference and local reference, and the locally referenced third-party library components are split into corresponding components for asynchronous loading.

After optimization, the JS file is reduced from 800KB + to 200KB +

After the optimization of SCSS and JS files, the loading time is reduced from 6.2s to 3.8s and 2.4s, but the loading time is still too long. The network analysis of the page shows that the main reason is as follows

  1. There are a lot of necessary JS resources in front of the page and they must be loaded before the business code
  2. The head GIF uses SVGA for loading display with a loading size of 1.7m, which takes a long time to load and takes a long time to block rendering.

Preconnect and DNS-prefetch are added to the head header to optimize the problem of long loading time of pre-loaded resources

preconnect

Preconnect allows the browser to perform several actions prior to an HTTP request being formally sent to the server, including DNS resolution, TLS negotiation, and TCP handshake, which eliminates round-trip latency and saves the user time.

dns-Prefetching

DNS prefetching allows the browser to run DNS resolution in the background while the user is browsing the page. You can fetch DNS prefetch on the specified URL by adding rel=” dnS-prefetch “to the attribute of a link tag.

“DNS requests have very little traffic in terms of bandwidth, but latency can be high, especially on mobile devices. DNS designation by Prefetching can significantly reduce delay in any scenario.

Svga is divided into two parts: opening play and loop play. The size and loading speed are as follows:

Loading 2s without caching results in a large white space when the page opens. There are two solutions: one is to use static images to replace blank space during loading to improve user experience, but it does not help the loading time; the other is to use other formats to replace SVGA to reduce the loading time and improve user experience.

At present, the mainstream Gif implementation methods include Gif, PNG frame animation, SVGA and Lottie.

Gif/ frame animation is realized by playing each frame of the picture, which needs to load larger resources and achieve longer effects. Compared with SVGA, the realization mode is worse.

Svga is compared to Lottie as follows:

Lottie is superior to Lottie by comparison, but there is a large change and the change cost is relatively high. Considering that the current header graph does not need to use aPLHA channel, it can be realized by using segmented video, with small changes and low development cost. The implementation code is as follows:

<! -- Address is the video address returned by the back end, support video segmentation, set the cover to avoid slow video loading in the case of weak network -->
<video
  ref="video"
  preload="auto"
  :poster="headImg"
  :src="headerMp4"
  autoplay
  muted
  loop
  webkit-playsinline="true"
  playsinline="true"></video>
Copy the code

conclusion

This activity is the biggest one I have been responsible for since I joined the team, and it took 4 people 15 days to complete the development. In the process of development, we also faced the urgent processing of online business and the frequent changes of demand content from the demander in the process of development, but we still completed the development task and delivered the acceptance test before the deadline. After this demand optimization, also in the field of front-end performance optimization has also gained a certain growth. I hope the team can continue to work together in such an atmosphere and make progress together.

Welcome everyone to leave a message to discuss, wish smooth work, happy life!

I’m bigO front. See you next time.