Sayfaların daha hızlı yüklenmesi için modern tarayıcılara modern kod sunma

Bu codelab'de, kullanıcıların rastgele kedileri derecelendirmesine olanak tanıyan bu basit uygulamanın performansını iyileştirin. Ne kadar kodun aktarıldığını inceleyerek JavaScript paketini nasıl optimize edeceğinizi öğrenin.

Uygulama ekran görüntüsü

Örnek uygulamada, her bir kediyi ne kadar sevdiğinizi belirtmek için bir kelime veya emoji seçebilirsiniz. Bir düğmeyi tıkladığınızda, uygulama o anki kedi resminin altında düğmenin değerini gösterir.

Ölçüm

Optimizasyon eklemeden önce bir web sitesini incelemekle başlamak her zaman iyi bir fikirdir:

  1. Siteyi önizlemek için Uygulamayı Göster'e, ardından Tam Ekran'a tam ekran basın.
  2. Geliştirici Araçları'nı açmak için "Control+Shift+J" (veya Mac'te "Command+Option+J") tuşlarına basın.
  3. sekmesini tıklayın.
  4. Önbelleği devre dışı bırak onay kutusunu seçin.
  5. Uygulamayı yeniden yükleyin.

Orijinal paket boyutu isteği

Bu uygulama için 80 KB'tan fazla kullanılıyor! Paketin bazı bölümlerinin kullanılıp kullanılmadığını öğrenmek için süre:

  1. Komut menüsünü açmak için Control+Shift+P (veya Mac'te Command+Shift+P) tuşuna basın. Komut Menüsü

  2. Kapsam sekmesini görüntülemek için Show Coverage girin ve Enter tuşuna basın.

  3. Kapsam sekmesinde, kapsamı yakalarken uygulamayı yeniden yüklemek için Yeniden Yükle'yi tıklayın.

    Uygulamayı kod kapsamıyla yeniden yükle

  4. Ana paket için ne kadar kod kullanıldığına ve ne kadarının yüklendiğine bir göz atın:

    Paketin kod kapsamı

Paketin yarısından fazlası (44 KB) bile kullanılmıyor. Bunun nedeni, uygulamanın eski tarayıcılarda çalışmasını sağlamak için içindeki kodun büyük bölümünün çoklu dolgulardan oluşmasıdır.

@babel/preset-env hesabını kullan

JavaScript dilinin söz dizimi, ECMAScript veya ECMA-262 olarak bilinen bir standarda uygundur. Spesifikasyonun daha yeni sürümleri her yıl kullanıma sunulur ve teklif sürecini geçen yeni özellikler içerir. Başlıca tarayıcıların her biri, bu özellikleri desteklemede her zaman farklı bir aşamadadır.

Uygulamada aşağıdaki ES2015 özellikleri kullanılır:

Aşağıdaki ES2017 özelliği de kullanılır:

Bunların nasıl kullanıldığını görmek için src/index.js kaynak kodunu ayrıntılı olarak inceleyebilirsiniz.

Bu özelliklerin tümü Chrome'un son sürümünde destekleniyor, Peki bunları desteklemeyen diğer tarayıcılar ne olacak? Uygulamanın içerdiği Babel, eski tarayıcıların ve ortamların anlayabileceği yeni söz dizimi içeren kodları derlemek için kullanılan en popüler kitaplıktır. Bunu iki şekilde yapar:

  • Çoklu dolgular, yeni ES2015+ işlevlerinin emülasyonunu sağlayarak tarayıcı tarafından desteklenmese bile bunların API'lerinin kullanılabilmesini sağlar. Array.includes yönteminin bir polyfill örneğini burada görebilirsiniz.
  • Eklentiler, ES2015 kodunu (veya sonraki sürümleri) eski ES5 söz dizimine dönüştürmek için kullanılır. Bunlar söz dizimiyle ilgili değişiklikler (ok işlevleri gibi) olduğundan, çoklu dolgularla emüle edilemez.

Hangi Babel kitaplıklarının dahil edildiğini görmek için package.json sayfasına bakın:

"dependencies": {
  "@babel/polyfill": "^7.0.0"
},
"devDependencies": {
  //...
  "babel-loader": "^8.0.2",
  "@babel/core": "^7.1.0",
  "@babel/preset-env": "^7.1.0",
  //...
}
  • @babel/core, temel Babel derleyicidir. Bununla, tüm Babel yapılandırmaları projenin kökündeki bir .babelrc içinde tanımlanır.
  • babel-loader, Babel'i web paketi derleme işlemine ekler.

Şimdi babel-loader öğesinin nasıl kural olarak eklendiğini görmek için webpack.config.js bölümüne göz atın:

module: {
  rules: [
    //...
    {
      test: /\.js$/,
      exclude: /node_modules/,
      loader: "babel-loader"
    }
  ]
},
  • @babel/polyfill, daha yeni ECMAScript özellikleri için gerekli tüm çoklu dolguları sağlar. Böylece, bu özellikleri desteklemeyen ortamlarda çalışabilirler. src/index.js. sayfasının en üstünde zaten içe aktarıldı
import "./style.css";
import "@babel/polyfill";
  • @babel/preset-env, hedef olarak seçilen tarayıcılar veya ortamlar için hangi dönüşümlerin ve çoklu dolguların gerekli olduğunu belirler.

Nasıl eklendiğini görmek için Babel yapılandırma dosyasına (.babelrc) göz atın:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions"
      }
    ]
  ]
}

