Daha hızlı uygulamalar için modern JavaScript'i yayınlayın, gönderin ve yükleyin

Modern JavaScript bağımlılıklarını ve çıkışı etkinleştirerek performansı iyileştirin.

Tarayıcıların% 90'ından fazlası modern JavaScript'i çalıştırabilir. Ancak eski JavaScript'in yaygın olması, günümüzde web'deki performans sorunlarının büyük bir kaynağı olmaya devam etmektedir.

Modern JavaScript

Modern JavaScript, belirli bir ECMAScript spesifikasyon sürümünde yazılan kod olarak değil, tüm modern tarayıcılar tarafından desteklenen söz dizimiyle nitelendirilir. Chrome, Edge, Firefox ve Safari gibi modern web tarayıcıları, tarayıcı pazarının% 90'ından fazlasını, temel oluşturma motorlarını kullanan farklı tarayıcılar ise %5'lik bir ek oran oluşturmaktadır. Bu da küresel web trafiğinin% 95'inin, son 10 yılda en çok kullanılan JavaScript dili özelliklerini destekleyen tarayıcılardan geldiği anlamına gelir. Örneğin:

  • Sınıflar (ES2015)
  • Ok işlevleri (ES2015)
  • Jeneratörler (ES2015)
  • Blok kapsamı (ES2015)
  • Yapılaşma (ES2015)
  • Kalan ve yayılma parametreleri (ES2015)
  • Nesne kısayolu (ES2015)
  • Async/await (ES2017)

Dil spesifikasyonunun yeni sürümlerindeki özellikler, modern tarayıcılar genelinde genellikle daha az tutarlı destek sunar. Örneğin, birçok ES2020 ve ES2021 özelliği tarayıcı pazarının yalnızca% 70'inde desteklenir. Ancak tarayıcıların çoğu hâlâ bu özelliklerden yararlanırken bu özellikleri doğrudan kullanmak güvenli olacak kadar yeterli değildir. Bu, "modern" JavaScript'in hareketli bir hedef olmasına rağmen ES2017'nin yaygın olarak kullanılan modern söz dizimi özelliklerinin çoğunu içermesinin yanı sıra en geniş tarayıcı uyumluluğuna sahip olduğu anlamına gelir. Başka bir deyişle ES2017, günümüzdeki modern söz dizimine en yakın seçenektir.

Eski JavaScript

Eski JavaScript, yukarıdaki dil özelliklerinin tümünü kullanmaktan özellikle kaçınan koddur. Çoğu geliştirici, kaynak kodlarını modern söz dizimi kullanarak yazar ancak daha fazla tarayıcı desteği için her şeyi eski söz dizimine derler. Eski söz dizimine derleme yapmak tarayıcı desteğini artırır ancak etkisi genellikle düşündüğümüzden daha az olur. Çoğu durumda destek %95'ten% 98'e çıkarken yüksek bir maliyete neden olur:

  • Eski JavaScript, genellikle eşdeğer modern koddan yaklaşık% 20 daha büyük ve daha yavaştır. Araç eksiklikleri ve yanlış yapılandırma, bu boşluğu genellikle daha da genişletir.

  • Yüklü kitaplıklar, tipik üretim JavaScript kodunun% 90'ını oluşturur. Kitaplık kodu, modern kod yayınlayarak önlenebilecek çoklu dolgu ve yardımcı yinelemeleri nedeniyle daha da yüksek bir eski JavaScript ek yüküne neden olur.

npm'de modern JavaScript

Node.js, kısa bir süre önce bir paket için giriş noktalarını tanımlamak üzere bir "exports" alanını standart hale getirdi:

{
  "exports": "./index.js"
}

"exports" alanı tarafından referans verilen modüller, ES2019'u destekleyen en az 12.8 Düğüm sürümünü gösterir. Bu, "exports" alanı kullanılarak başvurulan herhangi bir modülün modern JavaScript'te yazılabileceği anlamına gelir. Paket tüketicileri, "exports" alanına sahip modüllerin modern kod içerdiğini varsaymalı ve gerekirse transkript içermelidir.

Yalnızca modern

