Bu codelab'de, kullanıcıların rastgele kedi resimlerini puanlamasına olanak tanıyan basit bir uygulamanın performansını iyileştireceksiniz. Çevrilen kod miktarını en aza indirerek JavaScript paketini nasıl optimize edeceğinizi öğrenin.

Ö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, düğmenin değerini mevcut kedi resminin altında gösterir.
Ölçüm
Herhangi bir optimizasyon eklemeden önce web sitesini inceleyerek başlamak her zaman iyi bir fikirdir:
- Siteyi önizlemek için Uygulamayı Görüntüle'ye basın. Ardından Tam Ekran'a
basın.
- Geliştirici Araçları'nı açmak için `Ctrl+Üst Karakter+J` (veya Mac'te `Command+Option+J`) tuşlarına basın.
- Ağ sekmesini tıklayın.
- Önbelleği devre dışı bırak onay kutusunu işaretleyin.
- Uygulamayı yeniden yükleyin.

Bu uygulama için 80 KB'tan fazla alan kullanılıyor. Paketin kullanılmayan kısımları olup olmadığını öğrenme zamanı:
Komut menüsünü açmak için
Control+Shift+P(veya Mac'teCommand+Shift+P) tuşlarına basın.
Kapsam sekmesini görüntülemek için
Show CoverageyazıpEntertuşuna basın.Kapsam sekmesinde, kapsamı yakalarken uygulamayı yeniden yüklemek için Yeniden yükle'yi tıklayın.

Ana paket için ne kadar kodun kullanıldığına ve ne kadar kodun yüklendiğine bakın:
Paketin yarısından fazlası (44 KB) kullanılmıyor bile. Bunun nedeni, kodun büyük bir kısmının uygulamanın eski tarayıcılarda çalışmasını sağlamak için polyfill'lerden oluşmasıdır.
@babel/preset-env kullanın
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. Her büyük tarayıcı, bu özellikleri destekleme konusunda 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 dizinindeki kaynak kodunu inceleyebilirsiniz.
Bu özelliklerin tümü Chrome'un en yeni sürümünde desteklenir. Peki bu özellikleri desteklemeyen diğer tarayıcılar ne olacak? Uygulamaya dahil edilen Babel, daha yeni söz dizimi içeren kodu eski tarayıcıların ve ortamların anlayabileceği koda derlemek için kullanılan en popüler kitaplıktır. Bunu iki şekilde yapar:
- Polyfill'ler, daha yeni ES2015+ işlevlerini taklit etmek için dahil edilir. Böylece API'leri, tarayıcı tarafından desteklenmese bile kullanılabilir.
Array.includesyönteminin polyfill örneğini aşağıda görebilirsiniz. - Eklentiler, ES2015 kodunu (veya daha yeni 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 olduğundan (ör. ok işlevleri) polyfill'lerle taklit edilemez.
Hangi Babel kitaplıklarının dahil edildiğini görmek için package.json dosyası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 derleyicisidir. Böylece tüm Babel yapılandırmaları, projenin kökündeki bir.babelrcdosyasında tanımlanır.babel-loader, webpack derleme sürecine Babel'i dahil eder.
Şimdi webpack.config.js adlı kurala bakarak babel-loader adlı kuralın nasıl dahil edildiğini inceleyin:
module: {
rules: [
//...
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
},
@babel/polyfill, daha yeni ECMAScript özelliklerinin desteklenmeyen ortamlarda çalışabilmesi için gerekli tüm polyfill'leri sağlar.src/index.js.listesinin en üstüne zaten 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 polyfill'lerin gerekli olduğunu belirler.
Babel yapılandırma dosyası olan .babelrc dosyasına göz atarak nasıl dahil edildiğini inceleyin:
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions"
}
]
]
}
Bu, Babel ve webpack kurulumudur. Webpack dışında bir modül paketleyici kullanıyorsanız Babel'i uygulamanıza nasıl dahil edeceğinizi öğrenin.
.babelrc içindeki targets özelliği, hangi tarayıcıların hedeflendiğini tanımlar. @babel/preset-env, browserslist ile entegre olur. Bu, bu alanda kullanılabilecek uyumlu sorguların tam listesini browserlist belgelerinde bulabileceğiniz anlamına gelir.
"last 2 versions" değeri, her tarayıcının son iki sürümü için uygulamadaki kodu çevirir.
Hata ayıklama
Tarayıcının tüm Babel hedeflerinin yanı sıra dahil edilen tüm dönüşümleri ve polyfill'leri eksiksiz bir şekilde görmek için .babelrc: dosyasına 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 Glitch 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 süreciyle ilgili çeşitli ayrıntıları konsola kaydeder.