Bu bir Babel ve web paketi kurulumudur. Webpack'ten farklı bir modül paketleyici kullanıyorsanız uygulamanıza Babel'i nasıl ekleyeceğinizi öğrenin.

.babelrc öğesindeki targets özelliği, hangi tarayıcıların hedeflendiğini tanımlar. @babel/preset-env, browserlist ile entegre edilir. Yani bu alanda kullanılabilecek uyumlu sorguların tam listesini tarayıcı listesi dokümanlarında bulabilirsiniz.

"last 2 versions" değeri, her tarayıcının son iki sürümü için uygulamadaki kodu aktarır.

Hata ayıklama

Tarayıcının tüm Babel hedeflerinin yanı sıra dahil olan tüm dönüşümleri ve çoklu dolguları tam olarak görmek için .babelrc: öğesine bir debug alanı ekleyin

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "debug": true
      }
    ]
  ]
}
  • Araçlar'ı tıklayın.
  • Logs'u (Günlükler) tıklayın.

Uygulamayı yeniden yükleyin ve düzenleyicinin alt kısmındaki Sorunlu durum günlüklerine göz atın.

Hedeflenen tarayıcılar

Babel, kodun derlendiği tüm hedef ortamlar da dahil olmak üzere, derleme işlemiyle ilgili bazı ayrıntıları konsola kaydeder.

Hedeflenen tarayıcılar

Internet Explorer gibi kullanımdan kaldırılmış tarayıcıların bu listeye nasıl dahil edildiğine dikkat edin. Bu bir sorundur, çünkü desteklenmeyen tarayıcılara daha yeni özellikler eklenmez ve Babel bunlar için belirli söz dizimini aktarmaya devam eder. Kullanıcılar sitenize erişmek için bu tarayıcıyı kullanmıyorsa bu durum, paketinizin boyutunu gereksiz yere artırır.

Babel, kullanılan dönüşüm eklentilerinin bir listesini de günlüğe kaydeder:

Kullanılan eklentilerin listesi

Bu oldukça uzun bir liste. Bunlar, hedeflenen tüm tarayıcılarda ES2015+ söz dizimini eski söz dizimine dönüştürmek için Babel'in kullanması gereken tüm eklentilerdir.

Ancak Babel, kullanılan belirli çoklu dolguları göstermez:

Çoklu dolgu eklenmedi

