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

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

Tarayıcıların% 90'ından fazlası modern JavaScript'i çalıştırabilir ancak eski JavaScript'in yaygınlığı günümüzde web'de performans sorunlarının önemli bir kaynağı olmaya devam etmektedir.

Modern JavaScript

Modern JavaScript, belirli bir ECMAScript spesifikasyon sürümünde yazılmış kod olarak değil, tüm modern tarayıcılar tarafından desteklenen söz dizimi olarak tanımlanır. Chrome, Edge, Firefox ve Safari gibi modern web tarayıcıları tarayıcı pazarının% 90'ından fazlasını oluştururken aynı temel oluşturma motorlarını kullanan farklı tarayıcılar da ek %5'i oluşturur. Bu, dünya genelindeki web trafiğinin% 95'inin aşağıdakiler dahil olmak üzere son 10 yılda en yaygın kullanılan JavaScript dil özelliklerini destekleyen tarayıcılardan geldiği anlamına gelir:

  • Sınıflar (ES2015)
  • Ok işlevleri (ES2015)
  • Jeneratörler (ES2015)
  • Blok kapsamı (ES2015)
  • Yapıyı bozma (ES2015)
  • Rest ve spread parametreleri (ES2015)
  • Nesne kısaltması (ES2015)
  • Eş zamansız/bekle (ES2017)

Dil spesifikasyonunun yeni sürümlerindeki özellikler, modern tarayıcılarda genellikle daha tutarlı bir şekilde desteklenmez. Örneğin, birçok ES2020 ve ES2021 özelliği yalnızca tarayıcı pazarının% 70'inde desteklenir. Bu, tarayıcı sayısının çoğunu ifade etse de bu özelliklere doğrudan güvenmenin güvenli olduğu anlamına gelmez. Bu, "modern" JavaScript değişen bir hedef olsa da ES2017'nin yaygın olarak kullanılan modern söz dizimi özelliklerinin çoğunu içermesine rağmen en geniş tarayıcı uyumluluğu yelpazesine sahip olduğu anlamına gelir. Diğer bir deyişle, ES2017 günümüzün modern söz dizimine en yakın sürümdür.

Eski JavaScript

Eski JavaScript, özellikle yukarıdaki tüm dil özelliklerinin kullanılmadığı 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 derleyebilir. Eski söz dizimi için derleme yapmak tarayıcı desteğini artırır ancak bu etki genellikle düşündüğümüzden daha küçüktür. Birçok durumda, destek yaklaşık %95'ten% 98'e çıkarken önemli bir maliyete neden olur:

  • Eski JavaScript, genellikle eşdeğer modern koda kıyasla yaklaşık% 20 daha büyük ve daha yavaştır. Araç eksiklikleri ve yanlış yapılandırmalar bu farkı genellikle daha da artırır.

  • Yüklenen kitaplıklar, tipik üretim JavaScript kodunun% 90'ını oluşturur. Kitaplık kodu, modern kod yayınlanarak önlenebilen çoklu doldurma ve yardımcı programın kopyalanması nedeniyle eski JavaScript'in daha da yüksek bir yükü oluşturur.

npm'de modern JavaScript

Node.js kısa süre önce bir paketin giriş noktalarını tanımlamak için "exports" alanını standartlaştırdı:

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

"exports" alanının referans verdiği modüller, ES2019'u destekleyen en az 12.8 sürümüne sahip bir Node sürümünü ifade eder. Bu, "exports" alanı kullanılarak referans verilen tüm modüllerin modern JavaScript ile yazılabileceği anlamına gelir. Paket tüketicileri, "exports" alanı içeren modüllerin modern kod içerdiğini varsaymalı ve gerekirse derlemeyi yeniden yapmalıdır.

Yalnızca modern

Modern kod içeren bir paket yayınlamak ve paketi bağımlılık olarak kullanırken derlemeyi kullanıcıya bırakmak istiyorsanız yalnızca "exports" alanını kullanın.

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

Eski yedek ile modern

