Kullanıcı arabirimi boyutunu küçült

Uygulamanızı mümkün olduğunca küçük hale getirmek için webpack'i kullanma

Bir uygulamayı optimize ederken yapılacak ilk şeylerden biri uygulamayı mümkün olduğunca küçük hale getirmektir. Bunu webpack ile nasıl yapacağınız aşağıda açıklanmıştır.

Webpack 4, yeni mode işaretini kullanıma sundu. Webpack'e uygulamayı belirli bir ortam için oluşturduğunuzu belirtmek üzere bu işaretçiyi 'development' veya 'production' olarak ayarlayabilirsiniz:

// webpack.config.js
module.exports = {
  mode: 'production',
};

Uygulamanızı üretim için oluştururken production modunu etkinleştirdiğinizden emin olun. Bu, webpack'in kod sıkıştırma, kitaplıklardaki yalnızca geliştirme amaçlı kodun kaldırılması gibi optimizasyonlar ve daha fazlasını uygulamasını sağlar.

Daha fazla bilgi

Kod sıkıştırmayı etkinleştirme

Küçültme işlemi, fazladan boşlukları kaldırarak, değişken adlarını kısaltmak ve diğer yöntemlerle kodu sıkıştırmak anlamına gelir. Aşağıdaki gibi:

// Original code
function map(array, iteratee) {
  let index = -1;
  const length = array == null ? 0 : array.length;
  const result = new Array(length);

  while (++index < length) {
    result[index] = iteratee(array[index], index, array);
  }
  return result;
}

// Minified code
function map(n,r){let t=-1;for(const a=null==n?0:n.length,l=Array(a);++t<a;)l[t]=r(n[t],t,n);return l}

Webpack, kodu küçültmek için iki yöntemi destekler: paket düzeyinde küçültme ve yükleyiciye özel seçenekler. Bu iki araç aynı anda kullanılmalıdır.

Paket düzeyinde küçültme

Paket düzeyinde küçültme, derleme işleminden sonra paketin tamamını sıkıştırır. İşleyiş şekli:

  1. Kodunuzu şu şekilde yazarsınız:

    // comments.js
    import './comments.css';
    export function render(data, target) {
      console.log('Rendered!');
    }
    
  2. Webpack, dosyayı yaklaşık olarak aşağıdaki şekilde derler:

    // bundle.js (part of)
    "use strict";
    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
    /* harmony export (immutable) */ __webpack_exports__["render"] = render;
    /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__comments_css__ = __webpack_require__(1);
    /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__comments_css_js___default =
    __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__comments_css__);
    
    function render(data, target) {
    console.log('Rendered!');
    }
    
  3. Küçültücü, şablonu yaklaşık olarak aşağıdaki bölümlere sıkıştırır:

    // minified bundle.js (part of)
    "use strict";function t(e,n){console.log("Rendered!")}
    Object.defineProperty(n,"__esModule",{value:!0}),n.render=t;var o=r(1);r.n(o)
    

Webpack 4'te paket düzeyinde küçültme hem üretim modunda hem de üretim modu olmadan otomatik olarak etkinleştirilir. Bu araç, arka planda UglifyJS küçültücüyü kullanır. (Küçültmeyi devre dışı bırakmanız gerekirse geliştirme modunu kullanmanız veya false'yi optimization.minimize seçeneğine aktarmanız yeterlidir.)

Web paketi 3'te,doğrudan UglifyJS eklentisini kullanmanız gerekir. Eklenti, webpack ile birlikte gelir. Eklentiyi etkinleştirmek için yapılandırmanın plugins bölümüne ekleyin:

// webpack.config.js
const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.optimize.UglifyJsPlugin(),
  ],
};

Yükleyiciye özel seçenekler

Kodu küçültmenin ikinci yolu, yükleyiciye özel seçeneklerdir (yükleyici nedir?). Yükleyici seçenekleriyle, küçültücünün küçültme yapamadığı öğeleri sıkıştırabilirsiniz. Örneğin, bir CSS dosyasını css-loader ile içe aktardığınızda, dosya bir dizede derlenir:

/* comments.css */
.comment {
  color: black;
}
// minified bundle.js (part of)
exports=module.exports=__webpack_require__(1)(),
exports.push([module.i,".comment {\r\n  color: black;\r\n}",""]);

Kod sıkıştırıcı, dize olduğu için bu kodu sıkıştıramaz. Dosya içeriğini küçültmek için yükleyiciyi bunu yapacak şekilde yapılandırmamız gerekir:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          { loader: 'css-loader', options: { minimize: true } },
        ],
      },
    ],
  },
};

Daha fazla bilgi

NODE_ENV=production değerini belirtin

Kullanıcı arayüzü boyutunu küçültmenin bir diğer yolu da kodunuzdaki NODE_ENVortam değişkenini production değerine ayarlamaktır.

Kitaplıklar, hangi modda (geliştirme veya üretim) çalışmaları gerektiğini algılamak için NODE_ENV değişkenini okur. Bazı kitaplıklar bu değişkene bağlı olarak farklı davranır. Örneğin, NODE_ENV değeri production olarak ayarlanmadığında Vue.js ek kontroller yapar ve uyarılar yazdırır:

// vue/dist/vue.runtime.esm.js
// …
if (process.env.NODE_ENV !== 'production') {
  warn('props must be strings when using array syntax.');
}
// …

React de benzer şekilde çalışır. Uyarıları içeren bir geliştirme derlemesi yükler:

// react/index.js
if (process.env.NODE_ENV === 'production') {
  module.exports = require('./cjs/react.production.min.js');
} else {
  module.exports = require('./cjs/react.development.js');
}

// react/cjs/react.development.js
// …
warning$3(
    componentClass.getDefaultProps.isReactClassApproved,
    'getDefaultProps is only used on classic React.createClass ' +
    'definitions. Use a static property named `defaultProps` instead.'
);
// …

Bu tür kontroller ve uyarılar genellikle üretimde gereksizdir ancak kodda kalır ve kitaplık boyutunu artırır. Web paketi 4'te,optimization.nodeEnv: 'production' seçeneğini ekleyerek bunları kaldırın:

// webpack.config.js (for webpack 4)
module.exports = {
  optimization: {
    nodeEnv: 'production',
    minimize: true,
  },
};

Webpack 3'te bunun yerine DefinePlugin kullanın:

// webpack.config.js (for webpack 3)
const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': '"production"'
    }),
    new webpack.optimize.UglifyJsPlugin()
  ]
};

Hem optimization.nodeEnv seçeneği hem de DefinePlugin aynı şekilde çalışır. Bu seçenekler, process.env.NODE_ENV değerinin tüm kullanımlarını belirtilen değerle değiştirir. Yukarıdaki yapılandırmayla:

  1. Webpack, process.env.NODE_ENV değerinin tüm örneklerini "production" ile değiştirir:

    // vue/dist/vue.runtime.esm.js
    if (typeof val === 'string') {
      name = camelize(val);
      res[name] = { type: null };
    } else if (process.env.NODE_ENV !== 'production') {
      warn('props must be strings when using array syntax.');
    }
    

    // vue/dist/vue.runtime.esm.js
    if (typeof val === 'string') {
      name = camelize(val);
      res[name] = { type: null };
    } else if ("production" !== 'production') {
      warn('props must be strings when using array syntax.');
    }
    
  2. Ardından küçültücü, bu tür tüm if dallarını kaldırır. Çünkü "production" !== 'production' her zaman yanlıştır ve eklenti, şu dalların içindeki kodun hiçbir zaman yürütülmeyeceğini anlar:

    // vue/dist/vue.runtime.esm.js
    if (typeof val === 'string') {
      name = camelize(val);
      res[name] = { type: null };
    } else if ("production" !== 'production') {
      warn('props must be strings when using array syntax.');
    }
    

    // vue/dist/vue.runtime.esm.js (without minification)
    if (typeof val === 'string') {
      name = camelize(val);
      res[name] = { type: null };
    }
    

Daha fazla bilgi

ES modüllerini kullanma

Ön uç boyutunu küçültmenin bir diğer yolu da ES modülleri kullanmaktır.