Internet Explorer gibi kullanımdan kaldırılan tarayıcıların bu listede yer aldığına dikkat edin. Bu durum, desteklenmeyen tarayıcılara yeni özellikler eklenmeyeceği ve Babel'in bunlar için belirli söz dizimlerini çevirmeye devam edeceği için sorun teşkil 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üştürme eklentilerinin listesini de günlüğe kaydeder:

Bu oldukça uzun bir liste. Bunlar, Babel'in tüm hedeflenen tarayıcılar için ES2015+ söz dizimini eski söz dizimine dönüştürmek üzere kullanması gereken tüm eklentilerdir.
Ancak Babel, kullanılan belirli polyfill'leri göstermez:
![]()
Bunun nedeni, @babel/polyfill öğesinin tamamının doğrudan içe aktarılmasıdır.
Polyfill'leri tek tek yükleme
Varsayılan olarak Babel, @babel/polyfill bir dosyaya aktarıldığında eksiksiz bir ES2015+ ortamı için gereken her polyfill'i içerir. Hedef tarayıcılar için gereken belirli polyfill'leri 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 edilen tüm polyfill'leri görebilirsiniz:

Yalnızca "last 2 versions" için gerekli polyfill'ler dahil edilmiş olsa da liste hâlâ çok uzun. Bunun nedeni, her yeni özellik için hedef tarayıcıların ihtiyaç duyduğu polyfill'lerin hâlâ dahil edilmesidir. Yalnızca kodda kullanılan özellikler için gerekenleri dahil etmek ü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 polyfill'ler gerektiğinde otomatik olarak eklenir.
Bu, src/index.js. içinde @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 gerekli olan polyfill'ler dahil edilir.

Uygulama paketi boyutu önemli ölçüde küçülür.

Desteklenen tarayıcıların listesini daraltma
Dahil edilen tarayıcı hedeflerinin sayısı hâlâ oldukça fazladır ve Internet Explorer gibi kullanımdan kaldırılan tarayıcıları kullanan kullanıcı sayısı azdır. Yapılandırmaları aşağıdaki gibi 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.