Paketinizi modern kod kullanarak yayınlamak ancak eski tarayıcılar için ES5 + CommonJS yedek seçeneği de eklemek istiyorsanız "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, JavaScript modülü söz dizimini (import ve export) kullanan benzer bir eski yedek paketi işaretlemek için de kullanılabilir.

{
  "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 alandan yararlanır. Bu, import/export söz diziminden başka modern kod içermeyen eski bir pakettir. Bu nedenle, modern kodu, hâlâ paketleme için optimize edilmiş eski bir yedek ile birlikte göndermek için bu yaklaşımı kullanın.

Uygulamalarda modern JavaScript

Üçüncü taraf bağımlılıkları, web uygulamalarındaki tipik üretim JavaScript kodunun büyük çoğunluğunu oluşturur. npm bağımlılıkları geçmişte eski ES5 söz dizimi olarak yayınlansa da bu artık güvenli bir varsayım değildir ve bağımlılık güncellemelerinin uygulamanızdaki tarayıcı desteğini bozma riski vardır.

Modern JavaScript'e geçiş yapan npm paketlerinin sayısı arttıkça derleme araçlarının bunları işleyebilecek şekilde ayarlandığından emin olmanız önemlidir. Bağımlı olduğunuz npm paketlerinden bazılarının modern dil özelliklerini zaten kullanıyor olması muhtemeldir. Uygulamanızı eski tarayıcılarda bozmadan npm'den modern kod kullanmak için çeşitli seçenekler vardır. Ancak genel fikir, derleme sisteminin bağımlılıkları kaynak kodunuzla aynı söz dizimi hedefine derlemesi şeklindedir.

webpack

webpack 5'ten itibaren, webpack'ın paketler ve modüller için kod oluştururken kullanacağı söz dizimi yapılandırılabilir. Bu işlem, kodunuzu veya bağımlılıklarınızı derlemez. Yalnızca webpack tarafından oluşturulan "yapıştırıcı" kodunu etkiler. Tarayıcı desteği hedefini belirtmek için projenize bir browserslist yapılandırması ekleyin veya bunu doğrudan webpack yapılandırmanızda yapın:

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

Webpack'i, modern bir ES modülleri ortamını hedeflerken gereksiz sarmalayıcı işlevlerini atlayan optimize edilmiş paketler oluşturacak şekilde yapılandırmak da mümkündür. Bu işlem, webpack'i <script type="module"> kullanarak kod bölme paketlerini yükleyecek şekilde de 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'i derleyip yayınlamayı mümkün kılan çeşitli webpack eklentileri vardır.

Optimize Eklentisi

Optimize eklentisi, her bir kaynak dosya yerine nihai paketlenmiş kodu modern JavaScript'den eski JavaScript'e dönüştüren bir webpack eklentisidir. Bu, webpack yapılandırmanızın her şeyin modern JavaScript olduğunu varsaymasına olanak tanıyan, birden fazla çıkış veya söz dizimi için özel dallanma içermeyen bağımsız bir kurulumdur.

Optimize Plugin, tek tek modüller yerine paketler üzerinde çalıştığından uygulamanızın kodunu ve bağımlılarınızı eşit şekilde işler. Bu sayede, npm'deki modern JavaScript bağımlılıkları güvenli bir şekilde kullanılabilir. Çünkü bu bağımlılıkların kodları, doğru söz dizimine göre derlenir ve derlenir. 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ül yok 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 kodu ayrı olarak paketleyen özel webpack yapılandırmalarından daha hızlı ve verimli olabilir. Ayrıca Babel'i sizin için çalıştırır ve modern ile eski çıkışlar için ayrı optimal ayarlarla Terser'i kullanarak paketleri en aza indirir. Son olarak, oluşturulan eski paketlerin ihtiyaç duyduğu polyfill'ler, yeni tarayıcılarda hiçbir zaman kopyalanmayacak veya gereksiz yere yüklenmeyecek şekilde özel bir komut dosyasına ayıklanır.

Karşılaştırma: Kaynak modüllerini iki kez derleme ve oluşturulan paketleri derleme.

BabelEsmPlugin

BabelEsmPlugin, modern tarayıcılara daha az derlenmiş kod göndermek için mevcut paketlerin modern sürümlerini oluşturmak amacıyla @babel/preset-env ile birlikte çalışan bir webpack eklentisidir. module/nomodule için hazır en popüler çözümdür. Next.js ve Preact CLI tarafından kullanılı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ığından çok çeşitli webpack yapılandırmalarını destekler. Büyük uygulamalarda iki kez derleme işlemi biraz daha fazla zaman alabilir. Ancak bu teknik, BabelEsmPlugin'ün mevcut webpack yapılandırmalarına sorunsuz bir şekilde entegre edilmesine olanak tanır ve bu yöntemi mevcut en kullanışlı seçeneklerden biri haline getirir.

node_modules dosyalarını derleyebilmesi için babel-loader'ı yapılandırma

babel-loader'ü önceki iki eklentiden biri olmadan kullanıyorsanız modern JavaScript npm modüllerini kullanmak için önemli bir adım uygulamanız gerekir. İki ayrı babel-loader yapılandırması tanımlamak, node_modules'de bulunan modern dil özelliklerini ES2017'ye otomatik olarak derleyebilmenizi sağlar. Bu sırada kendi birinci taraf kodunuzu, projenizin yapılandırmasında tanımlanan Babel eklentileri ve hazır ayarlarıyla derleyebilirsiniz. Bu, modül/modülsüz kurulum için modern ve eski paketler oluşturmaz ancak eski tarayıcılarda sorun oluşturmadan modern JavaScript içeren npm paketlerini yükleyip kullanmayı mümkün kılar.

webpack-plugin-modern-npm, package.json içinde "exports" alanı bulunan npm bağımlılıkları derlemek için bu tekniği kullanır. 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, çözülen modüllerin package.json alanında "exports" alanı olup olmadığını kontrol ederek tekniği webpack yapılandırmanızda manuel olarak uygulayabilirsiniz. Özetlemek amacıyla önbelleğe alma işlemini atlayarak özel bir uygulama şu şekilde 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, modern söz dizimi özelliğinin sıkıştırıcınız tarafından 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.

Gruplandırma

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

@rollup/plugin-babel

Rollup kullanıyorsanız getBabelOutputPlugin() yöntemi (Rollup'un resmi Babel eklentisi tarafından sağlanır) kodu tek tek kaynak modüller yerine oluşturulan paketlerde dönüştürür. Rollup, tek bir derlemenin parçası olarak her biri kendi eklentilerine sahip birden fazla paket grubu oluşturmak için yerleşik destek sunar. Bu özelliği kullanarak her birini farklı bir Babel çıkış eklentisi yapılandırmasından geçirerek modern ve eski için farklı paketler oluşturabilirsiniz:

// 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ı

Rollup ve webpack, yüksek oranda yapılandırılabilirdir. Bu, genellikle her projenin yapılandırmasını güncelleyerek bağımlılıklarda modern JavaScript söz dizimini etkinleştirmesi gerektiği anlamına gelir. Parcel, Snowpack, Vite ve WMR gibi, yapılandırma yerine kuralı ve varsayılanları tercih eden daha üst düzey derleme araçları da vardır. Bu araçların çoğu, npm bağımlılıkları modern söz dizimi içerebileceğini varsayar ve üretim için derleme yaparken bunları uygun söz dizimi düzeylerine dönüştürür.

Webpack ve Rollup için özel eklentilere ek olarak, eski yedekleri olan modern JavaScript paketleri, devrim kullanılarak herhangi bir projeye eklenebilir. Devolution, bir derleme sisteminden gelen çıkışı eski JavaScript varyantları oluşturacak şekilde dönüştüren bağımsız bir araçtır. Bu araç, paketlemenin ve dönüşümlerin modern bir çıkış hedefi almasını sağlar.