Programing

Nuxt TTFB 향상

c10106 2022. 4. 9. 09:28
반응형

Nuxt TTFB 향상

나는 Nuxt와 Vuetify를 이용하여 큰 어플리케이션을 만들고 있어, 모든 것이 잘 되고 잘 작동하지만 안타깝게도 Lighthouse의 점수는 42점 만으로 최고가 아니야.

나는 이미 다음과 같은 몇 가지를 개선했다.

  • 구글에서 더 나은 글꼴 로딩;
  • 비동기 코드 이동 위치nuxtServerInit레이아웃에 따라,
  • 불필요한 타사 서비스 제거.

42번에서 54번까지 갔지만 아직도 결과가 별로 마음에 들지 않는다.

불행히도 나는 지식이 부족하기 때문에 이런 개선들을 하는데 최고가 아니다.

알겠다TTFB전혀 최적이진 않지만 난 내가 무엇을 개선할 수 있는지 잘 모르겠어...그러니 힌트와 제안으로 내 지원서를 활성화할 수 있도록 도와주길 바란다.

여기에 내 것을 붙일 것이다.nuxt.congig.js내가 뭘 어떻게 사용하는지 알 수 있도록:

const path = require('path')
const colors = require('vuetify/es5/util/colors').default
const bodyParser = require('body-parser')

const maxAge = 60 * 60 * 24 * 365 // one year
const prefix = process.env.NODE_ENV === 'production' ? 'example.' : 'exampledev.'
const description =
  'description...'

let domain
if (
  process.env.NODE_ENV === 'production' &&
  process.env.ENV_SLOT === 'staging'
) {
  domain = 'example.azurewebsites.net'
} else if (
  process.env.NODE_ENV === 'production' &&
  process.env.ENV_SLOT !== 'staging'
) {
  domain = 'example.com'
} else {
  domain = ''
}

