Bu codelab'de, kullanıcıların rastgele kedileri derecelendirmesine olanak tanıyan bu basit uygulamanın performansını artıracaksınız. Aktarılan kod miktarını en aza indirerek JavaScript paketini nasıl optimize edeceğinizi öğrenin.
Örnek uygulamada, her kediyi ne kadar beğendiğ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
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 `Control+Shift+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 bazı bölümlerinin kullanılmadığını öğrenmek için:
Komut menüsünü açmak için
Control+Shift+P
(veya Mac'teCommand+Shift+P
) tuşuna basın.Show Coverage
giripEnter
tuşuna basarak Kapsam sekmesini görüntüleyin.Kapsam yakalarken uygulamayı yeniden yüklemek için Kapsam sekmesinde Yeniden yükle'yi tıklayın.
Ana pakette ne kadar kod kullanıldığına ve ne kadar kod yüklendiğine göz atın:
Paketin yarısından fazlası (44 KB) kullanılmıyor. Bunun nedeni, uygulamanın eski tarayıcılarda çalışmasını sağlamak için kodun büyük bir kısmının polyfill'lerden oluşmasıdır.
@babel/preset-env 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 öneri sürecini geçen yeni özellikleri 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
adresindeki kaynak kodunu inceleyebilirsiniz.
Bu özelliklerin tümü Chrome'un en yeni sürümünde desteklenir. Ancak bu özellikleri desteklemeyen diğer tarayıcılar ne olacak? Uygulamaya dahil edilen Babel, yeni söz dizimi içeren kodu eski tarayıcıların ve ortamların anlayabileceği bir koda derlemek için kullanılan en popüler kitaplıktır. Bu işlemi iki şekilde yapar:
- Tarayıcı tarafından desteklenmese bile API'lerinin kullanılabilmesi için daha yeni ES2015+ işlevlerini taklit etmek üzere çoklu doldurmalar eklenir.
Array.includes
yönteminin polyfill örneğini aşağıda bulabilirsiniz. - 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 (ör. ok işlevleri) olduğundan, polyfill'lerle taklit edilemezler.
Hangi Babel kitaplıklarının dahil olduğunu görmek için package.json
bölümüne 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. Bu sayede tüm Babel yapılandırmaları, projenin kökündeki.babelrc
içinde tanımlanır.babel-loader
, webpack derleme sürecine Babel'i dahil eder.
Şimdi webpack.config.js
'ya bakarak babel-loader
'nin nasıl kural olarak eklendiğini görün:
module: { rules: [ //... { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" } ] },
@babel/polyfill
, daha yeni ECMAScript özelliklerinin desteklenmediği ortamlarda çalışabilmesi için gerekli tüm çoklu doldurma kodlarını sağlar.src/index.js.
dosyasının en üstüne zaten içe aktarılmışsa
import "./style.css";
import "@babel/polyfill";
@babel/preset-env
, hedef olarak seçilen tarayıcılar veya ortamlar için hangi dönüştürmelerin ve polyfill'lerin gerekli olduğunu belirler.
Babel yapılandırma dosyası .babelrc
'ya göz atarak nasıl dahil edildiğini görebilirsiniz:
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions"
}
]
]
}
Bu, Babel ve webpack kurulumudur. Webpack'ten farklı 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 nedenle, bu alanda kullanılabilecek uyumlu sorguların tam listesini browserslist belgelerinde bulabilirsiniz.
"last 2 versions"
değeri, her tarayıcının son iki sürümü için uygulamadaki kodu dönüştürür.
Hata ayıklama
Tarayıcının tüm Babel hedeflerinin yanı sıra dahil edilen tüm dönüştürme ve polyfill'leri eksiksiz olarak görmek için debug
alanını .babelrc:
öğesine 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, derleme süreciyle ilgili olarak konsola bir dizi ayrıntı kaydeder. Bu ayrıntılar arasında kodun derlendiği tüm hedef ortamlar yer alır.
Internet Explorer gibi desteği sonlandırılan tarayıcıların bu listeye dahil edildiğine dikkat edin. Desteklenmeyen tarayıcılara yeni özellikler eklenmeyeceği ve Babel'in bunlar için belirli söz dizimlerini derlemeye devam edeceği için bu durum sorun teşkil eder. Kullanıcılar sitenize erişmek için bu tarayıcıyı kullanmıyorsa bu işlem, paketinizin boyutunu gereksiz yere artırır.
Babel, kullanılan dönüştürme eklentilerinin listesini de günlüğe kaydeder:
Bu liste oldukça uzun. Bunlar, Babel'in hedef tarayıcıların tümünde ES2015+ söz dizimini daha eski bir söz dizimine dönüştürmek için kullanması gereken tüm eklentilerdir.
Ancak Babel, kullanılan belirli polyfill'leri göstermez:
Bunun nedeni, @babel/polyfill
'nın tamamının doğrudan içe aktarılmasıdır.
Polyfill'leri tek tek yükleme
Varsayılan olarak Babel, bir dosyaya @babel/polyfill
içe aktarıldığında tam bir ES2015+ ortamı için gereken tüm polyfill'leri içerir. Hedef tarayıcılar için gereken belirli polyfill'leri 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 dahil edilen tüm belirli polyfill'leri görebilirsiniz:
Şu anda yalnızca "last 2 versions"
için gerekli olan polyfill'ler dahil edilse de liste hâlâ çok uzun. Bunun nedeni, her yeni özellik için hedef tarayıcılarda gereken 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, gerektiğinde polyfill'ler otomatik olarak dahil edilir.
Bu, @babel/polyfill
içe aktarma işlemini src/index.js.
bölümünden kaldırabileceğiniz anlamına gelir.
import "./style.css";
import "@babel/polyfill";
Artık yalnızca uygulama için gereken polyfill'ler dahil ediliyor.
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 yüksek ve Internet Explorer gibi desteği sonlandırılan tarayıcıları kullanan kullanıcı sayısı az. 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ğundan bu değişiklikler arasında pek bir fark yoktur. Ancak, tarayıcı pazar payı yüzdesi (ör. ">0.25%"
) kullanmanın yanı sıra kullanıcılarınızın kullanmadığından emin olduğunuz belirli tarayıcıları hariç tutmak önerilen yaklaşımdır. Bu konu hakkında daha fazla bilgi edinmek için James Kyle'ın "Son 2 sürüm zararlı kabul ediliyor" başlıklı makalesine göz atın.
<script type="module"> kullanın.
Daha fazla iyileştirme yapılabilir. Kullanılmayan bir dizi polyfill kaldırılmış olsa da bazı tarayıcılar için gerekmeyen birçok polyfill gönderilmektedir. Modüller kullanılarak daha yeni söz dizimi 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.
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 gerek kalmadan) 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 daha yeni tarayıcılarda çalışacak ve büyük ölçüde derlenmemiş ancak daha küçük bir dosya boyutuna sahip bir modül içeren bir sürüm
- Herhangi bir eski tarayıcıda çalışacak daha büyük ve derlenmiş 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. Uygulamanın her sürümü için iki farklı derleme biçimi belirtilerek Babel ayarları webpack yapılandırmasına eklenebilir.
webpack.config.js
'ya eski komut dosyası için 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ğeri yerine false
değerine sahip esmodules
öğesinin kullanıldığını unutmayın. Bu, Babel'in ES modüllerini henüz desteklemeyen her tarayıcıyı hedeflemek için gerekli tüm dönüştürmeleri ve polyfill'leri içerdiği anlamına gelir.
entry
, cssRule
ve corePlugins
nesnelerini webpack.config.js
dosyasının başına 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 de benzer şekilde, aşağıdaki modül komut dosyası için legacyConfig
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 ayarlanmıştır. Bu, bu modüle çıkışı yapılan kodun, kullanılan tüm özellikler modülleri destekleyen tarayıcılarda zaten desteklendiğinden bu örnekte herhangi bir dönüşümden geçmeyen daha küçük ve daha az derlenmiş bir komut dosyası olduğu anlamına gelir.
Dosyanın en sonunda, her iki yapılandırmayı da tek bir dizide dışa aktarın.
module.exports = [
legacyConfig, moduleConfig
];
Bu işlem, 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 derlenmiş komut dosyası oluşturur.
Modülleri destekleyen tarayıcılar, nomodule
özelliği içeren 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 yedekle birlikte bir modül de ekleyebileceğiniz anlamına gelir. İdeal olarak, uygulamanın iki sürümü index.html
şöyle 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 varsayılan olarak her zaman ertelendiğini unutmayın.
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 eklemek, webpack.config.js
'in en üstüne ScriptExtHtmlWebpackPlugin'i içe aktarmaktır:
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 dokümanında modül sunma
Yapılması gereken son şey, hem eski hem de modern komut dosyası öğelerini HTML dosyasına aktarmaktır. Maalesef son HTML dosyasını oluşturan HTMLWebpackPlugin
eklentisi, modül ve nomodule komut dosyalarının çıkışını şu anda desteklemiyor. Bu sorunu çözmek için geçici çözümler ve ayrı eklentiler (ör. BabelMultiTargetPlugin ve HTMLWebpackMultiBuildPlugin) oluşturulmuş olsa da bu eğitimde modül komut dosyası öğesini manuel olarak eklemenin daha basit bir yaklaşımı kullanılmaktadır.
Dosyanın sonundaki src/index.js
bölümüne aşağıdakileri ekleyin:
...
</form>
<script type="module" src="main.mjs"></script>
</body>
</html>
Şimdi uygulamayı, modülleri destekleyen bir tarayıcıda (ör. Chrome'un en yeni sürümü) yükleyin.
Yalnızca modül getirilir. Büyük ölçüde dönüştürülmemiş olduğundan paket boyutu çok daha küçüktür. Diğer komut dosyası öğesi, tarayıcı tarafından tamamen yok sayılır.
Uygulamayı eski bir tarayıcıya yüklerseniz yalnızca daha büyük olan, derlenmiş komut dosyası (gerekli tüm polyfill'ler ve dönüşümlerle birlikte) getirilir. Chrome'un eski bir sürümünde (38. sürüm) yapılan tüm isteklerin ekran görüntüsünü aşağıda bulabilirsiniz.
Sonuç
Artık @babel/preset-env
kullanarak yalnızca hedeflenen tarayıcılar için gerekli polyfill'leri nasıl sağlayacağınızı biliyorsunuz. Ayrıca, JavaScript modüllerinin bir uygulamanın iki farklı derlenmiş sürümünü göndererek performansı daha da artırabileceğini biliyorsunuz. Bu iki tekniğin paket boyutunuzu nasıl önemli ölçüde küçültebileceğini iyi anladığınıza göre artık optimizasyon yapmaya başlayabilirsiniz.