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

Houssein Djirdeh
Houssein Djirdeh

Bu codelab'de, kullanıcıların rastgele kedileri derecelendirmesine olanak tanıyan bu basit uygulamanın performansını artırın. Ne kadar kodun aktarıldığını belirleyerek JavaScript paketinin nasıl optimize edileceğini öğrenin.

Uygulama ekran görüntüsü

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

Ölçüm

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

  1. Siteyi önizlemek için Uygulamayı Görüntüle'ye, 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ı öğrenme zamanı:

  1. Command 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 Yeniden yükle'yi tıklayarak kapsamı yakalama sırasında uygulamayı yeniden yükleyin.

    Uygulamayı kod kapsamıyla yeniden yükleyin

  4. Ana paket için kullanılan koda kıyasla ne kadar kod yüklendiğine 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ı kullanma

JavaScript dilinin söz dizimi, ECMAScript veya ECMA-262 olarak bilinen bir standarda uygundur. Spesifikasyonun daha yeni sürümleri her yıl yayınlanır 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:

Tüm bunların nasıl kullanıldığını görmek için src/index.js dilindeki kaynak kodu 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? Uygulamaya dahil olan Babel, yeni söz dizimini eski tarayıcı ve ortamların anlayabileceği koda içeren kodu derlemek için kullanılan en popüler kitaplıktır. Bunu iki şekilde yapar:

  • Çoklu dolgular, yeni ES2015+ işlevlerine öykünmek üzere eklenmiştir. Böylece, tarayıcı tarafından desteklenmese bile API'lerinin kullanılabilmesi için. Aşağıda, Array.includes yönteminin bir polyfill örneği verilmiştir.
  • Eklentiler, ES2015 kodunu (veya üzerini) 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 olduğunu 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, web paketi derleme işlemine Babel'i dahil eder.

Şimdi babel-loader öğesinin kural olarak nasıl dahil edildiğini webpack.config.js adresinde görebilirsiniz:

module: {
  rules: [
    //...
    {
      test: /\.js$/,
      exclude: /node_modules/,
      loader: "babel-loader"
    }
  ]
},
  • @babel/polyfill, yeni ECMAScript özellikleri için gerekli tüm çoklu dolguları sağlar. Böylece bu özellikler, bunları desteklemeyen ortamlarda çalışabilir. src/index.js. sayfasının en tepesinde 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ırmaları 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 paketleyicisi kullanıyorsanız Babel'ı uygulamanıza nasıl ekleyeceğinizi öğrenin.

.babelrc öğesindeki targets özelliği, hangi tarayıcıların hedeflendiğini tanımlar. @babel/preset-env, tarayıcı listesi ile entegre olur. Bu sayede, 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 Aksak durum günlüklerine göz atın.

Hedeflenen tarayıcılar

Babel, kodun derlendiği tüm hedef ortamlar 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 sorun teşkil eder, çünkü desteklenmeyen tarayıcılara yeni özellikler eklenmeyecektir ve Babel bunlar için belirli söz dizimini aktarmaya devam etmektedir. Kullanıcılar sitenize erişmek için bu tarayıcıyı kullanmıyorsa bu durum, paketinizin boyutunu gereksiz şekilde artırır.

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

Kullanılan eklentilerin listesi

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

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

Polyfill eklenmedi

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

Çoklu dolguları tek tek yükleme

@babel/polyfill bir dosyaya aktarıldığında, 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 useBuiltIns: 'entry' ekleyin.

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

Uygulamayı yeniden yükleyin. Artık içerikteki tüm çoklu dolguları görebilirsiniz:

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

"last 2 versions" için artık yalnızca gerekli çoklu dolgular dahil edilmiş olsa da liste hâlâ çok uzun. Bunun nedeni, yeni her özellikte hedef tarayıcılarda 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"
      }
    ]
  ]
}

Bununla birlikte, gerektiğinde çoklu dolgular otomatik olarak dahil edilir. Bu, src/index.js. hizmetinden @babel/polyfill içe aktarma işlemini kaldırabileceğiniz anlamına gelir

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

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