Bunun nedeni, @babel/polyfill öğesinin tamamının doğrudan içe aktarılmasıdır.

Çoklu dolguları tek tek yükleme

@babel/polyfill bir dosyaya içe aktarılırken Babel varsayılan olarak eksiksiz bir ES2015+ ortamı için gereken her çoklu dolguyu içerir. Hedef tarayıcılar için gereken belirli çoklu dolguları içe aktarmak üzere yapılandırmaya bir useBuiltIns: 'entry' ekleyin.

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "debug": true
        "useBuiltIns": "entry"
      }
    ]
  ]
}

Uygulamayı yeniden yükleyin. Artık dahil olan belirli çoklu dolguların tümünü görebilirsiniz:

İçe aktarılan çoklu dolguların listesi

"last 2 versions" için yalnızca gerekli çoklu dolgular artık dahil edilmiş olsa da bu liste çok uzun. Bunun nedeni, her yeni özellikte hedef tarayıcılar için gereken çoklu dolguların yine de dahil edilmesidir. Yalnızca kodda kullanılmakta olan özellikler için gerekenleri eklemek üzere özelliğin değerini usage olarak değiştirin.

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "debug": true,
        "useBuiltIns": "entry"
        "useBuiltIns": "usage"
      }
    ]
  ]
}

Bu sayede çoklu dolgular gerektiğinde otomatik olarak dahil edilir. Bu durumda, src/index.js. ürününde @babel/polyfill içe aktarma işlemini kaldırabilirsiniz.

import "./style.css";
import "@babel/polyfill";

Artık yalnızca uygulama için gereken çoklu dolgular dahil edildi.

Çoklu dolguların listesi otomatik olarak eklenir

Uygulama paketi boyutu önemli ölçüde küçültülmüştür.

Paket boyutu 30,1 KB'a düşürüldü

Desteklenen tarayıcıların listesini daraltma

Dahil edilen tarayıcı hedeflerinin sayısı hâlâ çok fazla ve pek çok kullanıcı Internet Explorer gibi kullanımdan kaldırılmış tarayıcıları kullanmamaktadır. Yapılandırmaları aşağıdaki şekilde güncelleyin:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "targets": [">0.25%", "not ie 11"],
        "debug": true,
        "useBuiltIns": "usage",
      }
    ]
  ]
}

Getirilen paketin ayrıntılarına göz atın.

30,0 KB paket boyutu

Uygulama çok küçük olduğundan, bu değişikliklerle arasında pek bir fark yok. Bununla birlikte, önerilen yaklaşım, tarayıcı pazar payı yüzdesi (">0.25%" gibi) ve kullanıcılarınızın kullanmadığından emin olduğunuz belirli tarayıcıları hariç tutmanızı sağlar. Bu konu hakkında daha fazla bilgi edinmek için James Kyle'ın zararlı olarak kabul ettiği "Son 2 sürüm" makalesine göz atın.

<script type="module"> kullanın

Daha fazla iyileştirme yapılabilir. Kullanılmayan bazı çoklu dolgular kaldırılmış olsa da, bazı tarayıcılar için gerekli olmayan birçok çoklu dolgu gönderilmeye devam etmektedir. Modüller kullanıldığında, yeni söz dizimi gereksiz çoklu dolgular kullanılmadan doğrudan tarayıcılara yazılabilir ve gönderilebilir.

JavaScript modülleri, tüm yaygın tarayıcılarda desteklenen yeni bir özelliktir. Diğer modüllerden içe ve dışa aktarma yapan komut dosyalarını tanımlamak için type="module" özelliği kullanılarak modüller oluşturulabilir. Örneğin:

// math.mjs
export const add = (x, y) => x + y;

<!-- index.html -->
<script type="module">
  import { add } from './math.mjs';

  add(5, 2); // 7
</script>

