Uzun süreli önbelleğe alma özelliğinden yararlanın

Webpack, öğeleri önbelleğe almaya nasıl yardımcı olur?

Uygulamanız gereken bir sonraki adım (uygulama boyutunu optimize ettikten uygulamanın önbelleğe alma süresini iyileştirir. Uygulamanın bazı kısımlarını kullanmak için ve her defasında yeniden indirmekten kaçınabilir.

Paket sürümü belirleme ve önbellek üstbilgilerini kullanma

Önbelleğe alma işlemi için genel yaklaşım şu şekildedir:

  1. tarayıcıya bir dosyayı çok uzun bir süre (ör. bir yıl) için önbelleğe almasını söyler:

    # Server header
    Cache-Control: max-age=31536000
    

    Cache-Control uygulamasının işlevini bilmiyorsanız Jake Archibald'ın en iyi önbelleğe alma konusunda mükemmel kayıt öğrenin.

  2. ve yeniden indirmeyi zorunlu kılmak için değiştirildiğinde dosyayı yeniden adlandırın:

    <!-- Before the change -->
    <script src="./index-v15.js"></script>
    
    <!-- After the change -->
    <script src="./index-v16.js"></script>
    

Bu yaklaşım, tarayıcıya JS dosyasını indirmesini, önbelleğe almasını ve önbelleğe alınmış kopya. Tarayıcı, yalnızca dosya adı değişirse ağa bağlanır (veya bir yıl geçerse).

Webpack ile de aynısını yaparsınız ancak bir sürüm numarası yerine dosya karma değeri. Karma değeri dosya adına eklemek için [chunkhash]:

// webpack.config.js
module.exports = {
  entry: './index.js',
  output: {
    filename: 'bundle.[chunkhash].js' // → bundle.8e0d62a03.js
  }
};

Gerektiğinde dosyayı istemciye göndermek için HtmlWebpackPlugin veya WebpackManifestPlugin.

HtmlWebpackPlugin, bir ama daha az esnek bir yaklaşım izler. Derleme sırasında bu eklenti Tüm derlenmiş kaynakları içeren HTML dosyası. Sunucu mantığınız karmaşıksa sizin için yeterli olacaktır:

<!-- index.html -->
<!DOCTYPE html>
<!-- ... -->
<script src="bundle.8e0d62a03.js"></script>

İlgili içeriği oluşturmak için kullanılan WebpackManifestPlugin Karmaşık bir sunucu parçanız varsa kullanışlı olan daha esnek bir yaklaşımdır. Derleme sırasında, dosya adları arasında eşleme içeren bir JSON dosyası oluşturur. karma olmadan ve karma ile dosya adları olmadan. Öğrenmek için sunucuda bu JSON kullanın hangi dosyayla çalışacak:

// manifest.json
{
  "bundle.js": "bundle.8e0d62a03.js"
}

Daha fazla bilgi

Bağımlılıkları ve çalışma zamanını ayrı bir dosyaya çıkarın

Bağımlılıklar

Uygulama bağımlılıkları genellikle gerçek uygulama kodundan daha az değişir. Taşınırsanız ayrı bir dosyada toplarsanız, tarayıcı bunları ayrı ayrı önbelleğe alabilir: ve uygulama kodu her değiştiğinde bunları yeniden indirmez.

Bağımlılıkları ayrı bir parçaya ayıklamak için üç adımı uygulayın:

  1. Çıkış dosya adını [name].[chunkname].js ile değiştirin:

    // webpack.config.js
    module.exports = {
      output: {
        // Before
        filename: 'bundle.[chunkhash].js',
        // After
        filename: '[name].[chunkhash].js'
      }
    };
    

    Web paketi, uygulamayı derlediğinde [name]'in yerini alır olduğunu görebilirsiniz. [name] bölümünü eklemezsek parçalarını birbirinden ayırt etmek oldukça zor.

  2. entry alanını nesneye dönüştürün:

    // webpack.config.js
    module.exports = {
      // Before
      entry: './index.js',
      // After
      entry: {
        main: './index.js'
      }
    };
    

    Bu snippet'te, "main" bir parçanın adıdır. Bu ad, [name] yerine 1. adımdaki talimatları uygulayın.

    Şu ana kadar uygulamayı derlerseniz bu parça, uygulama kodunun tamamını içerecektir. yapmadığımızı düşünün. Ancak bu süre içinde değişecek.

  3. Web paketi 4'te, optimization.splitChunks.chunks: 'all' seçeneğini ekleyin web paketi yapılandırmanıza ekleyin:

    // webpack.config.js (for webpack 4)
    module.exports = {
      optimization: {
        splitChunks: {
          chunks: 'all'
        }
      }
    };
    

    Bu seçenek, akıllı kod bölmeyi etkinleştirir. Bu öğeyle, webpack aşağıdaki durumlarda tedarikçi kodunu ayıklar: 30 kB'tan büyük olur (küçültme ve gzip'ten önce). Ayrıca, genel kodu da çıkarır. Bu, derlemeniz birkaç paket (ör. uygulamanızı rotalara ayırırsanız).

    Web paketi 3'te CommonsChunkPlugin etiketini ekleyin:

    // webpack.config.js (for webpack 3)
    module.exports = {
      plugins: [
        new webpack.optimize.CommonsChunkPlugin({
        // A name of the chunk that will include the dependencies.
        // This name is substituted in place of [name] from step 1
        name: 'vendor',
    
        // A function that determines which modules to include into this chunk
        minChunks: module => module.context && module.context.includes('node_modules'),
        })
      ]
    };
    

    Bu eklenti, yollarında node_modules ve bunları vendor.[chunkhash].js adlı ayrı bir dosyaya taşır.