ES modüllerini kullandığınızda webpack, ağaç sallama işlemini yapabilir. Ağda kaldırma, bir paketleyicinin tüm bağımlılık ağacını taraması, hangi bağımlılıkların kullanıldığını kontrol etmesi ve kullanılmayanları kaldırmasıdır. Bu nedenle, ES modülü söz dizimini kullanırsanız webpack, kullanılmayan kodu ortadan kaldırabilir:

  1. Birden fazla dışa aktarma içeren bir dosya yazarsınız ancak uygulama bunlardan yalnızca birini kullanır:

    // comments.js
    export const render = () => { return 'Rendered!'; };
    export const commentRestEndpoint = '/rest/comments';
    
    // index.js
    import { render } from './comments.js';
    render();
    
  2. Webpack, commentRestEndpoint öğesinin kullanılmadığını ve pakette ayrı bir dışa aktarma noktası oluşturmadığını anlar:

    // bundle.js (part that corresponds to comments.js)
    (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    const render = () => { return 'Rendered!'; };
    /* harmony export (immutable) */ __webpack_exports__["a"] = render;
    
    const commentRestEndpoint = '/rest/comments';
    /* unused harmony export commentRestEndpoint */
    })
    
  3. Küçültücü, kullanılmayan değişkeni kaldırır:

    // bundle.js (part that corresponds to comments.js)
    (function(n,e){"use strict";var r=function(){return"Rendered!"};e.b=r})
    

Bu işlem, ES modülleriyle yazılan kitaplıklarda bile çalışır.

Ancak tam olarak webpack'in yerleşik minify aracını (UglifyJsPlugin) kullanmanız gerekmez. Geçersiz kodların kaldırılmasını destekleyen herhangi bir küçültücü (ör. Babel Minify eklentisi veya Google Closure Compiler eklentisi) bu sorunu çözecektir.

Daha fazla bilgi

Resimleri optimize edin

Görseller, sayfa boyutunun yarısından fazlasını oluşturur. JavaScript kadar kritik olmasalar (ör. oluşturmayı engellemezler), yine de bant genişliğinin büyük bir kısmını tüketirler. webpack'ta bunları optimize etmek için url-loader, svg-url-loader ve image-webpack-loader özelliklerini kullanın.

url-loader, küçük statik dosyaları uygulamaya satır içi olarak ekler. Yapılandırma olmadan, iletilen bir dosyayı alır, derlenmiş paketin yanına yerleştirir ve bu dosyanın URL'sini döndürür. Ancak limit seçeneğini belirtirsek bu sınırdan küçük dosyalar Base64 veri URL'si olarak kodlanır ve bu URL döndürülür. Bu işlem, resmi JavaScript kodunda satır içi yapar ve bir HTTP isteği kaydeder:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(jpe?g|png|gif)$/,
        loader: 'url-loader',
        options: {
          // Inline files smaller than 10 kB (10240 bytes)
          limit: 10 * 1024,
        },
      },
    ],
  }
};
// index.js
import imageUrl from './image.png';
// → If image.png is smaller than 10 kB, `imageUrl` will include
// the encoded image: 'data:image/png;base64,iVBORw0KGg…'
// → If image.png is larger than 10 kB, the loader will create a new file,
// and `imageUrl` will include its url: `/2fcd56a1920be.png`

svg-url-loader, url-loader gibi çalışır. Tek fark, dosyaları Base64 kodu yerine URL kodlaması ile kodlar. Bu, SVG resimleri için kullanışlıdır. SVG dosyaları düz metin olduğu için bu kodlama daha az yer kaplar.

module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/,
        loader: "svg-url-loader",
        options: {
          limit: 10 * 1024,
          noquotes: true
        }
      }
    ]
  }
};

image-webpack-loader, üzerinden geçen resimleri sıkıştırır. JPG, PNG, GIF ve SVG resimleri desteklediğinden tüm bu türler için onu kullanacağız.