module.exports = {
  mode: 'universal',

  /**
   * Disabled telemetry
   */
  telemetry: false,

  /*
   ** Server options
   */
  server: {
    port: process.env.PORT || 3030
  },

  serverMiddleware: [
    bodyParser.json({ limit: '25mb' }),
    '~/proxy',
    '~/servermiddlewares/www.js'
  ],

  router: {
    middleware: 'maintenance'
  },

  env: {
    baseUrl:
      process.env.NODE_ENV === 'production'
        ? 'https://example.com'
        : 'https://localhost:3030',
    apiBaseUrl:
      process.env.API_BASE_URL || 'https://example.azurewebsites.net'
  },

  /*
   ** Headers of the page
   */
  head: {
    title: 'example',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      {
        hid: 'description',
        name: 'description',
        content: description
      },
      {
        hid: 'fb:app_id',
        property: 'fb:app_id',
        content: process.env.FACEBOOK_APP_ID || 'example'
      },
      {
        hid: 'fb:type',
        property: 'fb:type',
        content: 'website'
      },
      {
        hid: 'og:site_name',
        property: 'og:site_name',
        content: 'example'
      },
      {
        hid: 'og:url',
        property: 'og:url',
        content: 'https://example.com'
      },
      {
        hid: 'og:title',
        property: 'og:title',
        content: 'example'
      },
      {
        hid: 'og:description',
        property: 'og:description',
        content: description
      },
      {
        hid: 'og:image',
        property: 'og:image',
        content: 'https://example.com/images/ogimage.jpg'
      },
      {
        hid: 'robots',
        name: 'robots',
        content: 'index, follow'
      },
      {
        name: 'msapplication-TileColor',
        content: '#ffffff'
      },
      {
        name: 'theme-color',
        content: '#ffffff'
      }
    ],
    link: [
      {
        rel: 'apple-touch-icon',
        sizes: '180x180',
        href: '/apple-touch-icon.png?v=GvbAg4xwqL'
      },
      {
        rel: 'icon',
        type: 'image/png',
        sizes: '32x32',
        href: '/favicon-32x32.png?v=GvbAg4xwqL'
      },
      {
        rel: 'icon',
        type: 'image/png',
        sizes: '16x16',
        href: '/favicon-16x16.png?v=GvbAg4xwqL'
      },
      { rel: 'manifest', href: '/site.webmanifest?v=GvbAg4xwqL' },
      {
        rel: 'mask-icon',
        href: '/safari-pinned-tab.svg?v=GvbAg4xwqL',
        color: '#777777'
      },
      { rel: 'shortcut icon', href: '/favicon.ico?v=GvbAg4xwqL' },
      {
        rel: 'stylesheet',
        href:
          'https://fonts.googleapis.com/css?family=Abril+Fatface|Raleway:300,400,700&display=swap'
      }
    ]
  },

  /*
   ** Customize the page loading
   */
  loading: '~/components/loading.vue',

  /*
   ** Global CSS
   */
  css: ['~/assets/style/app.scss', 'swiper/dist/css/swiper.css'],

  /*
   ** Plugins to load before mounting the App
   */
  plugins: [
    '@/plugins/axios',
    '@/plugins/vue-swal',
    '@/plugins/example',
    { src: '@/plugins/vue-infinite-scroll', ssr: false },
    { src: '@/plugins/croppa', ssr: false },
    { src: '@/plugins/vue-debounce', ssr: false },
    { src: '@/plugins/vue-awesome-swiper', ssr: false },
    { src: '@/plugins/vue-html2canvas', ssr: false },
    { src: '@/plugins/vue-goodshare', ssr: false }
  ],

  /*
   ** Nuxt.js modules
   */
  modules: [
    '@/modules/static',
    '@/modules/crawler',
    '@nuxtjs/axios',
    '@nuxtjs/auth',
    '@nuxtjs/device',
    '@nuxtjs/prismic',
    '@dansmaculotte/nuxt-security',
    '@nuxtjs/sitemap',
    [
      '@nuxtjs/google-analytics',
      {
        id: 'example',
        debug: {
          sendHitTask: process.env.NODE_ENV === 'production'
        }
      }
    ],
    ['cookie-universal-nuxt', { parseJSON: false }],
    'nuxt-clipboard2'
  ],

  /*
   ** Security configuration
   */
  security: {
    dev: process.env.NODE_ENV !== 'production',
    hsts: {
      maxAge: 15552000,
      includeSubDomains: true,
      preload: true
    },
    csp: {
      directives: {
        // removed contents
      }
    },
    referrer: 'same-origin',
    additionalHeaders: true
  },

  /*
   ** Prismic configuration
   */
  prismic: {
    endpoint: 'https://example.cdn.prismic.io/api/v2',
    preview: false,
    linkResolver: '@/plugins/link-resolver',
    htmlSerializer: '@/plugins/html-serializer'
  },

  /*
   ** Auth module configuration
   */
  auth: {
    resetOnError: true,
    localStorage: false,
    cookie: {
      prefix,
      options: {
        maxAge,
        secure: true,
        domain
      }
    },
    redirect: {
      callback: '/callback',
      home: false
    },
    strategies: {
      local: {
        endpoints: {
          login: {
            url: '/auth/local',
            method: 'POST',
            propertyName: 'token'
          },
          logout: { url: '/auth/logout', method: 'POST' },
          user: { url: '/me', method: 'GET', propertyName: false }
        },
        tokenRequired: true,
        tokenType: 'Bearer'
      },
      google: {
        client_id:
          process.env.GOOGLE_CLIENT_ID ||
          'example'
      },
      facebook: {
        client_id: process.env.FACEBOOK_APP_ID || 'example',
        userinfo_endpoint:
          'https://graph.facebook.com/v2.12/me?fields=about,name,picture{url},email',
        scope: ['public_profile', 'email']
      }
    }
  },

  /*
   ** Vuetify Module initialization
   */
  buildModules: [
    ['@nuxtjs/pwa', { meta: false, oneSignal: false }],
    '@nuxtjs/vuetify'
  ],

  /*
   ** Vuetify configuration
   */
  vuetify: {
    customVariables: ['~/assets/style/variables.scss'],
    treeShake: true,
    rtl: false,
    defaultAssets: {
      font: false,
      icons: 'fa'
    }
  },

  /*
   ** Vue Loader configuration
   */
  chainWebpack: config => {
    config.plugin('VuetifyLoaderPlugin').tap(() => [
      {
        progressiveImages: true
      }
    ])
  },

  /*
   ** Build configuration
   */
  build: {
    analyze: true,
    optimizeCSS: true,
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {
      config.resolve.alias.vue = 'vue/dist/vue.common'
      // Run ESLint on save
      if (ctx.isDev && ctx.isClient) {
        config.devtool = 'cheap-module-source-map'
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/,
          options: {
            fix: true
          }
        })
      }
      if (ctx.isServer) {
        config.resolve.alias['~'] = path.resolve(__dirname)
        config.resolve.alias['@'] = path.resolve(__dirname)
      }
    }
  }
}