Bu değişikliklerden sonra, her derleme bir yerine iki dosya oluşturur: main.[chunkhash].js ve vendor.[chunkhash].js (web paketi 4 için vendors~main.[chunkhash].js). Webpack 4'te ise bağımlılıklar küçükse tedarikçi paketi oluşturulmayabilir. Bu sorun değil:

$ webpack
Hash: ac01483e8fec1fa70676
Version: webpack 3.8.1
Time: 3816ms
                        Asset      Size  Chunks             Chunk Names
 ./main.00bab6fd3100008a42b0.js   82 kB       0  [emitted]  main
./vendor.d9e134771799ecdf9483.js  47 kB       1  [emitted]  vendor

Tarayıcı bu dosyaları ayrı olarak önbelleğe alır ve yalnızca değişen kodu yeniden indirir.

Webpack çalışma zamanı kodu

Maalesef yalnızca tedarikçi firma kodunu ayıklamak yeterli değildir. Örneğin uygulama kodunda bir şeyi değiştirin:

// index.js
…
…

// E.g. add this:
console.log('Wat');

vendor karmasının da değiştiğini görürsünüz:

                           Asset   Size  Chunks             Chunk Names
./vendor.d9e134771799ecdf9483.js  47 kB       1  [emitted]  vendor

                            Asset   Size  Chunks             Chunk Names
./vendor.e6ea4504d61a1cc1c60b.js  47 kB       1  [emitted]  vendor

Bunun nedeni, modüllerin kodlarından ayrı olarak web paketi paketinde bir çalışma zamanı – küçük bir kod parçasıdır tek bir yerden yönetin. Kodu birden fazla dosyaya böldüğünüzde parçası, parça kimlikleri ile değerleri ilgili dosyalar:

// vendor.e6ea4504d61a1cc1c60b.js
script.src = __webpack_require__.p + chunkId + "." + {
    "0": "2f2269c7f0a55a5c1871"
}[chunkId] + ".js";

Webpack, bu çalışma zamanını, vendor olan son oluşturulan parçaya ekler. bizim örneğimize dönelim. Bu kod parçası da her parça değiştiğinde, farklı bir kod tüm vendor yığınının değişmesine neden olur.

Bu sorunu çözmek için çalışma zamanını ayrı bir dosyaya taşıyalım. Webpack 4'te, optimization.runtimeChunk seçeneği etkinleştirildiğinde:

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