Birçok yeni ECMAScript özelliği, JavaScript modüllerini destekleyen ortamlarda (Babel'e ihtiyaç duyulmak yerine) zaten desteklenmektedir. Bu, Babel yapılandırmasının, uygulamanızın iki farklı sürümünü tarayıcıya gönderecek şekilde değiştirilebileceği anlamına gelir:

  • Modülleri destekleyen yeni tarayıcılarda çalışacak ve büyük ölçüde aktarılmamış ancak daha küçük dosya boyutuna sahip bir modül içeren bir sürüm
  • Tüm eski tarayıcılarda çalışacak daha büyük, aktarılmış bir komut dosyası içeren sürüm

Babel ile ES Modüllerini Kullanma

Uygulamanın iki sürümünde ayrı @babel/preset-env ayarlarına sahip olmak için .babelrc dosyasını kaldırın. Babel ayarları, uygulamanın her sürümü için iki farklı derleme biçimi belirtilerek web paketi yapılandırmasına eklenebilir.

webpack.config.js alanına eski komut dosyasının bir yapılandırmasını ekleyerek başlayın:

const legacyConfig = {
  entry,
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "[name].bundle.js"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
          presets: [
            ["@babel/preset-env", {
              useBuiltIns: "usage",
              targets: {
                esmodules: false
              }
            }]
          ]
        }
      },
      cssRule
    ]
  },
  plugins
}

"@babel/preset-env" için targets değeri yerine false değerine sahip esmodules kullanıldığına dikkat edin. Diğer bir deyişle Babel, henüz ES modüllerini desteklemeyen tüm tarayıcıları hedeflemek için gerekli tüm dönüştürmeleri ve çoklu dolguları içerir.

webpack.config.js dosyasının başına entry, cssRule ve corePlugins nesnelerini ekleyin. Bunların tümü hem modül hem de tarayıcıya sunulan eski komut dosyaları arasında paylaşılır.

const entry = {
  main: "./src"
};

const cssRule = {
  test: /\.css$/,
  use: ExtractTextPlugin.extract({
    fallback: "style-loader",
    use: "css-loader"
  })
};

const plugins = [
  new ExtractTextPlugin({filename: "[name].css", allChunks: true}),
  new HtmlWebpackPlugin({template: "./src/index.html"})
];

Şimdi benzer şekilde, aşağıdaki modül komut dosyası için legacyConfig öğesinin tanımlandığı yerde bir yapılandırma nesnesi oluşturun:

const moduleConfig = {
  entry,
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "[name].mjs"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
          presets: [
            ["@babel/preset-env", {
              useBuiltIns: "usage",
              targets: {
                esmodules: true
              }
            }]
          ]
        }
      },
      cssRule
    ]
  },
  plugins
}

Buradaki temel fark, çıkış dosya adı için .mjs dosya uzantısının kullanılmasıdır. esmodules değeri true olarak ayarlanmıştır. Bu modül, kullanılan tüm özellikler modülleri destekleyen tarayıcılarda zaten desteklendiğinden bu örnekte herhangi bir dönüştürme işlemi yapmayan, daha küçük ve daha az derlenmiş bir komut dosyası olduğu anlamına gelir.

Dosyanın en sonunda, her iki yapılandırmayı tek bir dizide dışa aktarın.

module.exports = [
  legacyConfig, moduleConfig
];

Bu şekilde artık hem destekleyen tarayıcılar için daha küçük bir modül hem de eski tarayıcılar için daha büyük bir komut dosyası oluşturulur.

Modülleri destekleyen tarayıcılar, nomodule özelliğine sahip komut dosyalarını yoksayar. Buna karşılık, modülleri desteklemeyen tarayıcılar type="module" içeren komut dosyası öğelerini yoksayar. Bu, derlenmiş bir yedeğin yanı sıra bir modülü de dahil edebileceğiniz anlamına gelir. İdeal olarak, uygulamanın iki sürümü şunun gibi index.html olmalıdır:

<script type="module" src="main.mjs"></script>
<script nomodule src="main.bundle.js"></script>