Modern koda sahip bir paket yayınlamak ve bir bağımlılık olarak kullanıldığında paketin çevrilmesi için tüketiciye bırakmak isterseniz yalnızca "exports" alanını kullanın.

{
  "name": "foo",
  "exports": "./modern.js"
}

Eski bir yedeği olan modern

Paketinizi modern kod kullanarak yayınlamak ve eski tarayıcılar için ES5 + CommonJS yedeğini de eklemek üzere "main" ile birlikte "exports" alanını kullanın.

{
  "name": "foo",
  "exports": "./modern.js",
  "main": "./legacy.cjs"
}

Eski yedek ve ESM paketleyici optimizasyonlarıyla modern

"module" alanı, yedek bir CommonJS giriş noktası tanımlamanın yanı sıra, benzer bir eski yedek paketi işaret etmek için kullanılabilir ancak JavaScript modülü söz dizimini (import ve export) kullanır.

{
  "name": "foo",
  "exports": "./modern.js",
  "main": "./legacy.cjs",
  "module": "./module.js"
}

Webpack ve Rollup gibi birçok paketleyici, modül özelliklerinden yararlanmak ve ağaç sallamayı etkinleştirmek için bu alanı kullanır. Bu paket hâlâ import/export söz dizimi dışında herhangi bir modern kod içermeyen eski bir pakettir. Bu nedenle, hâlâ paketleme için optimize edilmiş eski bir yedekle modern kodu göndermek için bu yaklaşımı kullanın.

Uygulamalarda modern JavaScript

Web uygulamalarındaki tipik üretim JavaScript kodunun büyük çoğunluğunu üçüncü taraf bağımlılıkları oluşturur. npm bağımlılıkları geçmişte eski ES5 söz dizimi olarak yayınlanmış olsa da bu artık güvenli bir varsayım değildir ve uygulamanızda tarayıcı desteğini kesintiye uğratan risk bağımlılığı güncellemeleridir.

Modern JavaScript'e taşınan npm paketi sayısındaki artışla birlikte, derleme araçlarının bunları işleyecek şekilde ayarlandığından emin olmak önemlidir. İhtiyaç duyduğunuz npm paketlerinden bazılarının zaten modern dil özelliklerini kullanıyor olma ihtimali yüksektir. Eski tarayıcılarda uygulamanızı bozmadan npm'deki modern kodu kullanmak için birçok seçenek mevcuttur ancak genel yaklaşım, derleme sisteminin bağımlılıkları kaynak kodunuzla aynı söz dizimi hedefine aktarmasını sağlamaktır.

webpack

Web paketi 5'ten itibaren, paketler ve modüller için kod oluştururken web paketinin hangi söz dizimini kullanacağını yapılandırmak artık mümkündür. Bu, kodunuzu veya bağımlılıklarınızı aktarmaz, yalnızca webpack tarafından oluşturulan "yapışkan" kodunu etkiler. Tarayıcı desteği hedefini belirtmek için projenize bir browserslist yapılandırması ekleyin veya bu işlemi doğrudan webpack yapılandırmanızda yapın:

module.exports = {
  target: ['web', 'es2017'],
};

Ayrıca webpack, modern bir ES Modülleri ortamını hedeflerken gereksiz sarmalayıcı işlevlerini atlayan optimize edilmiş paketler oluşturacak şekilde de yapılandırılabilir. Bu işlem ayrıca webpack'i <script type="module"> kullanarak kod bölünmüş paketleri yükleyecek şekilde yapılandırır.

module.exports = {
  target: ['web', 'es2017'],
  output: {
    module: true,
  },
  experiments: {
    outputModule: true,
  },
};

Optimize Plugin ve BabelEsmPlugin gibi, eski tarayıcıları desteklemeye devam ederken modern JavaScript'in derlenip gönderilmesini mümkün kılan bir dizi web paketi eklentisi vardır.

Optimize Eklentisi

Optimize Plugin, her bir kaynak dosya yerine paket halinde sunulan nihai kodu modern JavaScript'ten eski JavaScript'e dönüştüren bir web paketi eklentisidir. Bu, web paketi yapılandırmanızın birden fazla çıkış veya söz dizimi için özel dallara ayırma işlemi olmadan her şeyin modern JavaScript olduğunu varsaymasına olanak tanıyan bağımsız bir kurulumdur.