Web paketi 3'te,bunu CommonsChunkPlugin ile ekstra boş bir yığın oluşturarak yapın:

// webpack.config.js (for webpack 3)
module.exports = {
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: module => module.context && module.context.includes('node_modules')
    }),
    // This plugin must come after the vendor one (because webpack
    // includes runtime into the last chunk)
    new webpack.optimize.CommonsChunkPlugin({
      name: 'runtime',
      // minChunks: Infinity means that no app modules
      // will be included into this chunk
      minChunks: Infinity
    })
  ]
};

Bu değişikliklerden sonra, her derleme üç dosya oluşturacaktır:

$ webpack
Hash: ac01483e8fec1fa70676
Version: webpack 3.8.1
Time: 3816ms
                            Asset     Size  Chunks             Chunk Names
   ./main.00bab6fd3100008a42b0.js    82 kB       0  [emitted]  main
 ./vendor.26886caf15818fa82dfa.js    46 kB       1  [emitted]  vendor
./runtime.79f17c27b335abc7aaf4.js  1.45 kB       3  [emitted]  runtime

Bu adımları ters sırayla index.html öğesine ekleyin. Hepsi bu kadar:

<!-- index.html -->
<script src="./runtime.79f17c27b335abc7aaf4.js"></script>
<script src="./vendor.26886caf15818fa82dfa.js"></script>
<script src="./main.00bab6fd3100008a42b0.js"></script>

Daha fazla bilgi

Ekstra HTTP isteğini kaydetmek için satır içi web paketi çalışma zamanı

İşleri daha da iyi hale getirmek için web paketi çalışma zamanını HTML koduna satır içi olarak eklemeyi deneyin. tıklayın. Örneğin, bunun yerine:

<!-- index.html -->
<script src="./runtime.79f17c27b335abc7aaf4.js"></script>

şunu yap:

<!-- index.html -->
<script>
!function(e){function n(r){if(t[r])return t[r].exports;…}} ([]);
</script>

Çalışma zamanı küçüktür ve bunu satır içi yapmak bir HTTP isteğini kaydetmenize yardımcı olur (halihazırda HTTP/1 ile önemlidir; HTTP/2 ile daha az önemli olsa da etkisi).

Bunu nasıl yapacağınız aşağıda açıklanmıştır.

HTMLWebpackEklentileri ile HTML oluşturursanız

URL'yi HtmlWebpackPlugin'in bir HTML dosyası, InlineSourcePlugin tek ihtiyacınız olan şey:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const InlineSourcePlugin = require('html-webpack-inline-source-plugin');

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      inlineSource: 'runtime~.+\\.js',
    }),
    new InlineSourcePlugin()
  ]
};

Özel bir sunucu mantığı kullanarak HTML oluşturursanız

Webpack 4 ile:

  1. URL'yi WebpackManifestPlugin çalışma zamanı parçasının oluşturulan adını öğrenmek için aşağıdaki adımları uygulayın:

    // webpack.config.js (for webpack 4)
    const ManifestPlugin = require('webpack-manifest-plugin');
    
    module.exports = {
      plugins: [
        new ManifestPlugin()
      ]
    };
    

    Bu eklentiyle oluşturulan bir derleme, şuna benzeyen bir dosya oluşturur:

    // manifest.json
    {
      "runtime~main.js": "runtime~main.8e0d62a03.js"
    }
    
  2. Çalışma zamanı parçasının içeriğini uygun bir şekilde satır içine alın. Ör. Node.js ve Express ile:

    // server.js
    const fs = require('fs');
    const manifest = require('./manifest.json');
    const runtimeContent = fs.readFileSync(manifest['runtime~main.js'], 'utf-8');
    
    app.get('/', (req, res) => {
      res.send(`
        …
        <script>${runtimeContent}</script>
        …
      `);
    });
    

