高橋  陽子

高橋 陽子

1657880470

如何在 Next.js 應用程序中保持 Lighthouse 高分

讓我們談談可用於測試網頁的最有用的技術 SEO 性能工具之一。它是自動化的,可以衡量性能、可訪問性和 SEO。更重要的是,它是開源的並且可以免費使用——並且可以用來測試漸進式 Web 應用程序,這是我想在本文中重點介紹的內容。讓我們一起瀏覽 Lighthouse 評分成功清單。但首先,一些信息: 

Web 應用程序的 SEO 性能成為過去幾年最熱門的話題之一。作為開發人員,我們經常忘記它,直到為時已晚。這通常是由於嚴格的期限、缺乏知識或只是懶惰造成的。 

今天,性能不僅僅是渲染應用程序所需的時間。糟糕的性能會導致糟糕的用戶體驗和更糟糕的 SEO 定位——不良結果的多米諾骨牌效應會損害你的應用程序。 

速度如何影響您的應用程序的性能?

您是否知道自 2010 年以來 Google 一直在關注網絡速度的排名?

  • 2010 年,Google 宣布將在桌面設備上的用戶搜索排名算法中考慮頁面速度。
  • 2017 年 12 月,谷歌轉向“移動優先”方法,然後在 2018 年 1 月,它開始使用頁面速度作為移動搜索的排名因素。
  • 2020 年 5 月,谷歌宣布他們將衡量整體頁面體驗,並將在其排名算法中使用它。頁面體驗基於許多信號,包括:
    • Core Web Vitals(加載性能、交互性的首次輸入延遲、視覺穩定性的累積佈局移位(CLS)、
    • 移動友好性, 
    • HTTPS,
    • 沒有侵入性的插頁式廣告。

您知道應用程序性能對用戶體驗甚至收入的巨大影響嗎?以下是谷歌研究“對移動速度的需求”的一些事實:

  • 如果加載時間超過 3 秒,53%的移動網站訪問者會離開網站
  • 在 5 秒內而不是 19 秒內加載的網站的可見度提高了 25%
  • 4 個頂級移動網站中有 3 個加載時間超過10 秒
  • 在 5 秒內而不是 19 秒內加載的網站的收入增加了 2 倍

但別擔心,我準備了一份清單,可以幫助您提高應用程序的整體速度,同時改善用戶體驗、搜索引擎優化和收入。因為 Lighthouse 是由 Google 製造的,所以它使用相同的性能指標,並有助於以清晰直觀的方式改進您的漸進式 Web 應用程序。

1. Lighthouse 中的 Web Vitals

讓我們開始提高您的 Lighthouse 性能得分。在我們繼續具體的提示和技巧之前,讓我們首先了解 Lighthouse 如何理解和計算性能分數。

Lighthouse的性能部分基於6 Web Vitals 測量頁面速度。

Web Vitals 是 Google 的一項舉措,旨在為質量信號提供統一指導,這些信號對於在網絡上提供出色的用戶體驗至關重要。

讓我們總結一下它們:

燈塔得分組件

  • FCP- First Contentful Paint-測量我們的應用程序在初次訪問期間需要渲染 DOM 中的第一個元素的時間。
  • SI – 速度指數– 衡量我們的應用程序填充內容的速度。Lighthouse 創建一個幻燈片,然後將幀相互比較。
  • LCP——最大內容繪製——測量在初始視口中渲染 DOM 中最大元素所需的時間。
  • TTI——交互時間——測量應用程序何時準備好與用戶交互。對於 Lighthouse,這意味著我們已經在初始視口中渲染了所有內容並註冊了事件。
  • TBT – Total Blocking Time – 衡量我們應用程序中“長任務”(耗時超過 50 毫秒的任務)的影響,是FCPTTI之間的區別。它是如何工作的?讓我們看一下這個例子:

我們的應用程序包含 20 個任務:

  • 10 個任務每個需要 40 毫秒
  • 10 個任務每個需要 60 毫秒

TBT只關心 60 毫秒的任務(更準確地說,是 50 毫秒閾值與值本身之間的差異)。所以我們的最終結果將是:

數量 x(價值 - 閾值)= 結果

10 x (60ms – 50ms) = 100ms

  • CLS – Cumulative Layout Shift – 初始視口中所有意想不到的佈局變化。值是根據“不穩定”元素在幀之間移動的距離計算的。

有了這些知識,我們終於可以繼續“修復”我們的應用程序 Lighthouse 分數了!

2. 修復字體以提高 Lighthouse 分數

為什麼字體會影響你的燈塔分數?這是因為它們的使用方式不僅會影響頁面速度(不同的字體有不同的大小,而且我們不僅僅指它們看起來有多大!)而且會對查看者如何查看您的頁面產生深遠影響正確加載。以下是一些需要注意的事項: 

  • 自託管– 避免從您無法控制的外部服務加載字體文件。只要有可能,您應該自行託管文件以避免更長的 HTTP 請求,或者使用帶有緩存的 CDN 託管。
  • 字體擴展——字體擴展對文件的最終大小有很大的影響。如果您選擇的字體帶有不同的擴展選項,則應始終選擇WOFF2,這是最輕的字體。
     

燈塔中的字體

字體子集——一些字體有更小的變體,稱為“子集”。它們包含更少的字形,這進一步減小了文件的大小。例如,某些字體具有僅包含拉丁字母和字符的“拉丁”子集。

燈塔中的字體大小

  • 可變字體- 可以將字體的多種變體合併到單個文件中,因此我們可以僅加載一個通常小於所有文件組合的文件,而不是加載具有不同變體的“X”數量的不同文件。 

假設我們想使用字體的所有變體(在這個例子中是 9 個文件)。我們將之前的結果乘以 9,並將其大小與單個可變字體文件進行比較。

可變字體燈塔

3. 腳本

腳本也會影響您的性能——尤其是當它們在不需要的地方出現瓶頸或占用寶貴的加載時間時。使他們更容易表現的最佳方法是: 

  • 異步加載– 引用:“沒有比您的應用程序代碼更重要的第三方代碼”。始終使用asyncdefer延遲加載第三方腳本,以防止阻塞應用程序的主線程。您還可以使用next/script來設置腳本的優先級。
  • 資源提示——明智地使用資源提示來進一步減少加載腳本所需的時間。您可以在此處了解有關它們的更多信息

標記管理器——考慮將第三方腳本的加載委託給標記管理器,您可以更好地控制腳本加載的順序和腳本的數量。

4.風格 

  • CSS over CSS-in-JS 解決方案——在樣式方面,您可能需要考慮一種更“老式”的做事方式。因為在 SSR 應用程序中,我們不想用更多的 JavaScript 壓倒主線程。這就是為什麼CSS-in-JS解決方案不是最適合 Next.js 應用程序的原因。 

您可以查看比較這兩種方法的這篇文章。此外,`Next.js` 已經內置了大量的 CSS 優化,比如類名和样式縮小、sass 支持、配置的 postcss,所以它是我們的一種方式。

字體顯示——為了避免 FOUT(無樣式文本的閃爍)或看到空白屏幕,您應該始終通過使用字體上的font-display屬性來控製字體的加載。

5. 捆綁

分析捆綁包可以很好地發現我們的塊的數量和大小。我們通常可以從這樣做的工具中學到很多東西。

  • Bundle-wizard – 這是@next/bundle-analyzer的一個很酷的替代品,它允許我們檢查我們的應用程序包。在我看來,它比其他工具有 3 大優勢。
  •  
    • 它有一個更好的用戶界面來導航
    • 它提供了塊的覆蓋範圍
    • 它不僅可以在構建期間在任何已部署的應用程序上運行
  • 塊拆分——減少捆綁包大小的一個好方法是將它們拆分成更小的部分。我們的應用程序更容易加載多個較小的塊而不是幾個大塊。幸運的是,webpack 確實允許我們拆分合併的塊。我們可以在next.config中這樣做:
module.exports = {
 webpack(config, { isServer }) {
   if (!isServer) {
     config.optimization.splitChunks.cacheGroups = {
       ...config.optimization.splitChunks.cacheGroups,
       '@sentry': {
         test: /[\\/]node_modules[\\/](@sentry)[\\/]/,
         name: '@sentry',
         priority: 10,
         reuseExistingChunk: false,
       },
     };
   }
 
   return config;
 },
};

這樣,@sentry包將被分割成它自己的小塊。此外,我們可以控制模塊的優先級。

刪除重複的模塊——有時在 monorepo 架構中工作時,我們可能會得到多次捆綁的包。同樣,webpack config 帶有一個可以合併我們重複的塊的屬性。它看起來像這樣:

module.exports = {
 optimization: {
   mergeDuplicateChunks: true,
 },
};

6.CLS

CLS 成為 2021 年的排名因素,所以它是相當新的。它代表 Cumulative Layout Shift,是衡量用戶體驗溫度的指標。

每當可見元素將其位置從一個渲染幀更改為下一幀時,就會發生佈局轉換。 

為動態內容保留空間——為了防止任何意外的佈局變化,我們應該始終為尚未渲染的內容保留空間。

有很多很棒的方法,比如骨架加載,它模仿給定組件的一般外觀,包括它的寬度和高度。這樣,我們將保留確切的空間,從而消除 CLS。

但有時,我們不必使用任何花哨的東西。我們可以只插入一個空的佔位符框,這將確保用戶沒有不愉快的變化。 

7. 圖片

圖像可能是最臭名昭著的頁面速度惡棍,這個問題現在和 1999 年一樣真實。除了我們有更多的技巧來解決這個問題並提高你的整體性能得分。開始了: 

  • 新一代文件擴展名——考慮以webpjpeg2000文件擴展名提供圖像。它比傳統的jpegpng輕得多,而且沒有明顯的質量損失。有許多庫可以在上傳過程中將圖像轉換為webp,因此請隨意使用它們。但請始終記住,某些較舊的瀏覽器可能不支持該擴展,因此請準備適用格式的後備版本。 
  • 尺寸變體——Lighthouse 確實建議為每個斷點提供不同變體的圖像。像Sharp這樣的庫允許我們生成同一張圖像的多種尺寸。要顯示它們,我們可以使用<picture>  標籤或img srcSet屬性,所有的“魔法”都將由瀏覽器為我們處理。
  • 延遲加載——總是延遲加載視口之外的圖像。這樣,我們可以在第一次訪問我們的頁面時節省時間。為此,我們可以在img標籤上使用loading=”lazy”屬性。
  • 預加載——考慮預加載首屏的圖像——尤其是 LCP 元素。預加載鏈接“告訴”瀏覽器比正常情況更早地獲取內容。 

Next/image –手動完成上述所有點可能既耗時又存在問題。幸運的是,有一些圖書館可以為我們處理它。其中之一是Next/Image組件,它將通過轉換為 webp、調整大小、延遲加載和預加載 API 為我們優化圖像。
 

8. JavaScript

有時,在 SEO 性能方面,JavaScript 可能會成為反派。為了提高應用程序的燈塔分數,我們可以避免一些常見錯誤: 

  • 代碼拆分(動態導入) ——代碼拆分允許我們延遲加載一些代碼,因此它減少了我們應用程序的主線程必須做的工作量。Next/dynamic是一個很好的代碼拆分工具。使用簡單的 API,我們可以將組件拆分為單獨的塊,這些塊將按需加載。我們還可以控制組件是否應該在服務器端呈現。
  • 避免桶文件——我們傾向於創建具有多個導出的桶文件。它們很方便導入,但將來可能會成為我們的痛點。如果搖樹過程不起作用,那麼每個單獨的導入都將導入整個“庫”。所以而不是:
// Exporting from barrel file
export Button from "ui/Button";
export Text from "ui/Text";
export Select from "ui/Select";
 
// Importing in component
import { Button, Select, Text } from "ui";

我們想直接從組件路徑導入:

// Importing in component
import Button from "ui/Button";
import Text from "ui/Text";
import Select from "ui/Select";

9. 如何保持高性能

如果我們已經達到了讓我們滿意的性能水平,那麼隨著時間的推移將其保持在同一水平會很好。我們還不能稱之為星期五!有一些工具可以幫助我們做到這一點:

  • Bundle-wizard –在我們的應用程序增長時不時運行此工具是一個很好的做法,以確保包大小保持較小,並且我們不會遇到任何與塊有關的意外問題。
  • Webpack 性能提示——來自 webpack的性能提示是我們運行bundle-wizard的一個很好的指標。它們真的很容易設置和配置,當任何應用程序塊超過大小限制時,讓我們有機會在構建期間拋出警告或錯誤。
  • PageSpeed Insights / Lighthouse –當然,我們衡量應用程序性能的主要工具是 Lighthouse。我們可以通過 Chrome 瀏覽器中的開發工具運行它,也可以通過 PSI 網站運行它。
  • WebPageTest –是 Lighthouse 的一個很酷的替代品。它也為我們測量了 Web Vitals,但以更易於訪問的方式為我們提供了更多信息。例如,我們可以看到任務的瀑布或渲染過程的幻燈片。
  • Lighthouse-CI –使用Lighthouse-CI,我們可以為特定指標或 CI 流程的整體性能得分設置測試。這樣,我們可以隨著應用程序的增長持續測量頁面速度。
  • 衡量真實用戶的表現——這是對已經投入生產並擁有一些活躍用戶的應用程序的提示。衡量真實用戶的表現是了解我們的弱點在哪裡的關鍵。 

Lighthouse 或 WebPageTest 之類的工具有時會產生誤導,因為它們總是在穩定的互聯網連接、最新版本的 Chrome 等下工作……而對於我們的最終用戶而言,情況並非總是如此。通常,用戶在給定頁面上的表現可能比 Lighthouse 建議的要差得多。 

個人推薦Sentry 的性能測量工具。它非常容易設置,並且會提供大量關於用戶設備、連接、位置等的信息,這可能有助於我們解決邊緣情況問題。

Web 應用程序性能連續性 

Web App 的性能不是我們可以修復一次就忘記的。它更像是一個隨著應用程序的增長而不斷檢查、分析和改進應用程序的過程。幸運的是,我們可以而且應該盡可能地自動化這個過程,讓我們的生活更輕鬆。 

正確設置的工作流程可以防止我們推送會破壞我們的應用程序性能的代碼,在實施過程中發現錯誤,甚至指出我們應該關注的痛點。

鏈接:https ://tsh.io/blog/how-to-keep-your-lighthouse-score-high-in-next-js-applications-a-checklist/

#nextjs #javascript #lighthouse

What is GEEK

Buddha Community

如何在 Next.js 應用程序中保持 Lighthouse 高分

NBB: Ad-hoc CLJS Scripting on Node.js

Nbb

Not babashka. Node.js babashka!?

Ad-hoc CLJS scripting on Node.js.

Status

Experimental. Please report issues here.

Goals and features

Nbb's main goal is to make it easy to get started with ad hoc CLJS scripting on Node.js.

Additional goals and features are:

  • Fast startup without relying on a custom version of Node.js.
  • Small artifact (current size is around 1.2MB).
  • First class macros.
  • Support building small TUI apps using Reagent.
  • Complement babashka with libraries from the Node.js ecosystem.

Requirements

Nbb requires Node.js v12 or newer.

How does this tool work?

CLJS code is evaluated through SCI, the same interpreter that powers babashka. Because SCI works with advanced compilation, the bundle size, especially when combined with other dependencies, is smaller than what you get with self-hosted CLJS. That makes startup faster. The trade-off is that execution is less performant and that only a subset of CLJS is available (e.g. no deftype, yet).

Usage

Install nbb from NPM:

$ npm install nbb -g

Omit -g for a local install.

Try out an expression:

$ nbb -e '(+ 1 2 3)'
6

And then install some other NPM libraries to use in the script. E.g.:

$ npm install csv-parse shelljs zx

Create a script which uses the NPM libraries:

(ns script
  (:require ["csv-parse/lib/sync$default" :as csv-parse]
            ["fs" :as fs]
            ["path" :as path]
            ["shelljs$default" :as sh]
            ["term-size$default" :as term-size]
            ["zx$default" :as zx]
            ["zx$fs" :as zxfs]
            [nbb.core :refer [*file*]]))

(prn (path/resolve "."))

(prn (term-size))

(println (count (str (fs/readFileSync *file*))))

(prn (sh/ls "."))

(prn (csv-parse "foo,bar"))

(prn (zxfs/existsSync *file*))

(zx/$ #js ["ls"])

Call the script:

$ nbb script.cljs
"/private/tmp/test-script"
#js {:columns 216, :rows 47}
510
#js ["node_modules" "package-lock.json" "package.json" "script.cljs"]
#js [#js ["foo" "bar"]]
true
$ ls
node_modules
package-lock.json
package.json
script.cljs

Macros

Nbb has first class support for macros: you can define them right inside your .cljs file, like you are used to from JVM Clojure. Consider the plet macro to make working with promises more palatable:

(defmacro plet
  [bindings & body]
  (let [binding-pairs (reverse (partition 2 bindings))
        body (cons 'do body)]
    (reduce (fn [body [sym expr]]
              (let [expr (list '.resolve 'js/Promise expr)]
                (list '.then expr (list 'clojure.core/fn (vector sym)
                                        body))))
            body
            binding-pairs)))

Using this macro we can look async code more like sync code. Consider this puppeteer example:

(-> (.launch puppeteer)
      (.then (fn [browser]
               (-> (.newPage browser)
                   (.then (fn [page]
                            (-> (.goto page "https://clojure.org")
                                (.then #(.screenshot page #js{:path "screenshot.png"}))
                                (.catch #(js/console.log %))
                                (.then #(.close browser)))))))))

Using plet this becomes:

(plet [browser (.launch puppeteer)
       page (.newPage browser)
       _ (.goto page "https://clojure.org")
       _ (-> (.screenshot page #js{:path "screenshot.png"})
             (.catch #(js/console.log %)))]
      (.close browser))

See the puppeteer example for the full code.

Since v0.0.36, nbb includes promesa which is a library to deal with promises. The above plet macro is similar to promesa.core/let.

Startup time

$ time nbb -e '(+ 1 2 3)'
6
nbb -e '(+ 1 2 3)'   0.17s  user 0.02s system 109% cpu 0.168 total

The baseline startup time for a script is about 170ms seconds on my laptop. When invoked via npx this adds another 300ms or so, so for faster startup, either use a globally installed nbb or use $(npm bin)/nbb script.cljs to bypass npx.

Dependencies

NPM dependencies

Nbb does not depend on any NPM dependencies. All NPM libraries loaded by a script are resolved relative to that script. When using the Reagent module, React is resolved in the same way as any other NPM library.

Classpath

To load .cljs files from local paths or dependencies, you can use the --classpath argument. The current dir is added to the classpath automatically. So if there is a file foo/bar.cljs relative to your current dir, then you can load it via (:require [foo.bar :as fb]). Note that nbb uses the same naming conventions for namespaces and directories as other Clojure tools: foo-bar in the namespace name becomes foo_bar in the directory name.

To load dependencies from the Clojure ecosystem, you can use the Clojure CLI or babashka to download them and produce a classpath:

$ classpath="$(clojure -A:nbb -Spath -Sdeps '{:aliases {:nbb {:replace-deps {com.github.seancorfield/honeysql {:git/tag "v2.0.0-rc5" :git/sha "01c3a55"}}}}}')"

and then feed it to the --classpath argument:

$ nbb --classpath "$classpath" -e "(require '[honey.sql :as sql]) (sql/format {:select :foo :from :bar :where [:= :baz 2]})"
["SELECT foo FROM bar WHERE baz = ?" 2]

Currently nbb only reads from directories, not jar files, so you are encouraged to use git libs. Support for .jar files will be added later.

Current file

The name of the file that is currently being executed is available via nbb.core/*file* or on the metadata of vars:

(ns foo
  (:require [nbb.core :refer [*file*]]))

(prn *file*) ;; "/private/tmp/foo.cljs"

(defn f [])
(prn (:file (meta #'f))) ;; "/private/tmp/foo.cljs"

Reagent

Nbb includes reagent.core which will be lazily loaded when required. You can use this together with ink to create a TUI application:

$ npm install ink

ink-demo.cljs:

(ns ink-demo
  (:require ["ink" :refer [render Text]]
            [reagent.core :as r]))

(defonce state (r/atom 0))

(doseq [n (range 1 11)]
  (js/setTimeout #(swap! state inc) (* n 500)))

(defn hello []
  [:> Text {:color "green"} "Hello, world! " @state])

(render (r/as-element [hello]))

Promesa

Working with callbacks and promises can become tedious. Since nbb v0.0.36 the promesa.core namespace is included with the let and do! macros. An example:

(ns prom
  (:require [promesa.core :as p]))

(defn sleep [ms]
  (js/Promise.
   (fn [resolve _]
     (js/setTimeout resolve ms))))

(defn do-stuff
  []
  (p/do!
   (println "Doing stuff which takes a while")
   (sleep 1000)
   1))

(p/let [a (do-stuff)
        b (inc a)
        c (do-stuff)
        d (+ b c)]
  (prn d))
$ nbb prom.cljs
Doing stuff which takes a while
Doing stuff which takes a while
3

Also see API docs.

Js-interop

Since nbb v0.0.75 applied-science/js-interop is available:

(ns example
  (:require [applied-science.js-interop :as j]))

(def o (j/lit {:a 1 :b 2 :c {:d 1}}))

(prn (j/select-keys o [:a :b])) ;; #js {:a 1, :b 2}
(prn (j/get-in o [:c :d])) ;; 1

Most of this library is supported in nbb, except the following:

  • destructuring using :syms
  • property access using .-x notation. In nbb, you must use keywords.

See the example of what is currently supported.

Examples

See the examples directory for small examples.

Also check out these projects built with nbb:

API

See API documentation.

Migrating to shadow-cljs

See this gist on how to convert an nbb script or project to shadow-cljs.

Build

Prequisites:

  • babashka >= 0.4.0
  • Clojure CLI >= 1.10.3.933
  • Node.js 16.5.0 (lower version may work, but this is the one I used to build)

To build:

  • Clone and cd into this repo
  • bb release

Run bb tasks for more project-related tasks.

Download Details:
Author: borkdude
Download Link: Download The Source Code
Official Website: https://github.com/borkdude/nbb 
License: EPL-1.0

#node #javascript

Eva  Murphy

Eva Murphy

1625674200

Google analytics Setup with Next JS, React JS using Router Events - 14

In this video, we are going to implement Google Analytics to our Next JS application. Tracking page views of an application is very important.

Google analytics will allow us to track analytics information.

Frontend: https://github.com/amitavroy/video-reviews
API: https://github.com/amitavdevzone/video-review-api
App link: https://video-reviews.vercel.app

You can find me on:
Twitter: https://twitter.com/amitavroy7​
Discord: https://discord.gg/Em4nuvQk

#next js #js #react js #react #next #google analytics

Eva  Murphy

Eva Murphy

1625751960

Laravel API and React Next JS frontend development - 28

In this video, I wanted to touch upon the functionality of adding Chapters inside a Course. The idea was to not think much and start the development and pick up things as they come.

There are places where I get stuck and trying to find answers to it up doing what every developer does - Google and get help. I hope this will help you understand the flow and also how developers debug while doing development.

App url: https://video-reviews.vercel.app
Github code links below:
Next JS App: https://github.com/amitavroy/video-reviews
Laravel API: https://github.com/amitavdevzone/video-review-api

You can find me on:
Twitter: https://twitter.com/amitavroy7​
Discord: https://discord.gg/Em4nuvQk

#next js #api #react next js #next #frontend #development

Lilyan  Streich

Lilyan Streich

1599119110

Next js Tutorial For Beginners

Next js Tutorial For Beginners is the today’s topic. It is no secret that creating single-page applications can be immensely challenging these days. But with the help of some libraries, frameworks, and tools, it is effortless nowadays. React.js is the common frontend libraries among the Front-end developers. Its virtual dom theory makes React faster and gives us the better application performance. Now, one problem is that Single Page Applications are not at all SEO  friendly because it is rendered on the Client side  and not Server side . So when the Search Engine crawlers try to send a request, they cannot get our meta content or description and not even the main content. Search Engines do not care about how your app is architected or whatever ideology was used to adjust and fetch the right material. Their bots are not as smart as using your apps as a real user would. All they care about is that once they send their spiders to crawl and index your site, whatever the server provides on the first request is what gets indexed. In our case, all they get is our div tag with an id and bundled JS file, and we can not index our website correctly. So some how, we need a SSR to tackle this problem and in React js, Next.js is the perfect solution.

#js #react.js #next.js

高橋  陽子

高橋 陽子

1657880470

如何在 Next.js 應用程序中保持 Lighthouse 高分

讓我們談談可用於測試網頁的最有用的技術 SEO 性能工具之一。它是自動化的,可以衡量性能、可訪問性和 SEO。更重要的是,它是開源的並且可以免費使用——並且可以用來測試漸進式 Web 應用程序,這是我想在本文中重點介紹的內容。讓我們一起瀏覽 Lighthouse 評分成功清單。但首先,一些信息: 

Web 應用程序的 SEO 性能成為過去幾年最熱門的話題之一。作為開發人員,我們經常忘記它,直到為時已晚。這通常是由於嚴格的期限、缺乏知識或只是懶惰造成的。 

今天,性能不僅僅是渲染應用程序所需的時間。糟糕的性能會導致糟糕的用戶體驗和更糟糕的 SEO 定位——不良結果的多米諾骨牌效應會損害你的應用程序。 

速度如何影響您的應用程序的性能?

您是否知道自 2010 年以來 Google 一直在關注網絡速度的排名?

  • 2010 年,Google 宣布將在桌面設備上的用戶搜索排名算法中考慮頁面速度。
  • 2017 年 12 月,谷歌轉向“移動優先”方法,然後在 2018 年 1 月,它開始使用頁面速度作為移動搜索的排名因素。
  • 2020 年 5 月,谷歌宣布他們將衡量整體頁面體驗,並將在其排名算法中使用它。頁面體驗基於許多信號,包括:
    • Core Web Vitals(加載性能、交互性的首次輸入延遲、視覺穩定性的累積佈局移位(CLS)、
    • 移動友好性, 
    • HTTPS,
    • 沒有侵入性的插頁式廣告。

您知道應用程序性能對用戶體驗甚至收入的巨大影響嗎?以下是谷歌研究“對移動速度的需求”的一些事實:

  • 如果加載時間超過 3 秒,53%的移動網站訪問者會離開網站
  • 在 5 秒內而不是 19 秒內加載的網站的可見度提高了 25%
  • 4 個頂級移動網站中有 3 個加載時間超過10 秒
  • 在 5 秒內而不是 19 秒內加載的網站的收入增加了 2 倍

但別擔心,我準備了一份清單,可以幫助您提高應用程序的整體速度,同時改善用戶體驗、搜索引擎優化和收入。因為 Lighthouse 是由 Google 製造的,所以它使用相同的性能指標,並有助於以清晰直觀的方式改進您的漸進式 Web 應用程序。

1. Lighthouse 中的 Web Vitals

讓我們開始提高您的 Lighthouse 性能得分。在我們繼續具體的提示和技巧之前,讓我們首先了解 Lighthouse 如何理解和計算性能分數。

Lighthouse的性能部分基於6 Web Vitals 測量頁面速度。

Web Vitals 是 Google 的一項舉措,旨在為質量信號提供統一指導,這些信號對於在網絡上提供出色的用戶體驗至關重要。

讓我們總結一下它們:

燈塔得分組件

  • FCP- First Contentful Paint-測量我們的應用程序在初次訪問期間需要渲染 DOM 中的第一個元素的時間。
  • SI – 速度指數– 衡量我們的應用程序填充內容的速度。Lighthouse 創建一個幻燈片,然後將幀相互比較。
  • LCP——最大內容繪製——測量在初始視口中渲染 DOM 中最大元素所需的時間。
  • TTI——交互時間——測量應用程序何時準備好與用戶交互。對於 Lighthouse,這意味著我們已經在初始視口中渲染了所有內容並註冊了事件。
  • TBT – Total Blocking Time – 衡量我們應用程序中“長任務”(耗時超過 50 毫秒的任務)的影響,是FCPTTI之間的區別。它是如何工作的?讓我們看一下這個例子:

我們的應用程序包含 20 個任務:

  • 10 個任務每個需要 40 毫秒
  • 10 個任務每個需要 60 毫秒

TBT只關心 60 毫秒的任務(更準確地說,是 50 毫秒閾值與值本身之間的差異)。所以我們的最終結果將是:

數量 x(價值 - 閾值)= 結果

10 x (60ms – 50ms) = 100ms

  • CLS – Cumulative Layout Shift – 初始視口中所有意想不到的佈局變化。值是根據“不穩定”元素在幀之間移動的距離計算的。

有了這些知識,我們終於可以繼續“修復”我們的應用程序 Lighthouse 分數了!

2. 修復字體以提高 Lighthouse 分數

為什麼字體會影響你的燈塔分數?這是因為它們的使用方式不僅會影響頁面速度(不同的字體有不同的大小,而且我們不僅僅指它們看起來有多大!)而且會對查看者如何查看您的頁面產生深遠影響正確加載。以下是一些需要注意的事項: 

  • 自託管– 避免從您無法控制的外部服務加載字體文件。只要有可能,您應該自行託管文件以避免更長的 HTTP 請求,或者使用帶有緩存的 CDN 託管。
  • 字體擴展——字體擴展對文件的最終大小有很大的影響。如果您選擇的字體帶有不同的擴展選項,則應始終選擇WOFF2,這是最輕的字體。
     

燈塔中的字體

字體子集——一些字體有更小的變體,稱為“子集”。它們包含更少的字形,這進一步減小了文件的大小。例如,某些字體具有僅包含拉丁字母和字符的“拉丁”子集。

燈塔中的字體大小

  • 可變字體- 可以將字體的多種變體合併到單個文件中,因此我們可以僅加載一個通常小於所有文件組合的文件,而不是加載具有不同變體的“X”數量的不同文件。 

假設我們想使用字體的所有變體(在這個例子中是 9 個文件)。我們將之前的結果乘以 9,並將其大小與單個可變字體文件進行比較。

可變字體燈塔

3. 腳本

腳本也會影響您的性能——尤其是當它們在不需要的地方出現瓶頸或占用寶貴的加載時間時。使他們更容易表現的最佳方法是: 

  • 異步加載– 引用:“沒有比您的應用程序代碼更重要的第三方代碼”。始終使用asyncdefer延遲加載第三方腳本,以防止阻塞應用程序的主線程。您還可以使用next/script來設置腳本的優先級。
  • 資源提示——明智地使用資源提示來進一步減少加載腳本所需的時間。您可以在此處了解有關它們的更多信息

標記管理器——考慮將第三方腳本的加載委託給標記管理器,您可以更好地控制腳本加載的順序和腳本的數量。

4.風格 

  • CSS over CSS-in-JS 解決方案——在樣式方面,您可能需要考慮一種更“老式”的做事方式。因為在 SSR 應用程序中,我們不想用更多的 JavaScript 壓倒主線程。這就是為什麼CSS-in-JS解決方案不是最適合 Next.js 應用程序的原因。 

您可以查看比較這兩種方法的這篇文章。此外,`Next.js` 已經內置了大量的 CSS 優化,比如類名和样式縮小、sass 支持、配置的 postcss,所以它是我們的一種方式。

字體顯示——為了避免 FOUT(無樣式文本的閃爍)或看到空白屏幕,您應該始終通過使用字體上的font-display屬性來控製字體的加載。

5. 捆綁

分析捆綁包可以很好地發現我們的塊的數量和大小。我們通常可以從這樣做的工具中學到很多東西。

  • Bundle-wizard – 這是@next/bundle-analyzer的一個很酷的替代品,它允許我們檢查我們的應用程序包。在我看來,它比其他工具有 3 大優勢。
  •  
    • 它有一個更好的用戶界面來導航
    • 它提供了塊的覆蓋範圍
    • 它不僅可以在構建期間在任何已部署的應用程序上運行
  • 塊拆分——減少捆綁包大小的一個好方法是將它們拆分成更小的部分。我們的應用程序更容易加載多個較小的塊而不是幾個大塊。幸運的是,webpack 確實允許我們拆分合併的塊。我們可以在next.config中這樣做:
module.exports = {
 webpack(config, { isServer }) {
   if (!isServer) {
     config.optimization.splitChunks.cacheGroups = {
       ...config.optimization.splitChunks.cacheGroups,
       '@sentry': {
         test: /[\\/]node_modules[\\/](@sentry)[\\/]/,
         name: '@sentry',
         priority: 10,
         reuseExistingChunk: false,
       },
     };
   }
 
   return config;
 },
};

這樣,@sentry包將被分割成它自己的小塊。此外,我們可以控制模塊的優先級。

刪除重複的模塊——有時在 monorepo 架構中工作時,我們可能會得到多次捆綁的包。同樣,webpack config 帶有一個可以合併我們重複的塊的屬性。它看起來像這樣:

module.exports = {
 optimization: {
   mergeDuplicateChunks: true,
 },
};

6.CLS

CLS 成為 2021 年的排名因素,所以它是相當新的。它代表 Cumulative Layout Shift,是衡量用戶體驗溫度的指標。

每當可見元素將其位置從一個渲染幀更改為下一幀時,就會發生佈局轉換。 

為動態內容保留空間——為了防止任何意外的佈局變化,我們應該始終為尚未渲染的內容保留空間。

有很多很棒的方法,比如骨架加載,它模仿給定組件的一般外觀,包括它的寬度和高度。這樣,我們將保留確切的空間,從而消除 CLS。

但有時,我們不必使用任何花哨的東西。我們可以只插入一個空的佔位符框,這將確保用戶沒有不愉快的變化。 

7. 圖片

圖像可能是最臭名昭著的頁面速度惡棍,這個問題現在和 1999 年一樣真實。除了我們有更多的技巧來解決這個問題並提高你的整體性能得分。開始了: 

  • 新一代文件擴展名——考慮以webpjpeg2000文件擴展名提供圖像。它比傳統的jpegpng輕得多,而且沒有明顯的質量損失。有許多庫可以在上傳過程中將圖像轉換為webp,因此請隨意使用它們。但請始終記住,某些較舊的瀏覽器可能不支持該擴展,因此請準備適用格式的後備版本。 
  • 尺寸變體——Lighthouse 確實建議為每個斷點提供不同變體的圖像。像Sharp這樣的庫允許我們生成同一張圖像的多種尺寸。要顯示它們,我們可以使用<picture>  標籤或img srcSet屬性,所有的“魔法”都將由瀏覽器為我們處理。
  • 延遲加載——總是延遲加載視口之外的圖像。這樣,我們可以在第一次訪問我們的頁面時節省時間。為此,我們可以在img標籤上使用loading=”lazy”屬性。
  • 預加載——考慮預加載首屏的圖像——尤其是 LCP 元素。預加載鏈接“告訴”瀏覽器比正常情況更早地獲取內容。 

Next/image –手動完成上述所有點可能既耗時又存在問題。幸運的是,有一些圖書館可以為我們處理它。其中之一是Next/Image組件,它將通過轉換為 webp、調整大小、延遲加載和預加載 API 為我們優化圖像。
 

8. JavaScript

有時,在 SEO 性能方面,JavaScript 可能會成為反派。為了提高應用程序的燈塔分數,我們可以避免一些常見錯誤: 

  • 代碼拆分(動態導入) ——代碼拆分允許我們延遲加載一些代碼,因此它減少了我們應用程序的主線程必須做的工作量。Next/dynamic是一個很好的代碼拆分工具。使用簡單的 API,我們可以將組件拆分為單獨的塊,這些塊將按需加載。我們還可以控制組件是否應該在服務器端呈現。
  • 避免桶文件——我們傾向於創建具有多個導出的桶文件。它們很方便導入,但將來可能會成為我們的痛點。如果搖樹過程不起作用,那麼每個單獨的導入都將導入整個“庫”。所以而不是:
// Exporting from barrel file
export Button from "ui/Button";
export Text from "ui/Text";
export Select from "ui/Select";
 
// Importing in component
import { Button, Select, Text } from "ui";

我們想直接從組件路徑導入:

// Importing in component
import Button from "ui/Button";
import Text from "ui/Text";
import Select from "ui/Select";

9. 如何保持高性能

如果我們已經達到了讓我們滿意的性能水平,那麼隨著時間的推移將其保持在同一水平會很好。我們還不能稱之為星期五!有一些工具可以幫助我們做到這一點:

  • Bundle-wizard –在我們的應用程序增長時不時運行此工具是一個很好的做法,以確保包大小保持較小,並且我們不會遇到任何與塊有關的意外問題。
  • Webpack 性能提示——來自 webpack的性能提示是我們運行bundle-wizard的一個很好的指標。它們真的很容易設置和配置,當任何應用程序塊超過大小限制時,讓我們有機會在構建期間拋出警告或錯誤。
  • PageSpeed Insights / Lighthouse –當然,我們衡量應用程序性能的主要工具是 Lighthouse。我們可以通過 Chrome 瀏覽器中的開發工具運行它,也可以通過 PSI 網站運行它。
  • WebPageTest –是 Lighthouse 的一個很酷的替代品。它也為我們測量了 Web Vitals,但以更易於訪問的方式為我們提供了更多信息。例如,我們可以看到任務的瀑布或渲染過程的幻燈片。
  • Lighthouse-CI –使用Lighthouse-CI,我們可以為特定指標或 CI 流程的整體性能得分設置測試。這樣,我們可以隨著應用程序的增長持續測量頁面速度。
  • 衡量真實用戶的表現——這是對已經投入生產並擁有一些活躍用戶的應用程序的提示。衡量真實用戶的表現是了解我們的弱點在哪裡的關鍵。 

Lighthouse 或 WebPageTest 之類的工具有時會產生誤導,因為它們總是在穩定的互聯網連接、最新版本的 Chrome 等下工作……而對於我們的最終用戶而言,情況並非總是如此。通常,用戶在給定頁面上的表現可能比 Lighthouse 建議的要差得多。 

個人推薦Sentry 的性能測量工具。它非常容易設置,並且會提供大量關於用戶設備、連接、位置等的信息,這可能有助於我們解決邊緣情況問題。

Web 應用程序性能連續性 

Web App 的性能不是我們可以修復一次就忘記的。它更像是一個隨著應用程序的增長而不斷檢查、分析和改進應用程序的過程。幸運的是,我們可以而且應該盡可能地自動化這個過程,讓我們的生活更輕鬆。 

正確設置的工作流程可以防止我們推送會破壞我們的應用程序性能的代碼,在實施過程中發現錯誤,甚至指出我們應該關注的痛點。

鏈接:https ://tsh.io/blog/how-to-keep-your-lighthouse-score-high-in-next-js-applications-a-checklist/

#nextjs #javascript #lighthouse