몇 가지 유용한 정보:

  • 나는 각 페이지와 컴포넌트마다 범위형만 사용하고, Vuetify의 거의 모든 것을 그대로 사용하고 있기 때문에 맞춤형의 양은 정말 형편없다.
  • 브라우저에서 "페이지 소스 보기"를 수행하면 페이지 내에 최소화가 아닌 매우 긴 CSS가 표시되는 것을 좋아하지 않는다.
  • 다음을 사용하여 아무것도 로드하지 않는다.fetch또는asyncData구성 요소가 장착되면 데이터를 로드하는 것이 좋다.
  • 에브리싱은 Azure에 배치되었고 나는 A를 소비한다.Net core API.

좋은 점은 특히 TTFB의 성능을 개선하기 위한 몇 가지 예를 갖춘 모범 사례들이다.

Lighthouse I see "Remove 미사용 JavaScript(JavaScript 제거)"를 참조하십시오./_nuxt/..例句...되지 않은 것 가 붙어 하지만 이 파일들은 사용되지 않은 것 같지 않은데 왜 이렇게 플래그가 붙었는지 알고 싶어...

Azure가 각 배치마다 프로젝트를 청소해야 할까?나도 몰라...나는 그것을 사용한다.azAzure Cli와 I는 단지 하기만 하면 구축된다.git push azure master특별한 건 없어

"초기 서버 응답 시간 단축"…어떻게?생산 앱이 실행되는 계획은 아즈레에서 빠를수록 빠른데, 무엇을 어떻게 개선해야 할까?

"메인 스레드 작업 최소화":그것은 무엇을 뜻하나요?

"자바스크립트 실행 시간 단축": 어떻게?

나는 네가 내가 모든 것을 이해하고 활성화할 수 있도록 도와줬으면 좋겠어.

너의 요청으로 이 게시물을 업데이트 할 테니, 아마 너는 그 프로젝트에 대해 더 많은 것을 보고 싶어 할 거야.고마워요.

나는 최근에 다소 큰 규모의 Nuxt 애플리케이션으로 이 과정을 겪어야 했으므로, 우리가 생각해낸 통찰력과 해결책들 중 일부를 공유할 수 있다.우리는 행복하기 전에 간신히 40점 정도 앞섰다.

누구라도 읽을 수 있는 조언 1위:프레임워크를 폐기하십시오.설계상, 그들은 가능한 많은 일반적인 사용 사례를 처리하고 크기를 희생시키면서 가능한 한 쉽게 적용할 수 있도록 비대해진다.크기와 속도가 전부인 브라우저의 영역에서 각각의 새로운 프레임워크(Nuxt, Vue, Vuetify)는 크기와 속도에 부정적인 영향을 미치는 추상화 층을 더한다.

어쨌든, 그 문제를 해결하면서, 프레임워크를 버릴 수 없는 사람들을 위한 몇 가지 다른 조언이 있다.

등대는 종종 오해를 불러일으킬 수 있다.

우리는 "Remove used Javascript" 경고가 기본적으로 Vue로 수정될 수 없다는 것을 발견했다.문제는 Lighthouse가 테스트 중에 실제로 실행되는 코드만 검사할 수 있고, Vue 런타임에서 오류 처리나 온클릭 처리를 위한 코드가 당연히 필요한지 알 수 없다는 점이다.

불행히도 런타임에 어떤 코드가 필요한지 미리 알 수 없기 때문에 모든 코드를 전송해야 한다.그러나 개발자로서 당신은 적어도 애플리케이션의 초기 로딩 동안 필요한 제3자 라이브러리, 모듈 및 플러그인에 대한 제어권을 가지고 있다.필요한 물건만 보내고 쓰도록 하는 것은 너에게 달려있다.

그래서 라이트하우스 눈에는 쓸모없고 사용되지 않는 코드가 많이 있다.하지만, 응용 프로그램이 어떤 것을 해야 한다면, 더 이상 쓸모없는 것이 아니다.그래서 그것이 다소 오해의 소지가 있는 것이다.

Javascript 응용프로그램의 작동 방식에 대한 사실만으로 이러한 도구들이 보고할 "문제"가 많기 때문에 항상 이 점을 명심하십시오.필자에게 있어, 이러한 프레임워크의 개발자들은 구글의 관점에서 Javascript 앱에 진정으로 접근하고 성능을 발휘할 수 있도록 하는데 있어서 아직 극복해야 할 몇 가지 장애물이 더 있는 것 같다.

플러그인과 모듈을 짧게 유지하십시오.

에서 애플리케이션에 추가하는 각 플러그인nuxt.config.js각 페이지에 포함되는 메인 JS 번들의 크기를 증가시킨다.이것은 불가피하게 많은 사용하지 않는 코드, 거대한 JS 파일 크기, 그리고 물론, 더 긴 로딩 시간으로 이어진다.