Veya webpack 3 ile:

  1. filename belirterek çalışma zamanı adını statik hale getirin:

    module.exports = {
      plugins: [
        new webpack.optimize.CommonsChunkPlugin({
          name: 'runtime',
          minChunks: Infinity,
          filename: 'runtime.js'
        })
      ]
    };
    
  2. runtime.js içeriğini uygun bir şekilde satır içine alın. Ör. Node.js ve Express ile:

    // server.js
    const fs = require('fs');
    const runtimeContent = fs.readFileSync('./runtime.js', 'utf-8');
    
    app.get('/', (req, res) => {
      res.send(`
        …
        <script>${runtimeContent}</script>
        …
      `);
    });
    

Şu anda ihtiyaç duymadığınız kodu geç yükleyin

Bazen bir sayfada daha fazla veya daha az önemli bölümler bulunur:

  • YouTube'da bir video sayfası yüklerseniz videoya, içeriğin yorum. Bu durumda video, yorumlardan daha önemlidir.
  • Bir haber sitesindeki bir makaleyi açtığınızda, o haberin içeriği hakkında daha çok reklamlardan daha iyi bir şekilde anlatacağım. Bu durumda metin reklamlardan daha önemlidir.

Bu gibi durumlarda, ilk yükleme performansını iyileştirmek için geri kalan bölümleri daha sonra geç yükleyebilirsiniz. import() işlevi ve code-splitting:

// videoPlayer.js
export function renderVideoPlayer() { … }

// comments.js
export function renderComments() { … }

// index.js
import {renderVideoPlayer} from './videoPlayer';
renderVideoPlayer();

// …Custom event listener
onShowCommentsClick(() => {
  import('./comments').then((comments) => {
    comments.renderComments();
  });
});

import(), belirli bir modülü dinamik olarak yüklemek istediğinizi belirtir. Zaman webpack import('./module.js') görür, bu modülü ayrı bir chunk:

$ webpack
Hash: 39b2a53cb4e73f0dc5b2
Version: webpack 3.8.1
Time: 4273ms
                            Asset     Size  Chunks             Chunk Names
      ./0.8ecaf182f5c85b7a8199.js  22.5 kB       0  [emitted]
   ./main.f7e53d8e13e9a2745d6d.js    60 kB       1  [emitted]  main
 ./vendor.4f14b6326a80f4752a98.js    46 kB       2  [emitted]  vendor
./runtime.79f17c27b335abc7aaf4.js  1.45 kB       3  [emitted]  runtime

ve yalnızca yürütme import() işlevine ulaştığında indirir.

Bu işlem, main paketini küçülterek ilk yükleme süresini iyileştirir. Daha da iyisi, önbelleğe almayı iyileştirir. Ana parçadaki kodu değiştirirseniz yorum yığını bundan etkilenmez.

Daha fazla bilgi

Kodu rotalara ve sayfalara ayırın

Uygulamanızın birden fazla yolu veya sayfası varsa ancak (tek bir main parçası) muhtemelen bir sonraki dikkat edin. Örneğin, bir kullanıcı sitenizin ana sayfasından birini ziyaret ettiğinde:

WebFundamentals ana sayfası

aynı web sitesindeki bir makaleyi oluşturmak için ancak sayfayı yüklerler. Ayrıca, kullanıcı her zaman sadece evi ziyaret ediyorsa ve makale kodunda bir değişiklik yaparsanız webpack, bütün uygulamayı indirecektir, ancak kullanıcının tüm uygulamayı yeniden indirmesi gerekecektir.

Uygulamayı sayfalara (veya tek sayfalık bir uygulamaysa rotalara) bölersek kullanıcı yalnızca ilgili kodu indirir. Ayrıca tarayıcı, uygulama kodunu önbelleğe alır. daha iyidir: ana sayfa kodunu değiştirirseniz webpack yalnızca parçasını oluşturur.

Tek sayfalık uygulamalar için