Bu yükleyici, resimleri uygulamaya yerleştirmediğinden url-loader ve svg-url-loader ile birlikte çalışmalıdır. Bu yükleyiciyi her iki kurala da (biri JPG/PNG/GIF resimleri, diğeri SVG resimleri için) kopyalayıp yapıştırmak zorunda kalmamak için enforce: 'pre' ile ayrı bir kural olarak ekleyeceğiz:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(jpe?g|png|gif|svg)$/,
        loader: 'image-webpack-loader',
        // This will apply the loader before the other ones
        enforce: 'pre'
      }
    ]
  }
};

Yükleyicinin varsayılan ayarları kullanıma hazırdır. Ancak daha fazla yapılandırmak isterseniz eklenti seçeneklerine göz atın. Hangi seçenekleri belirteceğinizi belirlemek için Addy Osmani'nin resim optimizasyonu ile ilgili mükemmel rehberine göz atın.

Daha fazla bilgi

Bağımlılıkları optimize etme

Ortalama JavaScript boyutunun yarısından fazlası bağımlılıklardan kaynaklanır ve bu boyutun bir kısmı gereksiz olabilir.

Örneğin, Lodash (v4.17.4 itibarıyla) pakete 72 KB sıkıştırılmış kod ekler. Ancak, örneğin yalnızca 20 yöntemini kullanırsanız yaklaşık 65 KB küçültülmüş kod herhangi bir işe yaramaz.

Moment.js de bu tür bir kitaplıktır. 2.19.1 sürümü 223 KB sıkıştırılmış kod kullanıyor. Bu çok büyük bir boyuttur. Bir sayfadaki JavaScript'in ortalama boyutu Ekim 2017'de 452 KB idi. Ancak bu boyutun 170 KB'ı yerelleştirme dosyalarıdır. Moment.js'yi birden fazla dilde kullanmıyorsanız bu dosyalar, paketi bir amaç olmadan şişirir.

Bu bağımlılıkların tümü kolayca optimize edilebilir. Optimizasyon yaklaşımlarını bir GitHub deposunda topladık. Hemen göz atın!

ES modülleri için modül birleştirmeyi etkinleştir (kapsam kaldırma olarak da bilinir)

Bir paket oluştururken webpack, her modülü bir işleve sarmalar:

// index.js
import {render} from './comments.js';
render();

// comments.js
export function render(data, target) {
  console.log('Rendered!');
}

// bundle.js (part  of)
/* 0 */
(function(module, __webpack_exports__, __webpack_require__) {
  "use strict";
  Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
  var __WEBPACK_IMPORTED_MODULE_0__comments_js__ = __webpack_require__(1);
  Object(__WEBPACK_IMPORTED_MODULE_0__comments_js__["a" /* render */])();
}),
/* 1 */
(function(module, __webpack_exports__, __webpack_require__) {
  "use strict";
  __webpack_exports__["a"] = render;
  function render(data, target) {
    console.log('Rendered!');
  }
})

Geçmişte, CommonJS/AMD modüllerini birbirinden ayırmak için bu gerekliydi. Ancak bu, her modül için bir boyut ve performans ek yükü getiriyordu.

Webpack 2, CommonJS ve AMD modüllerinin aksine her birini bir işlevle sarmalamadan paket hâline getirilebilen ES modülleri için destek sunar. Webpack 3 ise modül birleştirme özelliğiyle bu tür paketleme işlemlerini mümkün kıldı. Modül birleştirme işleminin işlevi şudur:

// index.js
import {render} from './comments.js';
render();

// comments.js
export function render(data, target) {
  console.log('Rendered!');
}

// Unlike the previous snippet, this bundle has only one module
// which includes the code from both files

// bundle.js (part of; compiled with ModuleConcatenationPlugin)
/* 0 */
(function(module, __webpack_exports__, __webpack_require__) {
  "use strict";
  Object.defineProperty(__webpack_exports__, "__esModule", { value: true });

  // CONCATENATED MODULE: ./comments.js
    function render(data, target) {
    console.log('Rendered!');
  }

  // CONCATENATED MODULE: ./index.js
  render();
})

Fark görüyor musunuz? Düz pakette, modül 0, modül 1'den render gerektiriyordu. Modül birleştirme kullanıldığında require, gerekli işlevle değiştirilir ve modül 1 kaldırılır. Pakette daha az modül ve daha az modül yükü vardır.