Modüllerin getirilmesini ve çalıştırılmasını destekleyen tarayıcılar main.mjs işlemini destekler ve yok sayar main.bundle.js. Modülleri desteklemeyen tarayıcılar tam tersini yapar.

Normal komut dosyalarının aksine, modül komut dosyalarının her zaman varsayılan olarak ertelendiği unutulmamalıdır. Eşdeğer nomodule komut dosyasının da ertelenmesini ve yalnızca ayrıştırmadan sonra yürütülmesini istiyorsanız defer özelliğini eklemeniz gerekir:

<script type="module" src="main.mjs"></script>
<script nomodule src="main.bundle.js" defer></script>

Burada yapılması gereken son şey, modüle ve eski komut dosyasına sırasıyla module ve nomodule özelliklerini eklemektir. webpack.config.js öğesinin en üstüne ScriptExtHtmlWebpackPlugin öğesini içe aktarın:

const path = require("path");

const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");

Şimdi yapılandırmalardaki plugins dizisini bu eklentiyi içerecek şekilde güncelleyin:

const plugins = [
  new ExtractTextPlugin({filename: "[name].css", allChunks: true}),
  new HtmlWebpackPlugin({template: "./src/index.html"}),
  new ScriptExtHtmlWebpackPlugin({
    module: /\.mjs$/,
    custom: [
      {
        test: /\.js$/,
        attribute: 'nomodule',
        value: ''
    },
    ]
  })
];

Bu eklenti ayarları, tüm .mjs komut dosyası öğeleri için bir type="module" özelliğinin yanı sıra tüm .js komut dosyası modülleri için bir nomodule özelliği ekler.

HTML dokümanında modül sunma

Yapılması gereken son şey, hem eski hem de modern komut dosyası öğelerinin çıktısını HTML dosyasına eklemektir. Maalesef son HTML dosyasını oluşturan HTMLWebpackPlugin eklentisi şu anda hem modülün hem de modül bulunmayan komut dosyalarının çıkışını desteklememektedir. Bu sorunu çözmek için BabelMultiTargetPlugin ve HTMLWebpackMultiBuildPlugin gibi geçici çözümler ve ayrı eklentiler oluşturulmuş olsa da, bu eğiticide modül komut dosyası öğesini manuel olarak eklemeye yönelik daha basit bir yaklaşım kullanılmıştır.

Dosyanın sonuna şu satırı src/index.js ekleyin:

    ...
    </form>
    <script type="module" src="main.mjs"></script>
  </body>
</html>

Şimdi uygulamayı, Chrome'un en son sürümü gibi modülleri destekleyen bir tarayıcıda yükleyin.

Yeni tarayıcılar için 5,2 KB modülü ağ üzerinden getirildi

Yalnızca modül getirilir. Modül, büyük ölçüde aktarılmadığı için paket boyutu çok daha küçük olur. Diğer komut dosyası öğesi tarayıcı tarafından tamamen yok sayılır.

Uygulamayı eski bir tarayıcıya yüklerseniz yalnızca gerekli tüm çoklu dolguları ve dönüşümleri içeren, daha büyük olan, aktarılmış komut dosyası getirilir. Chrome'un eski bir sürümünde (sürüm 38) yapılan tüm isteklerin ekran görüntüsünü burada bulabilirsiniz.

Eski tarayıcılar için 30 KB&#39;lık komut dosyası getirildi

Sonuç

Artık hedeflenen tarayıcılar için yalnızca gerekli çoklu dolguları sağlamak amacıyla @babel/preset-env özelliğini nasıl kullanacağınızı biliyorsunuz. Ayrıca JavaScript modüllerinin, bir uygulamanın çevrilen iki farklı sürümünü göndererek performansı nasıl daha da iyileştirebileceğini de biliyorsunuz. Bu iki tekniğin de paket boyutunu nasıl önemli ölçüde küçültebileceğini anladığınızda, devam edip optimizasyon yapabilirsiniz.