대신 필요한 페이지에만 플러그인을 추가하는 것이 타당하다.

// inside the SocialSharing.vue component
import Vue from 'vue'
import VueGoodshare from 'vue-goodshare'
Vue.use(VueGoodshare)

export default { ... }

주의사항:이 가져오기가 수행되는 페이지에는 여전히 다음에서 가져온 모든 코드가 있을 것이다.vue-goodshare추가. 대신 실제로 필요한 이러한 라이브러리의 구성 요소만 포함하는 것이 훨씬 낫다.

이것을 확인하는 좋은 방법은 당신의 빌드를analyze진실로 정해진 재산(여기서 분석을 공유하는 것이 도움이 될 수 있다)

초기 서버 응답 시간 단축

만약 당신이 이미 최고의 서버를 운영하고 있다면, 당신이 일의 속도를 높이기 위해 할 수 있는 몇 가지 일이 있다.

  1. 페이지를 서버 측에 렌더링할 필요가 없도록 캐슁 기능을 활용하십시오.그러나 이러한 시험들 중 일부는 특별히 캐싱을 무력화시켜 결과가 좋지 않게 된다.
  2. 페이지를 렌더링하는 데 필요한 작업량을 줄이십시오.차단 API 호출이 발생하지 않도록 하고, 페이지를 단순하고 작게 유지하고, 서버에 과부하가 걸리지 않도록 하십시오.
  3. 에지 캐슁 또는 에지 배포를 활용하여 애플리케이션을 사용자에게 보다 가깝게 만드십시오.예를 들어, 애플리케이션이 USWEST에 배치되고 Lighthouse가 두바이에서 테스트되고 있는 경우, 해당 요청에서 많은 지연 시간이 발생할 수 있으며, 이는 서버 응답 시간을 증가시킬 것이다.

더 많은 도움을 받으려면 실행 중인 특정 서버와 해당 서버의 위치를 추적해야 할 수 있다.하지만, 내가 개략적으로 설명한 점들은 거의 확실히 당신의 TFFB를 녹색 점수로 만들 것이다.

주 스레드 작업 최소화

브라우저에서 메인 스레드는 모든 동작이 일어나는 곳이다.그것은 사용자 상호작용을 처리하고 페이지를 업데이트하며, 본질적으로 HTML 문서를 살아있는 애플리케이션으로 바꾸는 것을 전적으로 책임진다.너무 바쁜 메인 스레드는 성능 문제로 이어질 수 있으며, 특히 사용자가 페이지와 상호 작용하려고 할 때 눈에 띈다.

종종 이것을 보면, Javascript를 너무 많이 실행하기 때문이다.특히 너무 많은 Javascript를 한꺼번에 실행하다 보니 결국 메인 실을 차단하게 되는 겁니다.자바스크립트가 많은 어플리케이션은 이것 때문에 악명이 높으며, 그것은 정말 풀기 어려운 문제가 될 수 있다.

우리 앱의 가장 큰 도우미는 중요하지 않은 스크립트 로딩을 지연시키는 것이었습니다.예를 들어, 우리는 롤바와 구글 애널리틱스를 모든 페이지에서 실행한다.app-start에서 스크립트를 로드하는 대신 작은 명령 대기열만 로드하고 대형 스크립트의 로드 시간을 최대 5초까지 지연시킨다.이로써 메인 스레드는 Vue 애플리케이션 리무드와 같은 보다 중요한 것에 집중할 수 있게 되었다.

또한 처리할 JS의 양을 줄임으로써 상당한 비용 절감 효과를 얻을 수 있다.클라이언트에 반환되는 각 코드 라인은 전송, 구문 분석 및 실행되어야 하는 다른 라인이다.난 분명히 너의 모습을 볼 것이다.modules그리고plugins제일 먼저 열매가 낮게 매달렸는지부터 알아봐야지

Javascript 실행 시간 단축

이것은 우리의 시험에서 종종 "앱이 여전히 무언가를 하고 있다"는 것을 의미하는 또 다른 불행한 측정법이다.나는 그것이 우리의 경험에서 응용프로그램의 성능이나 사용자 경험에 영향을 미치지 않았기 때문에 유감이라고 말한다.

우리는 인터컴, 롤바, GA 등 제3자 서비스를 자주 보았는데, 제3자 코드로는 사용하지 않는 것 외에 할 수 있는 일이 없다.