Bu davranışı etkinleştirmek için web paketi 4'te optimization.concatenateModules seçeneğini etkinleştirin:

// webpack.config.js (for webpack 4)
module.exports = {
  optimization: {
    concatenateModules: true
  }
};

Web paketi 3'te,ModuleConcatenationPlugin kullanın:

// webpack.config.js (for webpack 3)
const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.optimize.ModuleConcatenationPlugin()
  ]
};

Daha fazla bilgi

Hem webpack hem de webpack olmayan kodunuz varsa externals değerini kullanın

Bazı kodların webpack ile derlendiği, bazılarının ise derlendiği büyük bir projeniz olabilir. Oynatıcı widget'ının webpack ile oluşturulmuş olabileceği bir video barındırma sitesi gibi, etrafındaki sayfa da aşağıdakiler olmayabilir:

Bir video barındırma sitesinin ekran görüntüsü
(Tamamen rastgele bir video barındırma sitesi)

Her iki kod parçası da ortak bağımlılıklara sahipse kodlarını birkaç kez indirmemek için bunları paylaşabilirsiniz. Bu işlem web paketinin externals seçeneğiyle yapılır. Modülleri değişkenlerle veya diğer harici içe aktarma işlemleriyle değiştirir.

window bölgesinde bağımlılıklar kullanılabiliyorsa

Webpack dışındaki kodunuz window içinde değişken olarak kullanılabilen bağımlılıklara dayanıyorsa bağımlılık adlarını değişken adlarıyla takma adlandırın:

// webpack.config.js
module.exports = {
  externals: {
    'react': 'React',
    'react-dom': 'ReactDOM'
  }
};

Bu yapılandırmayla webpack, react ve react-dom paketlerini bir araya getirmez. Bunların yerine şuna benzer bir metin yer alır:

// bundle.js (part of)
(function(module, exports) {
  // A module that exports `window.React`. Without `externals`,
  // this module would include the whole React bundle
  module.exports = React;
}),
(function(module, exports) {
  // A module that exports `window.ReactDOM`. Without `externals`,
  // this module would include the whole ReactDOM bundle
  module.exports = ReactDOM;
})

Bağımlılıklar AMD paketleri olarak yükleniyorsa

Web paketi olmayan kodunuz window içine bağımlılıkları göstermiyorsa işler daha karmaşıktır. Bununla birlikte, web paketi olmayan kod bu bağımlılıkları AMD paketleri olarak tüketirse aynı kodu iki kez yüklemekten kaçınabilirsiniz.

Bunu yapmak için web paketi kodunu AMD paketi ve takma ad modüllerinden kitaplık URL'lerinde derleyin:

// webpack.config.js
module.exports = {
  output: {
    libraryTarget: 'amd'
  },
  externals: {
    'react': {
      amd: '/libraries/react.min.js'
    },
    'react-dom': {
      amd: '/libraries/react-dom.min.js'
    }
  }
};

Web paketi, paketi define() içine sarmalar ve şu URL'lere bağımlı hale getirir:

// bundle.js (beginning)
define(["/libraries/react.min.js", "/libraries/react-dom.min.js"], function () {  });

Web paketi olmayan kod, bağımlılıklarını yüklemek için aynı URL'leri kullanıyorsa bu dosyalar yalnızca bir kez yüklenir. Ek istekler, yükleyici önbelleğini kullanır.

Daha fazla bilgi

Özet

  • Web paketi 4 kullanıyorsanız üretim modunu etkinleştirme
  • Paket düzeyinde kod sıkıştırıcı ve yükleyici seçenekleriyle kodunuzu küçültme
  • NODE_ENV yerine production yazarak yalnızca geliştirme amaçlı kodu kaldırın.
  • Ağaç sallamayı etkinleştirmek için ES modüllerini kullanma
  • Resimleri sıkıştırın
  • Bağımlılığa özel optimizasyonlar uygulama
  • Modül birleştirmeyi etkinleştir
  • Sizin için mantıklıysa externals özelliğini kullanın.