ใช้ประโยชน์จากการแคชในระยะยาว

Webpack ช่วยแคชเนื้อหาได้อย่างไร

ขั้นตอนถัดไป (หลังจากการเพิ่มประสิทธิภาพขนาดแอปที่ ปรับปรุงเวลาที่ใช้ในการโหลดแอปคือการแคช ใช้เพื่อทำให้ส่วนต่างๆ ของแอปอยู่บน และหลีกเลี่ยงการดาวน์โหลดซ้ำทุกครั้ง

ใช้การกำหนดเวอร์ชัน Bundle และส่วนหัวแคช

วิธีการทั่วไปในการแคชคือ

  1. กำหนดให้เบราว์เซอร์แคชไฟล์เป็นเวลานานมาก (เช่น 1 ปี):

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

    หากไม่คุ้นเคยกับสิ่งที่ Cache-Control ทำ โปรดดูเหตุการณ์ของ Jake Archibald โพสต์ที่ยอดเยี่ยม เกี่ยวกับการแคชที่ดีที่สุด แนวทางปฏิบัติที่ดีที่สุด

  2. และเปลี่ยนชื่อไฟล์เมื่อถูกเปลี่ยนเพื่อบังคับให้ดาวน์โหลดใหม่

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

วิธีนี้จะทำให้เบราว์เซอร์ดาวน์โหลดไฟล์ JS แคช และใช้ สำเนาที่แคชไว้ เบราว์เซอร์จะเชื่อมต่อกับเครือข่ายก็ต่อเมื่อชื่อไฟล์มีการเปลี่ยนแปลงเท่านั้น (หรือหากผ่านไป 1 ปีแล้ว)

คุณก็ทำเช่นเดียวกันใน Webpack แต่จะระบุหมายเลขเวอร์ชันแทนหมายเลขเวอร์ชัน แฮชไฟล์ ในการรวมแฮชในชื่อไฟล์ ให้ใช้ [chunkhash]:

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

หากคุณต้องการ ส่งไฟล์ไปยังไคลเอ็นต์ ให้ใช้ HtmlWebpackPlugin หรือ WebpackManifestPlugin

HtmlWebpackPlugin เป็น เรียบง่าย แต่มีความยืดหยุ่นน้อยกว่า ในระหว่างการคอมไพล์ ปลั๊กอินนี้จะสร้าง ไฟล์ HTML ที่มีทรัพยากรที่คอมไพล์ทั้งหมด หากตรรกะของเซิร์ฟเวอร์ไม่ตรงกัน ซับซ้อน คุณควรจะเพียงพอแล้ว

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

WebpackManifestPlugin เป็นวิธีที่ยืดหยุ่นกว่า ซึ่งมีประโยชน์หากคุณมีส่วนของเซิร์ฟเวอร์ที่ซับซ้อน ระหว่างบิลด์ ระบบจะสร้างไฟล์ JSON ที่มีการแมประหว่างชื่อไฟล์ ไม่มีแฮช และชื่อไฟล์มีแฮช ใช้ JSON นี้บนเซิร์ฟเวอร์เพื่อหาคำตอบ ไฟล์ที่ควรใช้กับ

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

อ่านเพิ่มเติม

แยกทรัพยากร Dependency และรันไทม์ออกเป็นไฟล์แยกต่างหาก

การอ้างอิง

ทรัพยากร Dependency ของแอปมีแนวโน้มที่จะเปลี่ยนแปลงน้อยกว่าโค้ดของแอปจริง หากคุณย้าย แยกให้เป็นไฟล์แยกต่างหาก เบราว์เซอร์จะสามารถแคชข้อมูลแยกกันได้ – และจะไม่ดาวน์โหลดแอปซ้ำทุกครั้งที่โค้ดของแอปเปลี่ยนแปลง

หากต้องการแยกทรัพยากร Dependency แยกเป็นกลุ่มย่อย ให้ทำ 3 ขั้นตอนดังนี้

  1. แทนที่ชื่อไฟล์เอาต์พุตด้วย [name].[chunkname].js ดังนี้

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

    เมื่อ Webpack สร้างแอป แอปจะแทนที่ [name] ด้วยชื่อของกลุ่ม หากเราไม่เพิ่มส่วน [name] เราจะ แยกความแตกต่างระหว่างส่วนเล็กๆ ด้วยแฮช ซึ่งเป็นเรื่องที่ค่อนข้างยาก!

  2. แปลงช่อง entry เป็นออบเจ็กต์

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

    ในข้อมูลโค้ดนี้ "main" เป็นชื่อของกลุ่ม ชื่อนี้จะถูกแทนที่ใน สถานที่ของ [name] จากขั้นตอนที่ 1

    ในปัจจุบัน ถ้าคุณสร้างแอป กลุ่มนี้ประกอบด้วยโค้ดทั้งหมดของแอป เหมือนที่เรายังไม่ได้ทำตามขั้นตอนเหล่านี้ แต่การดำเนินการนี้จะเปลี่ยนไปภายในไม่กี่วินาที

  3. ใน Webpack 4 ให้เพิ่มตัวเลือก optimization.splitChunks.chunks: 'all' ลงในการกำหนดค่า Webpack ดังนี้

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

    ตัวเลือกนี้จะเปิดใช้การแยกโค้ดอัจฉริยะ เมื่อใช้แพลตฟอร์มนี้ Webpack จะแยกรหัสของผู้ให้บริการออกมา ไฟล์มีขนาดใหญ่กว่า 30 kB (ก่อนการลดขนาดและ gzip) และยังดึงโค้ดทั่วไปออกมา วิธีนี้มีประโยชน์หากบิลด์ของคุณสร้างแพ็กเกจหลายรายการ (เช่น หากคุณแยกแอปออกเป็นเส้นทาง)

    ใน Webpack 3 ให้เพิ่มแท็ก CommonsChunkPlugin:

    // 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'),
        })
      ]
    };
    

    ปลั๊กอินนี้ใช้โมดูลทั้งหมดในเส้นทางที่มี node_modules และ ย้ายไปยังไฟล์แยกต่างหากที่ชื่อว่า vendor.[chunkhash].js