Tek sayfalık uygulamaları rotalara göre bölmek için import() kullanın (Geç yükleme kodu en az üç aylık reklam harcaması yapın." bölümünde bulabilirsiniz. Bir çerçeve kullanırsanız bunun için zaten bir çözümü olabilir:

Geleneksel çok sayfalı uygulamalar için

Geleneksel uygulamaları sayfalara göre ayırmak için webpack'in girişini kullanın. puan. Uygulamanızda üç tane Bunlar ana sayfa, makale sayfası ve kullanıcı hesabı sayfası gibi üç giriş olmalıdır:

// webpack.config.js
module.exports = {
  entry: {
    home: './src/Home/index.js',
    article: './src/Article/index.js',
    profile: './src/Profile/index.js'
  }
};

Webpack, her giriş dosyası için ayrı bir bağımlılık ağacı derler ve Yalnızca söz konusu girişin kullandığı modülleri içeren bir paket:

$ webpack
Hash: 318d7b8490a7382bf23b
Version: webpack 3.8.1
Time: 4273ms
                            Asset     Size  Chunks             Chunk Names
      ./0.8ecaf182f5c85b7a8199.js  22.5 kB       0  [emitted]
   ./home.91b9ed27366fe7e33d6a.js    18 kB       1  [emitted]  home
./article.87a128755b16ac3294fd.js    32 kB       2  [emitted]  article
./profile.de945dc02685f6166781.js    24 kB       3  [emitted]  profile
 ./vendor.4f14b6326a80f4752a98.js    46 kB       4  [emitted]  vendor
./runtime.318d7b8490a7382bf23b.js  1.45 kB       5  [emitted]  runtime

Bu nedenle, yalnızca makale sayfasında Lodash kullanıyorsa home ve profile paketleri yer almaz ve kullanıcının bu kitaplığı indirmesi gerekmez. ana sayfayı ziyaret edin.

Ayrı bağımlılık ağaçlarının dezavantajları vardır. İki giriş noktası Bağımlılıklarınızı bir tedarikçi paketine taşımadıysanız, puanlar Lodash'in bir kopyasını içerir. Bu sorunu çözmek için webpack 4'te optimization.splitChunks.chunks: 'all' seçeneğini web paketi yapılandırmanıza ekleyin:

// webpack.config.js (for webpack 4)
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  }
};

Bu seçenek, akıllı kod bölmeyi etkinleştirir. Bu seçenekle webpack otomatik olarak ortak kodu arayın ve bunu ayrı dosyalara çıkarın.

Alternatif olarak, web paketi 3'te CommonsChunkPlugin kullanın. - ortak bağımlılıkları belirtilen yeni bir dosyaya taşır:

module.exports = {
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'common',
      minChunks: 2    // 2 is the default value
    })
  ]
};

En iyi değeri bulmak için minChunks değeriyle oynayabilirsiniz. Genellikle, küçük tutmanız ama parçaların sayısı arttıkça artırın. Örneğin, Örneğin, 3 parça için minChunks 2, 30 parça için 8 olabilir Çünkü 2'de tutarsanız ortak dosyaya çok fazla modül eklenir, çok fazla şişirirsiniz.

Daha fazla bilgi

Modül kimliklerini daha kararlı hale getirin

Webpack, kod oluştururken her modüle bir kimlik atar. Daha sonra, bu kimlikler paketteki require() saniye içinde kullanılıyor. Genellikle derleme çıktısında kimlikler görürsünüz hemen önce:

$ webpack
Hash: df3474e4f76528e3bbc9
Version: webpack 3.8.1
Time: 2150ms
                           Asset      Size  Chunks             Chunk Names
      ./0.8ecaf182f5c85b7a8199.js  22.5 kB       0  [emitted]
   ./main.4e50a16675574df6a9e9.js    60 kB       1  [emitted]  main
 ./vendor.26886caf15818fa82dfa.js    46 kB       2  [emitted]  vendor
./runtime.79f17c27b335abc7aaf4.js  1.45 kB       3  [emitted]  runtime

↓ Burada

[0] ./index.js 29 kB {1} [built]
[2] (webpack)/buildin/global.js 488 bytes {2} [built]
[3] (webpack)/buildin/module.js 495 bytes {2} [built]
[4] ./comments.js 58 kB {0} [built]
[5] ./ads.js 74 kB {1} [built]
+ 1 hidden module