Uygulama çok küçük olduğu için bu değişiklikler çok büyük bir fark yaratmaz. Ancak, kullanıcılarınızın kullanmadığından emin olduğunuz belirli tarayıcıları hariç tutmanın yanı sıra tarayıcı pazar payı yüzdesini (ör. ">0.25%") kullanmak önerilen yaklaşımdır. Bu konuda daha fazla bilgi edinmek için James Kyle'ın "Last 2 versions" considered harmful (Zararlı olduğu düşünülen "Son 2 sürüm") makalesine göz atın.
<script type="module"> kullanın
Ancak daha fazla iyileştirme yapılabilir. Kullanılmayan bazı polyfill'ler kaldırılmış olsa da bazı tarayıcılar için gerekli olmayan birçok polyfill gönderiliyor. Modüller kullanılarak daha yeni söz dizimleri yazılabilir ve gereksiz polyfill'ler kullanılmadan doğrudan tarayıcılara gönderilebilir.
JavaScript modülleri, tüm büyük tarayıcılarda desteklenen nispeten yeni bir özelliktir.
Modüller, diğer modüllerden içe ve dışa aktarma yapan 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'e gerek yoktur). 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 daha yeni tarayıcılarda çalışan ve büyük ölçüde dönüştürülmemiş ancak daha küçük dosya boyutuna sahip bir modül içeren sürüm
- Eski tarayıcılarda çalışacak daha büyük, çevrilmiş bir komut dosyası içeren sürüm
Babel ile ES modüllerini kullanma
Uygulamanın iki sürümü için ayrı @babel/preset-env ayarları kullanmak istiyorsanız .babelrc dosyasını kaldırın. Babel ayarları, uygulamanın her sürümü için iki farklı derleme biçimi belirtilerek webpack yapılandırmasına eklenebilir.
Eski komut dosyası için webpack.config.js dosyasına bir yapılandırma 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ğerini kullanmak yerine, false değerine sahip esmodules kullanıldığına dikkat edin. Bu, Babel'in henüz ES modüllerini desteklemeyen tüm tarayıcıları hedeflemek için gerekli tüm dönüşümleri ve polyfill'leri içerdiği anlamına gelir.
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, legacyConfig öğesinin tanımlandığı aşağıdaki modül komut dosyası için 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ış dosyası adı için .mjs dosya uzantısının kullanılmasıdır. Burada esmodules değeri "true" olarak ayarlanmıştır. Bu, bu modüle çıkışı yapılan kodun daha küçük, daha az derlenmiş bir komut dosyası olduğu anlamına gelir. Kullanılan tüm özellikler modülleri destekleyen tarayıcılarda zaten desteklendiğinden bu örnekte herhangi bir dönüşümden geçmez.
Dosyanın en sonunda her iki yapılandırmayı da tek bir dizide dışa aktarın.
module.exports = [
legacyConfig, moduleConfig
];
Bu sayede, 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 çevrilmiş 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, 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 içinde şu şekilde olmalıdır:
<script type="module" src="main.mjs"></script>
<script nomodule src="main.bundle.js"></script>
Modülleri destekleyen tarayıcılar main.mjs öğesini getirip yürütür ve main.bundle.js. öğesini yoksayar. Modülleri desteklemeyen tarayıcılar ise bunun tam tersini yapar.
Normal komut dosyalarının aksine, modül komut dosyalarının her zaman varsayılan olarak ertelendiğini unutmayın.
Eşdeğer nomodule komut dosyasının da ertelenmesini ve yalnızca ayrıştırma işleminden 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 işlem, modüle ve eski komut dosyasına sırasıyla module ve nomodule özelliklerini eklemektir. webpack.config.js dosyasının en üstüne ScriptExtHtmlWebpackPlugin'i 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ği ve tüm .js komut dosyası modülleri için bir nomodule özelliği ekler.
HTML belgesinde modül yayınlama
Son olarak, hem eski hem de modern komut dosyası öğelerini HTML dosyasına aktarmanız gerekir. Maalesef nihai HTML dosyasını oluşturan eklenti (HTMLWebpackPlugin), hem modül hem de modül olmayan komut dosyalarının çıkışını şu anda desteklemiyor. Bu sorunu çözmek için oluşturulan geçici çözümler ve ayrı eklentiler (ör. BabelMultiTargetPlugin ve HTMLWebpackMultiBuildPlugin) olsa da bu eğitimde, modül komut dosyası öğesini manuel olarak ekleme gibi daha basit bir yaklaşım kullanılmıştır.
Dosyanın sonuna src/index.js için aşağıdakileri ekleyin:
...
</form>
<script type="module" src="main.mjs"></script>
</body>
</html>
Şimdi uygulamayı, Chrome'un en yeni sürümü gibi modülleri destekleyen bir tarayıcıda yükleyin.

Yalnızca modül getirilir ve büyük ölçüde dönüştürülmediği için paket boyutu çok daha küçüktür. Diğer komut dosyası öğesi, tarayıcı tarafından tamamen yoksayılır.
Uygulamayı daha eski bir tarayıcıda yüklerseniz yalnızca gerekli tüm polyfill'leri ve dönüşümleri içeren daha büyük, çevrilmiş 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ü aşağıda görebilirsiniz.

Sonuç
Artık yalnızca hedeflenen tarayıcılar için gerekli polyfill'leri sağlamak üzere @babel/preset-env aracını nasıl kullanacağınızı biliyorsunuz. Ayrıca JavaScript modüllerinin, bir uygulamanın iki farklı çevrilmiş sürümünü göndererek performansı nasıl daha da artırabileceğini biliyorsunuz. Bu iki tekniğin paket boyutunuzu nasıl önemli ölçüde azaltabileceğini öğrendiğinize göre artık optimizasyona başlayabilirsiniz.