หลังจากการเปลี่ยนแปลงเหล่านี้ แต่ละบิลด์จะสร้างไฟล์ 2 ไฟล์แทนที่จะเป็นไฟล์เดียว ได้แก่ main.[chunkhash].js และ vendor.[chunkhash].js (vendors~main.[chunkhash].js สำหรับ Webpack 4) ในกรณีของ Webpack 4 ระบบอาจไม่สร้างกลุ่มผู้ให้บริการหากทรัพยากร Dependency มีขนาดเล็ก ซึ่งไม่มีปัญหา

$ 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

เบราว์เซอร์จะแคชไฟล์เหล่านี้แยกต่างหาก และดาวน์โหลดเฉพาะโค้ดที่เปลี่ยนแปลงไปเท่านั้น

รหัสรันไทม์ของ Webpack

ขออภัย การดึงข้อมูลเฉพาะโค้ดผู้ให้บริการนั้นไม่เพียงพอ หากคุณพยายาม เปลี่ยนแปลงบางอย่างในโค้ดของแอป

// index.js
…
…

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

คุณจะสังเกตเห็นว่าแฮช vendor จะเปลี่ยนไปด้วย

                           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

ซึ่งเกิดขึ้นเนื่องจาก Bundle ของ Webpack นอกเหนือจากโค้ดของโมดูลแล้วจะมี a รันไทม์ – โค้ดสั้นๆ ที่จัดการการดำเนินการโมดูล เมื่อแบ่งโค้ดออกเป็นหลายๆ ไฟล์ โค้ดชิ้นนี้จะเริ่มรวมถึงการจับคู่ระหว่างรหัสกลุ่มกับ ไฟล์ที่เกี่ยวข้อง:

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

Webpack จะรวมรันไทม์นี้ไปยังกลุ่มที่สร้างล่าสุด ซึ่งก็คือ vendor ในกรณีของเรา และทุกครั้งที่กลุ่มมีการเปลี่ยนแปลง โค้ดนี้ก็จะเปลี่ยนไปด้วย ทำให้กลุ่ม vendor ทั้งหมดเปลี่ยนไป

เราจะย้ายรันไทม์ไปไว้ในไฟล์แยกต่างหากเพื่อแก้ไขปัญหานี้ ใน Webpack 4 นี่คือ ด้วยการเปิดใช้ตัวเลือก optimization.runtimeChunk

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