Optimize Eklentisi, ayrı ayrı modüller yerine paketlerde çalıştığından uygulamanızın kodunu ve bağımlılıklarınızı eşit şekilde işler. Bu, npm'den gelen modern JavaScript bağımlılıklarının kullanımını güvenli hale getirir, çünkü bunların kodları paketlenip doğru söz dizimine aktarılır. Ayrıca, modern ve eski tarayıcılar için ayrı paketler oluştururken iki derleme adımı içeren geleneksel çözümlerden daha hızlı olabilir. İki paket grubu, modül/modülsüz kalıbı kullanılarak yüklenecek şekilde tasarlanmıştır.

// webpack.config.js
const OptimizePlugin = require('optimize-plugin');

module.exports = {
  // ...
  plugins: [new OptimizePlugin()],
};

Optimize Plugin, genellikle modern ve eski kodları ayrı ayrı paketleyen özel web paketi yapılandırmalarından daha hızlı ve etkili olabilir. Ayrıca, sizin yerinize çalışan Babel'i işler ve Terser'ı kullanarak paketleri modern ve eski çıkışlar için ayrı optimum ayarlarla küçültür. Son olarak, oluşturulan eski paketlerin ihtiyaç duyduğu çoklu dolgular özel bir komut dosyasına çıkarılır. Böylece hiçbir zaman yinelenmezler veya yeni tarayıcılara gereksiz yere yüklenmezler.

Karşılaştırma: Kaynak modüllerin iki kez aktarılmasıyla oluşturulan paketlerin dönüştürülmesi.

BabelEsmPlugin

BabelEsmPlugin, modern tarayıcılara daha az aktarılmış kod göndermek için mevcut paketlerin modern sürümlerini oluşturmak üzere @babel/preset-env ile birlikte çalışan bir web paketi eklentisidir. Next.js ve Preact CLI tarafından kullanılan, modül/modülsüz için en popüler kullanıma hazır çözümdür.

// webpack.config.js
const BabelEsmPlugin = require('babel-esm-plugin');

module.exports = {
  //...
  module: {
    rules: [
      // your existing babel-loader configuration:
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
    ],
  },
  plugins: [new BabelEsmPlugin()],
};

BabelEsmPlugin, uygulamanızın büyük ölçüde ayrı iki derlemesini çalıştırdığı için çok çeşitli web paketi yapılandırmasını destekler. İki kez derlemek, büyük uygulamalarda biraz ek zaman alabilir ancak bu teknik, BabelEsmPlugin ürününün mevcut web paketi yapılandırmalarına sorunsuz bir şekilde entegre edilmesine olanak tanır ve onu mevcut en uygun seçeneklerden biri yapar.

node_modules öğesini aktarmak için babel-loader'ı yapılandırın

babel-loader eklentisini önceki iki eklentiden biri olmadan kullanıyorsanız modern JavaScript npm modüllerini kullanmak için gerçekleştirmeniz gereken önemli bir adım vardır. İki ayrı babel-loader yapılandırması tanımlamak, node_modules'da bulunan modern dil özelliklerini otomatik olarak derleyip ES2017'ye dönüştürmenize olanak tanırken bir yandan da projenizin yapılandırmasında tanımlanan Babel eklentileri ve hazır ayarlarla kendi birinci taraf kodunuzu aktarır. Bu, modül/modül kurulumu için modern ve eski paketler oluşturmaz ancak eski tarayıcıları bozmadan modern JavaScript içeren npm paketlerinin yüklenmesini ve kullanılmasını mümkün kılar.

webpack-plugin-modern-npm, package.json içinde "exports" alanı olan npm bağımlılıklarını derlemek için bu tekniği kullanır. Çünkü bu bağımlılıklar modern söz dizimi içerebilir:

// webpack.config.js
const ModernNpmPlugin = require('webpack-plugin-modern-npm');

module.exports = {
  plugins: [
    // auto-transpile modern stuff found in node_modules
    new ModernNpmPlugin(),
  ],
};