Çoklu dolguların listesi otomatik olarak dahil edilir

Uygulama paketi boyutu önemli ölçüde küçültüldü.

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â oldukça fazladır ve birç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.

Paket boyutu 30,0 KB

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

<script type="module"> kodunu kullanın

Daha fazla iyileştirme yapılabilir. Kullanılmayan bazı çoklu dolgular kaldırılmış olsa da, bazı tarayıcılarda ihtiyaç duyulmayan birçok çoklu dolgu mevcuttur. Modüller kullanıldığında, gereksiz çoklu dolgular kullanılmadan doğrudan daha yeni söz dizimi yazılabilir ve tarayıcılara gönderilebilir.

JavaScript modülleri, tüm popüler tarayıcılarda desteklenen yeni bir özelliktir. Modüller, diğer modüllerden içe ve dışa aktarılan komut dosyalarını tanımlamak için type="module" özelliği kullanılarak 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 zaten desteklenmektedir (Babel'a gerek yoktur). Bu, Babel yapılandırması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 dosya boyutu daha küçük olan bir modül içeren sürüm
  • Eski tarayıcılarda çalışacak daha büyük, aktarılmış bir komut dosyası içeren sürüm

ES Modüllerini Babel ile Kullanma

Uygulamanın iki sürümü için ayrı @babel/preset-env ayarlarına sahip olmak istiyorsanız .babelrc dosyasını kaldırın. Babel ayarları, uygulamanın her sürümü için iki farklı derleme biçimi belirterek 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üşümleri ve çoklu dolguları içerir.

webpack.config.js dosyasının başına entry, cssRule ve corePlugins nesneleri ekleyin. Bunların tümü, tarayıcıya sunulan modül ve 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 de 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. Burada esmodules değeri true olarak ayarlanır. Bu, kullanılan tüm özellikler modülleri destekleyen tarayıcılarda zaten desteklendiğinden, bu modülde verilen kodun daha küçük ve daha az derlenmiş bir komut dosyası olduğu anlamına gelir.

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

module.exports = [
  legacyConfig, moduleConfig
];

Artık bu yöntem, hem destekleyici tarayıcılar için daha küçük bir modül hem de eski tarayıcılar için daha büyük bir aktarılmış komut dosyası oluşturmaktadır.

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, bir modülün yanı sıra derlenmiş bir yedek de ekleyebileceğiniz anlamına gelir. İdeal olarak, uygulamanın iki sürümü index.html biçiminde olmalıdır:

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

Modülleri destekleyen ve main.mjs getiren ve çalıştıran tarayıcılar main.bundle.js. Modülleri desteklemeyen tarayıcılar tam tersini yapar.

Normal komut dosyalarından farklı olarak 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, sırasıyla modüle ve eski komut dosyasına module ve nomodule özelliklerini eklemektir. webpack.config.js öğesinin en üstündeki 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ülleri 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, şu anda hem modül hem de modül olmayan 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ğiticinin amacı doğrultusunda modül komut dosyası öğesini manuel olarak eklemeye yönelik daha basit bir yaklaşım kullanılmıştır.

Dosyanın sonuna src/index.js öğesini 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 ve 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. Aşağıda, Chrome'un eski bir sürümünde (sürüm 38) yapılan tüm isteklerin ekran görüntüsünü görebilirsiniz.

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

Sonuç

Artık yalnızca hedeflenen tarayıcılar için gereken gerekli çoklu dolguları sağlamak üzere @babel/preset-env özelliğini nasıl kullanacağınızı biliyorsunuz. Ayrıca JavaScript modüllerinin, bir uygulamanın aktarılmış iki farklı sürümünü göndererek performansı nasıl daha da iyileştirebileceğini de biliyorsunuz. Bu tekniklerin her ikisinin de yayın boyutunu nasıl önemli ölçüde küçültebileceğini iyice anladıktan sonra devam edin ve optimizasyon yapın.