ใน Webpack 3 ให้ทำโดยการสร้างกลุ่มเปล่าเพิ่มเติมด้วย CommonsChunkPlugin:

// 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
    })
  ]
};

หลังจากการเปลี่ยนแปลงเหล่านี้ แต่ละบิลด์จะสร้างไฟล์ 3 ไฟล์ ได้แก่

$ 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

รวมเมตริกดังกล่าวไว้ใน index.html ตามลำดับ เพียงเท่านี้ก็เสร็จแล้ว

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

อ่านเพิ่มเติม

รันไทม์ของ Webpack ในบรรทัดเพื่อบันทึกคำขอ HTTP เพิ่มเติม

เพื่อให้งานดียิ่งขึ้น ให้ลองใส่รันไทม์ของ Webpack ไว้ใน HTML คำตอบ กล่าวคือ แทนที่จะเป็น

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

ทำสิ่งนี้

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

รันไทม์มีขนาดเล็ก และการแทรกไว้จะช่วยคุณบันทึกคำขอ HTTP ( สำคัญกับ HTTP/1 ไม่สำคัญกับ HTTP/2 แต่อาจยังมี )

มาดูวิธีกัน

หากคุณสร้าง HTML ด้วย HtmlWebpackPlugin

หากคุณใช้แท็ก HtmlWebpackPlugin เพื่อสร้าง ไฟล์ HTML InlineSourcePlugin คือสิ่งที่คุณต้องมี:

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

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

หากคุณสร้าง HTML โดยใช้ตรรกะเซิร์ฟเวอร์ที่กำหนดเอง