Alternatif olarak, package.json modülünde çözümlenen "exports" alanı olup olmadığını kontrol ederek tekniği web paketi yapılandırmanıza manuel olarak uygulayabilirsiniz. Önbelleğe alma özelliği kısa olduğu için atlandığında özel bir uygulama aşağıdaki gibi görünebilir:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      // Transpile for your own first-party code:
      {
        test: /\.js$/i,
        loader: 'babel-loader',
        exclude: /node_modules/,
      },
      // Transpile modern dependencies:
      {
        test: /\.js$/i,
        include(file) {
          let dir = file.match(/^.*[/\\]node_modules[/\\](@.*?[/\\])?.*?[/\\]/);
          try {
            return dir && !!require(dir[0] + 'package.json').exports;
          } catch (e) {}
        },
        use: {
          loader: 'babel-loader',
          options: {
            babelrc: false,
            configFile: false,
            presets: ['@babel/preset-env'],
          },
        },
      },
    ],
  },
};

Bu yaklaşımı kullanırken, küçültücünüz tarafından modern söz diziminin desteklendiğinden emin olmanız gerekir. Hem Terser hem de uglify-es, sıkıştırma ve biçimlendirme sırasında ES2017 söz dizimini korumak ve bazı durumlarda oluşturmak için {ecma: 2017} belirtme seçeneğine sahiptir.

Birleşim

Toplayıcı, tek bir derleme kapsamında birden fazla paket grubu oluşturma için yerleşik desteğe sahiptir ve varsayılan olarak modern kod oluşturur. Sonuç olarak, Toplayıcı muhtemelen kullandığınız resmi eklentilerle modern ve eski paketler oluşturacak şekilde yapılandırılabilir.

@rollup/plugin-babel

Toplayıcı özelliğini kullanırsanız getBabelOutputPlugin() yöntemi (Rollup'ın resmi Babel eklentisi tarafından sağlanır) kodu bağımsız kaynak modüller yerine oluşturulan paketlerde dönüştürür. Toplayıcı, tek bir derlemenin parçası olarak her biri kendi eklentilerine sahip birden fazla paket grubu oluşturmayı sağlayan yerleşik desteğe sahiptir. Bunu, modern ve eski paketlerin her birini farklı bir Babel çıkış eklentisi yapılandırmasından geçirerek farklı paketler oluşturmak için kullanabilirsiniz:

// rollup.config.js
import {getBabelOutputPlugin} from '@rollup/plugin-babel';

export default {
  input: 'src/index.js',
  output: [
    // modern bundles:
    {
      format: 'es',
      plugins: [
        getBabelOutputPlugin({
          presets: [
            [
              '@babel/preset-env',
              {
                targets: {esmodules: true},
                bugfixes: true,
                loose: true,
              },
            ],
          ],
        }),
      ],
    },
    // legacy (ES5) bundles:
    {
      format: 'amd',
      entryFileNames: '[name].legacy.js',
      chunkFileNames: '[name]-[hash].legacy.js',
      plugins: [
        getBabelOutputPlugin({
          presets: ['@babel/preset-env'],
        }),
      ],
    },
  ],
};

Ek derleme araçları

Toplayıcı ve web paketi yüksek düzeyde yapılandırılabilir niteliktedir. Bu da genellikle her projenin, bağımlılıklarda modern JavaScript söz dizimini etkinleştirmek için yapılandırmasını güncellemesi gerektiği anlamına gelir. Ayrıca, Parcel, Snowpack, Vite ve WMR gibi kuralları ve varsayılanları tercih eden üst düzey derleme araçları vardır. Bu araçların çoğu, npm bağımlılıklarının modern söz dizimi içerebileceğini ve üretim için derleme derlerken bunları uygun söz dizimi düzeylerine aktarır.

Web paketi ve Rollup için özel eklentilere ek olarak, eski yedekleri içeren modern JavaScript paketleri de geliştirme kullanan tüm projelere eklenebilir. Devolution, eski JavaScript varyantlarını oluşturmak için derleme sisteminden çıktıyı dönüştüren ve paketleme ile dönüşümlerin modern bir çıkış hedefi varsayılmasını sağlayan bağımsız bir araçtır.