Varsayılan olarak, kimlikler bir sayaç kullanılarak hesaplanır (yani ilk modülün kimliği 0, ikincisinin kimliği 1'dir, vb.). Bu sorun, yeni bir kampanyaya üzerinde değişiklik yaptığınızda, modül listesinin ortasında görünebilir ve sonraki modüllerin Kimlikler:

$ webpack
Hash: df3474e4f76528e3bbc9
Version: webpack 3.8.1
Time: 2150ms
                           Asset      Size  Chunks             Chunk Names
      ./0.5c82c0f337fcb22672b5.js    22 kB       0  [emitted]
   ./main.0c8b617dfc40c2827ae3.js    82 kB       1  [emitted]  main
 ./vendor.26886caf15818fa82dfa.js    46 kB       2  [emitted]  vendor
./runtime.79f17c27b335abc7aaf4.js  1.45 kB       3  [emitted]  runtime
   [0] ./index.js 29 kB {1} [built]
   [2] (webpack)/buildin/global.js 488 bytes {2} [built]
   [3] (webpack)/buildin/module.js 495 bytes {2} [built]

↓ Sekmemize yeni bir modül...

[4] ./webPlayer.js 24 kB {1} [built]

↓ Ve neler yaptığına bakın! comments.js artık 4 yerine 5 kimliğine sahip

[5] ./comments.js 58 kB {0} [built]

ads.js artık 5 yerine 6 kimliğine sahip

[6] ./ads.js 74 kB {1} [built]
       + 1 hidden module

Bu işlem, değişen kimlikleri olan modülleri içeren veya bunlara bağlı olan tüm parçaları geçersiz kılar: korumayı sağlar. Örneğimizde 0 parçası (parça) comments.js ile birlikte) ve main parçası (diğer uygulama koduyla yığın) geçersiz kılınmıştır. Yalnızca main için geçerli olması gerekirdi.

Bu sorunu çözmek için modül kimliklerinin HashedModuleIdsPlugin. Sayaca dayalı kimlikleri, modül yollarının karmalarıyla değiştirir:

$ webpack
Hash: df3474e4f76528e3bbc9
Version: webpack 3.8.1
Time: 2150ms
                           Asset      Size  Chunks             Chunk Names
      ./0.6168aaac8461862eab7a.js  22.5 kB       0  [emitted]
   ./main.a2e49a279552980e3b91.js    60 kB       1  [emitted]  main
 ./vendor.ff9f7ea865884e6a84c8.js    46 kB       2  [emitted]  vendor
./runtime.25f5d0204e4f77fa57a1.js  1.45 kB       3  [emitted]  runtime

↓ Burada

[3IRH] ./index.js 29 kB {1} [built]
[DuR2] (webpack)/buildin/global.js 488 bytes {2} [built]
[JkW7] (webpack)/buildin/module.js 495 bytes {2} [built]
[LbCc] ./webPlayer.js 24 kB {1} [built]
[lebJ] ./comments.js 58 kB {0} [built]
[02Tr] ./ads.js 74 kB {1} [built]
    + 1 hidden module

Bu yaklaşımda, bir modülün kimliği yalnızca söz konusu modülü yeniden adlandırırsanız veya taşırsanız modülünü kullanabilirsiniz. Yeni modüller diğer modüllerin çalışmasını etkilemeyecektir Kimlikler.

Etkinleştirmek için yapılandırmanın plugins bölümüne ekleyin:

// webpack.config.js
module.exports = {
  plugins: [
    new webpack.HashedModuleIdsPlugin()
  ]
};

Daha fazla bilgi

Özet

  • Paketi önbelleğe alın ve paket adını değiştirerek sürümleri birbirinden ayırt edin
  • Paketi uygulama kodu, tedarikçi firma kodu ve çalışma zamanına böl
  • HTTP isteğini kaydetmek için çalışma zamanını satır içine alma
  • Kritik olmayan kodu import ile geç yükleyin
  • Gereksiz öğeler yüklenmesini önlemek için kodu rotalara/sayfalara göre bölün