เมื่อใช้ Webpack 4

  1. เพิ่ม WebpackManifestPlugin เพื่อทราบชื่อที่สร้างขึ้นของกลุ่มรันไทม์

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

    บิลด์ที่มีปลั๊กอินนี้จะสร้างไฟล์ที่มีหน้าตาดังนี้

    // manifest.json
    {
      "runtime~main.js": "runtime~main.8e0d62a03.js"
    }
    
  2. แทรกเนื้อหาของกลุ่มรันไทม์ในลักษณะที่สะดวก เช่น ด้วย Node.js และ Express

    // 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>
        …
      `);
    });
    

หรือ Webpack 3:

  1. ทำให้ชื่อรันไทม์คงที่โดยการระบุ filename

    module.exports = {
      plugins: [
        new webpack.optimize.CommonsChunkPlugin({
          name: 'runtime',
          minChunks: Infinity,
          filename: 'runtime.js'
        })
      ]
    };
    
  2. แทรกเนื้อหา runtime.js ไว้ในหน้าด้วยวิธีที่สะดวก เช่น ด้วย Node.js และ Express

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

โค้ดแบบ Lazy Loading ที่คุณไม่จำเป็นต้องใช้ในตอนนี้

บางครั้งหน้าเว็บอาจมีส่วนสำคัญมากหรือน้อยดังต่อไปนี้

  • หากคุณโหลดหน้าวิดีโอใน YouTube คุณจะให้ความสำคัญกับวิดีโอมากกว่า ความคิดเห็น ในวิดีโอนี้ วิดีโอมีความสำคัญมากกว่าความคิดเห็น
  • ถ้าคุณเปิดบทความในเว็บไซต์ข่าว คุณจะให้ความสำคัญกับข้อความ บทความมากกว่าเรื่องโฆษณา ตรงนี้ข้อความสำคัญกว่าโฆษณา

ในกรณีดังกล่าว ให้ปรับปรุงประสิทธิภาพการโหลดเริ่มต้นโดยการดาวน์โหลดเฉพาะ สิ่งสำคัญที่สุดก่อน แล้วค่อยโหลดส่วนที่เหลือทีหลัง ใช้เมธอด import()ฟังก์ชัน และ 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() ระบุว่าคุณต้องการโหลดโมดูลที่เฉพาะเจาะจงแบบไดนามิก วันและเวลา Webpack เห็น import('./module.js') จึงย้ายโมดูลนี้ไปยัง 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

และดาวน์โหลดเมื่อการดำเนินการถึงฟังก์ชัน import() เท่านั้น

การดำเนินการนี้จะทำให้แพ็กเกจ main เล็กลง ซึ่งจะลดเวลาที่ใช้ในการโหลดในช่วงแรก ยิ่งไปกว่านั้น การแคชก็ช่วยปรับปรุงการแคชได้ กล่าวคือหากคุณเปลี่ยนโค้ดในส่วนหลัก กลุ่มความคิดเห็นจะไม่ได้รับผลกระทบ

อ่านเพิ่มเติม

แยกโค้ดออกเป็นเส้นทางและหน้าเว็บ

หากแอปมีหลายเส้นทางหรือหน้าเว็บ แต่มีไฟล์ JS ไฟล์เดียวที่มี โค้ด (กลุ่ม main กลุ่มเดียว) เป็นไปได้ว่าคุณกำลังแสดงไบต์เพิ่มเติมกับ คำขอแต่ละรายการ ตัวอย่างเช่น เมื่อผู้ใช้เข้าชมหน้าแรกของเว็บไซต์

หน้าแรกของ WebFundamentals

ลูกค้าไม่จำเป็นต้องโหลดโค้ดเพื่อแสดงบทความที่อยู่ใน แต่หน้าเว็บจะโหลดขึ้นมา นอกจากนี้ หากผู้ใช้เข้าชมเฉพาะบ้านเสมอ และคุณทำการเปลี่ยนแปลงโค้ดบทความ Webpack จะทำให้ ทั้งชุด และผู้ใช้ต้องดาวน์โหลดแอปทั้งหมดใหม่อีกครั้ง

หากเราแบ่งแอปออกเป็นหลายหน้า (หรือเส้นทาง ถ้าเป็นแอปหน้าเดียว) ผู้ใช้ จะดาวน์โหลดเฉพาะโค้ดที่เกี่ยวข้องเท่านั้น นอกจากนี้ เบราว์เซอร์จะแคชโค้ดของแอป ที่ดีกว่า นั่นคือ หากคุณเปลี่ยนรหัสหน้าแรก Webpack จะทำให้เฉพาะ กลุ่มที่เกี่ยวข้อง

สำหรับแอปแบบหน้าเดียว

หากต้องการแยกแอปหน้าเว็บเดียวตามเส้นทาง ให้ใช้ import() (โปรดดู "โค้ดการโหลดแบบ Lazy Loading ที่คุณไม่ต้องการใช้ในขณะนี้") หากใช้เฟรมเวิร์ก อาจมีโซลูชันอยู่แล้วในกรณีนี้

สำหรับแอปที่มีหลายหน้าแบบดั้งเดิม

หากต้องการแยกแอปแบบดั้งเดิมตามหน้า ให้ใช้รายการของ Webpack คะแนน ถ้าแอปมี ได้แก่ หน้าแรก หน้าบทความ และหน้าบัญชีผู้ใช้ ควรมีข้อมูล 3 รายการดังนี้

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

Webpack จะสร้างโครงสร้างทรัพยากร Dependency แยกต่างหากสำหรับไฟล์รายการแต่ละไฟล์ ชุดที่มีเฉพาะโมดูลที่รายการนั้นใช้

$ 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

ดังนั้น หากหน้าบทความเท่านั้นที่ใช้ Lodash แล้ว ชุดข้อมูล home และ profile จะไม่รวมอยู่ด้วย และผู้ใช้ไม่ต้องดาวน์โหลดไลบรารีนี้เมื่อ เข้าชมหน้าแรก

แต่ต้นไม้ที่ต้องพึ่งพาแยกกันก็มีข้อเสียอยู่ หากจุดแรกเข้า 2 จุดใช้ Lodash และคุณไม่ได้ย้ายทรัพยากร Dependency ไปไว้ในแพ็กเกจผู้ให้บริการทั้ง 2 รายการ คะแนนจะรวมสำเนาของ Lodash ในการแก้ปัญหานี้ ใน Webpack 4 ให้เพิ่มเมธอด ตัวเลือก optimization.splitChunks.chunks: 'all' ในการกำหนดค่า Webpack ของคุณ

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

ตัวเลือกนี้จะเปิดใช้การแยกโค้ดอัจฉริยะ เมื่อใช้ตัวเลือกนี้ Webpack จะทำงาน มองหาโค้ดทั่วไปและแยกออกมาเป็นไฟล์แยกต่างหาก

หรือใน Webpack 3 ให้ใช้ CommonsChunkPlugin – จะย้ายทรัพยากร Dependency ทั่วไปไปยังไฟล์ที่ระบุใหม่ ดังนี้

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

ลองใช้ค่า minChunks เพื่อหาค่าที่ดีที่สุดได้เลย โดยทั่วไป คุณต้องการทำให้รูปย่อยๆ ไม่ใหญ่ แต่เพิ่มจำนวนขึ้นถ้าจำนวนไฟล์มีมากขึ้น สำหรับ เช่น สำหรับ 3 กลุ่ม minChunks อาจเป็น 2 แต่สำหรับ 30 กลุ่มอาจเป็น 8 – เพราะหากคุณคงระดับที่ 2 จะทำให้มีโมดูลที่มากเกินไปในไฟล์ทั่วไป พองเกินไป

อ่านเพิ่มเติม

ทำให้รหัสโมดูลเสถียรขึ้น

เมื่อสร้างโค้ด Webpack จะกำหนดรหัสให้กับแต่ละโมดูล หลังจากนั้น รหัสเหล่านี้จะ ที่ใช้ไปใน require() วินาทีภายในแพ็กเกจ โดยปกติคุณจะเห็นรหัสในเอาต์พุตของบิลด์ ก่อนเส้นทางโมดูล

$ 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

↓ ที่นี่

[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

โดยค่าเริ่มต้น ระบบจะคำนวณรหัสโดยใช้ตัวนับ (เช่น โมดูลแรกมีรหัส 0 อันที่ 2 จะมีรหัส 1 เป็นต้น) ปัญหาก็คือ เมื่อคุณเพิ่ม โมดูลใหม่ โฆษณาอาจปรากฏที่กลางรายการโมดูล โดยเปลี่ยน โมดูลถัดไป รหัส:

$ 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]

↓ เราได้เพิ่ม โมดูล...

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

↓ แล้วมาดูสิ่งที่ทำกันเลย! ตอนนี้ comments.js มีรหัส 5 แทนที่จะเป็น 4

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

ads.js ตอนนี้มีรหัส 6 จากเดิม 5 รายการ

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

การดำเนินการนี้จะทำให้กลุ่มทั้งหมดที่มีหรือขึ้นอยู่กับโมดูลที่มีรหัสมีการเปลี่ยนแปลง เช่น แม้ว่าโค้ดจริง จะไม่มีการเปลี่ยนแปลงก็ตาม ในกรณีนี้ กลุ่ม 0 (กลุ่ม ด้วย comments.js) และ main กลุ่ม (กลุ่มที่มีรหัสแอปอื่น) จะได้รับ ใช้ไม่ได้ ขณะที่มีเพียง main เท่านั้น

หากต้องการแก้ไขปัญหานี้ ให้เปลี่ยนวิธีคำนวณรหัสโมดูลโดยใช้ HashedModuleIdsPlugin โดยแทนที่รหัสแบบอิงตามตัวนับด้วยแฮชของเส้นทางโมดูล ดังนี้

$ 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

↓ ที่นี่

[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

วิธีนี้จะทำให้รหัสของโมดูลเปลี่ยนแปลงก็ต่อเมื่อคุณเปลี่ยนชื่อหรือย้ายโมดูลเท่านั้น โมดูลใหม่จะไม่ส่งผลกระทบต่อโมดูลอื่นๆ รหัส

หากต้องการเปิดใช้ปลั๊กอิน ให้เพิ่มลงในส่วน plugins ของการกำหนดค่า ดังนี้

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

อ่านเพิ่มเติม

สรุป

  • แคชชุดและแยกความแตกต่างระหว่างเวอร์ชันด้วยการเปลี่ยนชื่อ Bundle
  • แยกแพ็กเกจออกเป็นโค้ดของแอป โค้ดของผู้ให้บริการ และรันไทม์
  • แทรกรันไทม์เพื่อบันทึกคำขอ HTTP
  • โหลดโค้ดที่ไม่สำคัญแบบ Lazy Loading ด้วย import
  • แยกโค้ดตามเส้นทาง/หน้าเพื่อหลีกเลี่ยงการโหลดรายการที่ไม่จำเป็น