내 조언:내가 강조했던 다른 모든 것을 사용하여 응용 프로그램을 최적화하는 데 집중하십시오.이것은 구체적으로 고치기가 믿을 수 없을 정도로 어려울 수 있는 것이며, 대개는 메인 실이 너무 바쁘거나, 3부 코드가 느리게 되는 등 다른 것들의 증상일 뿐이다.

마지막 조언 하나

다른 모든 테스트가 실패할 경우, 자신에게 유리한 테스트 중 일부를 "트레킹"할 수 있을 것이다.테스트가 끝날 때까지 GA와 롤바 스크립트의 로드를 지연시킴으로써 이렇게 했다.이 도구는 특정 기간 동안 특정 메트릭스를 살펴보고 그에 따라 점수를 매긴다는 점을 기억하십시오.폴드 아래의 게으른 하중과 같은 간단한 대체 기법을 활용하여 성능의 현저한 차이를 알 수 있을 것이다.

어쨌든, 이것은 꽤 복잡한 작업이며, 여기에는 결코 "성공을 위한 3단계 가이드"가 없다.온라인에서는 몇 가지 간단한 변경으로 Vue 앱을 30개에서 100개까지 가져왔다고 주장하는 가이드를 많이 찾을 수 있지만, 실제 앱은 코드를 많이 가지고 있고 많은 일을 한다는 사실을 무시하며 속도와 성능으로 균형을 맞추는 것이 예술의 한 형태다.

셸 애플리케이션 모델 또는 서비스 직원과 같은 리소스를 살펴보십시오.

이 게시물에 대한 설명이 필요하면 언제든지 문의해 보십시오.하지만 당신이 묻고 있는 질문은 광범위하며, 단 하나의 "올바른" 접근방식이 있는 것은 아니라는 것을 명심하라.여기서 중요한 비트를 가져다가 가능한 한 응용하는 것은 결국 너에게 달려 있다.

예를 사용하여 업데이트

내가 말한 대부분의 내용은 지나치게 단순하고 설명이 필요 없는 주제를 다루었거나 처음부터 모호한 개념을 다루었기 때문에 예시를 보여주기가 꽤 어려웠다.그러나 우리가 사용한 방법 중 어느 정도 좋은 결과가 나온 방법이 하나 나올 수 있다.

Intercom을 로드하는 데 사용하는 수정된 스크립트의 예는 다음과 같다.

    var APP_ID = "your_app_id_here";
    window.intercomSettings = {
        app_id: APP_ID,
        hide_default_launcher: !0,
        session_duration: 36e5
    },
    function() {
        var n,
            e,
            t = window,
            o = t.Intercom;
        "function" == typeof o ? (o("reattach_activator"), o("update", t.intercomSettings)) : (n = document, (e = function() {
            e.c(arguments)
        }).q = [], e.c = function(t) {
            e.q.push(t)
        }, t.Intercom = e, o = function() {

            // Don't load the full Intercom script until after 10s
            setTimeout(function() {
                var t = n.createElement("script");
                t.type = "text/javascript",
                t.crossorigin = "anonymous",
                t.async = !0,
                t.src = "https://widget.intercom.io/widget/" + APP_ID;
                var e = n.getElementsByTagName("script")[0];
                e.parentNode.insertBefore(t, e)
            }, 1e4)

        }, "complete" === document.readyState ? o() : t.attachEvent ? t.attachEvent("onload", o) : t.addEventListener("load", o, !1))

앱에 배치하기 위해 제공하는 스크립트의 사용자 지정 버전<head></head>태그. 하지만, 당신은 우리가 추가했다는 것을 알게 될 것이다.setTimeout전체 인터컴 스크립트의 로딩을 지연시키는 기능.이것은 당신의 애플리케이션이 네트워크나 CPU 시간에 경쟁하지 않고 다른 모든 것을 로드할 수 있는 기회를 준다.

하지만, 인터콤은 더 이상 이용이 보장되지 않기 때문에, 인터컴과 상호작용할 때 더 많은 주의를 기울여야 할 것이다.

이 정확히 동일한 개념은 당신이 로드할 수 있는 거의 모든 제3자 스크립트에 적용될 수 있다.또한 명령 대기열을 초기화하지만 실제 스크립트 로딩은 연기하는 Google Analytics와 함께 사용하십시오.분명히, 이것은 짧은 세션의 추적 문제를 야기할 수 있지만, 성능이 주요 목표라면, 그것이 당신이 해야 할 절충이다.

참조URL: https://stackoverflow.com/questions/62535339/improve-nuxt-ttfb

반응형