Shimming: แก่นแท้ แนวทาง และหลักการทำงาน

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-05-19 เวลาอ่าน: 8 นาที

Shimming เป็นเทคนิคเพื่อให้มั่นใจถึงความเข้ากันได้ของโมดูลที่คาดหวังตัวแปรทั่วโลกหรือ API เฉพาะบางอย่าง ในระบบนิเวศของ Webpack นั้น shimming ถูกนำมาใช้ผ่าน ProvidePlugin, imports-loader และ exports-loader ซึ่งช่วยให้เชื่อมต่อไลบรารี legacy ได้โดยไม่ต้องแก้ไขซอร์สโค้ดของมัน ตาม Webpack Documentation (2026) นั้น shimming ยังคงเป็นเครื่องมือสำคัญสำหรับการรวมปลั๊กอิน jQuery และการพึ่งพาอื่นๆ ที่ไม่รองรับระบบโมดูล

สิ่งสำคัญ

  • Shimming เป็นเทคนิคการแทนที่ตัวแปรทั่วโลกและ API เพื่อให้มั่นใจถึงความเข้ากันได้ของโมดูลในการบิลด์
  • ProvidePlugin จะนำเข้าโมดูลโดยอัตโนมัติเมื่อตรวจพบการอ้างอิงถึงตัวแปรทั่วโลกในโค้ด
  • imports-loader และ exports-loader จัดการขอบเขตของโมดูล โดยการเพิ่มหรือแก้ไขอินเทอร์เฟซของมัน
  • Shim ต่างจาก polyfill ตรงที่ไม่ได้ใช้งานฟังก์ชันที่ขาดหายไป แต่เปลี่ยนเส้นทางคำเรียกที่มีอยู่
  • Webpack ให้กลไก shimming ในตัวโดยไม่จำเป็นต้องติดตั้งแพ็กเกจเพิ่มเติม

Shimming คืออะไร?

Shimming เป็นเทคนิคซอฟต์แวร์ที่ฝังชั้นความเข้ากันได้ระหว่างโค้ดกับสภาพแวดล้อมโดยไม่แก้ไขซอร์สโค้ดของโมดูล ในบริบทของการบิลด์ JavaScript นั้น shimming แก้ปัญหาเมื่อโมดูลเข้าถึงตัวแปรทั่วโลก (window.$, global.process) ที่ไม่มีอยู่ในสภาพแวดล้อมแบบโมดูล

Shim และ polyfill: ความแตกต่างหลัก

Polyfill ใช้งานฟังก์ชันที่ขาดหายไปตั้งแต่เริ่มต้น โดยเพิ่มความสามารถใหม่ให้กับสภาพแวดล้อม ตัวอย่างเช่น core-js เพิ่ม Array.prototype.flatMap สำหรับเบราว์เซอร์เก่า Shim ในทางกลับกันจะเปลี่ยนเส้นทางคำเรียกที่มีอยู่ไปยังการใช้งานที่มีอยู่ หรือแทนที่ออบเจ็กต์ทั่วโลกที่คาดหวัง ใน Webpack นั้น ProvidePlugin จะแทรก import $ from 'jquery' โดยอัตโนมัติทุกที่ที่พบการอ้างอิงถึงตัวแปรทั่วโลก $ โดยไม่ต้องแก้ไขโค้ด

ความแตกต่างหลักอยู่ที่เป้าหมาย Polyfill เพิ่มสิ่งที่ไม่เคยมี ในขณะที่ shim ทำให้โค้ดที่มีอยู่เข้ากันได้กับสภาพแวดล้อมที่มันทำงาน การเลือกระหว่างสองแบบนี้ขึ้นอยู่กับปัญหาที่กำลังแก้: การไม่มี API หรือความไม่เข้ากันของอินเทอร์เฟซ

Shimming ทำงานอย่างไรใน Webpack

Webpack จัดการแต่ละโมดูลเป็นหน่วยที่แยกจากกันโดยมีขอบเขตของตัวเอง หากไลบรารีอ้างอิงตัวแปรทั่วโลก jQuery ในรูปแบบ window.$ การบิลด์จะจบลงด้วยข้อผิดพลาด เนื่องจากตัวแปรนี้ไม่มีอยู่ในบริบทของโมดูล ProvidePlugin แก้ปัญหาในขั้นตอนการคอมไพล์: เมื่อตรวจพบตัวระบุ $ ในโค้ด ปลั๊กอินจะแทรก import $ from 'jquery' ลงในตอนต้นของไฟล์โดยอัตโนมัติ

js
// ซอร์สโค้ด (โมดูล legacy เข้าถึง jQuery ทั่วโลก)
$('.element').hide();

// หลังจากการประมวลผลของ ProvidePlugin (Webpack แทรก import)
import $ from 'jquery';
$('.element').hide();

นอกจากนี้ imports-loader ยังช่วยให้ระบุได้อย่างชัดเจนว่าโมดูลควรได้รับข้อมูลพึ่งพาใดบ้าง สิ่งนี้มีประโยชน์เมื่อไลบรารีใช้ this ในระดับบนสุด โดยคาดหวังว่า this จะอ้างถึง window แทนที่จะเป็น module.exports

ProvidePlugin: ตัวแปรทั่วโลกสำหรับโมดูล

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

คอนฟิกูเรชันของปลั๊กอิน

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

module.exports = {
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
      _: 'lodash',
      'window.$': 'jquery',
    }),
  ],
};

ProvidePlugin รองรับการนำเข้าเฉพาะจุดผ่านไวยากรณ์อาร์เรย์ ตัวอย่างเช่น [lodash, debounce] นำเข้าเฉพาะฟังก์ชัน debounce จาก lodash ซึ่งช่วยลดขนาดของบันเดิลสุดท้าย สิ่งนี้สำคัญเป็นพิเศษสำหรับโปรเจกต์มือถือ ที่ทุกกิโลไบต์ส่งผลต่อเวลาโหลด

imports-loader และ exports-loader

imports-loader เพิ่มการนำเข้าที่จำเป็นลงในตอนต้นของโมดูล ในขณะที่ exports-loader กำหนดค่าที่ส่งออกสำหรับโมดูลที่ไม่ใช้ module.exports อย่างชัดเจน โลเดอร์เหล่านี้ทำงานในระดับไฟล์แต่ละไฟล์ ไม่ใช่ทั่วโลกเหมือน ProvidePlugin

การแก้ไขข้อมูลพึ่งพาด้วย imports-loader

js
// webpack.config.js — การตั้งค่า imports-loader
module.exports = {
  module: {
    rules: [
      {
        test: /legacy-module\.js$/,
        use: [
          {
            loader: 'imports-loader',
            options: {
              imports: [
                'jquery',
                '$',
              ],
            },
          },
        ],
      },
    ],
  },
};

exports-loader ใช้เมื่อไลบรารีกำหนดค่าให้กับตัวแปรทั่วโลก แต่ไม่ส่งออกค่าผ่านระบบโมดูล โลเดอร์จะดึงค่าออกมาและเปลี่ยนให้เป็นการส่งออกแบบโมดูล ทำให้โมดูลอื่นสามารถนำเข้ามันผ่าน import ได้

การตั้งค่า shimming ในคอนฟิกูเรชันของ Webpack

Shimming ถูกตั้งค่าใน webpack.config.js ผ่านการผสมผสานของปลั๊กอินและโลเดอร์ สถานการณ์ทั่วไปประกอบด้วย ProvidePlugin สำหรับตัวแปรทั่วโลก และ imports-loader สำหรับโมดูลเฉพาะที่ต้องการการเปลี่ยนแปลงขอบเขต

คอนฟิกูเรชันพื้นฐานของ Webpack สำหรับ shimming

js
const webpack = require('webpack');
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    globalObject: 'this',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules\/(?!legacy-lib)/,
        use: [
          {
            loader: 'imports-loader',
            options: {
              type: 'commonjs',
              imports: ['jquery', '$'],
            },
          },
        ],
      },
    ],
  },
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
    }),
  ],
};

ฟิลด์ globalObject ใน output กำหนดบริบทสำหรับการอ้างอิง this ในระดับบนสุด สำหรับสภาพแวดล้อมเบราว์เซอร์ ค่า 'this' อ้างถึง window ในขณะที่สำหรับ React Native หรือ Node.js จะอ้างถึง global การเลือกค่าที่ถูกต้องจะป้องกันข้อผิดพลาดในการรันไทม์ในสภาพแวดล้อมเป้าหมาย

ข้อผิดพลาดทั่วไปในการทำ shimming

Shimming เป็นเครื่องมือที่ทรงพลังแต่ก็อันตราย การตั้งค่าที่ผิดนำไปสู่การทำซ้ำโค้ดในบันเดิล ความขัดแย้งของชื่อ และข้อผิดพลาดการรันไทม์ที่ไม่คาดคิด นักพัฒนามักลืมว่า ProvidePlugin ทำงานในขั้นตอนการคอมไพล์และไม่สามารถจัดการการอ้างอิงตัวแปรแบบไดนามิกได้

ความขัดแย้งของตัวแปรทั่วโลก

หากปลั๊กอินสองตัวใช้ jQuery คนละเวอร์ชัน ProvidePlugin จะแทนที่เพียงตัวเดียวเท่านั้น คือตัวที่ระบุไว้ก่อนในคอนฟิกูเรชัน ไลบรารีที่สองจะได้รับเวอร์ชันที่ไม่เข้ากัน ซึ่งทำให้เกิดข้อผิดพลาดที่แก้ไขยาก วิธีแก้ — ใช้ exports-loader สำหรับแต่ละไลบรารีพร้อมระบุเวอร์ชันอย่างชัดเจน หรือใช้ webpack.IgnorePlugin เพื่อแยกโมดูลที่ซ้ำกัน

ข้อผิดพลาดทั่วไปอีกอย่างหนึ่งคือการพยายาม shim โมดูลที่ใช้คำเรียก require แบบซิงโครนัสของ CommonJS ในบริบทแบบไดนามิก ProvidePlugin ประมวลผลเฉพาะตัวระบุแบบคงที่เท่านั้น ดังนั้นการอ้างอิงแบบไดนามิกจึงต้องถูกแทนที่ด้วยตนเองหรือใช้ NormalModuleReplacementPlugin

ปัญหาด้านประสิทธิภาพเมื่อทำ shimming ผิดวิธี

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

สำหรับการวินิจฉัยปัญหากับ shimming ให้ใช้ webpack-bundle-analyzer — เครื่องมือสำหรับแสดงภาพองค์ประกอบของบันเดิล หาก jQuery หรือไลบรารีอื่นปรากฏในบันเดิลหลายครั้ง แสดงว่าเวอร์ชันที่ต่างกันกำลังขัดแย้งกัน หรือ ProvidePlugin ถูกตั้งค่าสำหรับตัวระบุหลายตัวที่นำไปสู่เวอร์ชันแพ็กเกจที่ต่างกัน วิธีแก้ — รวมเวอร์ชันข้อมูลพึ่งพาให้เป็นหนึ่งผ่าน resolve.alias และตรวจสอบว่าตัวระบุที่ shim ทั้งหมดชี้ไปยังโมดูลเดียวกัน

ทางเลือกแทน shimming: การปรับโครงสร้างและการอัปเดตข้อมูลพึ่งพา

ก่อนที่จะใช้ shimming ให้ประเมินความเป็นไปได้ในการอัปเดตไลบรารีเป็นเวอร์ชันที่รองรับระบบโมดูล แพ็กเกจ legacy จำนวนมากมีทางเลือกสมัยใหม่ที่ไม่ต้องใช้ shimming ตัวอย่างเช่น ปลั๊กอิน jQuery สามารถแทนที่ด้วย API เนทีฟของเบราว์เซอร์: $.ajaxfetch, $.eachArray.forEach การปรับโครงสร้างให้ผลประโยชน์ระยะยาวในการบำรุงรักษา ในขณะที่ shimming เป็นวิธีแก้ไขชั่วคราวที่ทำให้คอนฟิกูเรชันซับซ้อนขึ้น

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

Shimming ใน JavaScript สมัยใหม่: ESM และ import maps

ด้วยการพัฒนาของ ES โมดูล เนทีฟในเบราว์เซอร์และการถือกำเนิดของ import maps บางสถานการณ์ของ shimming สามารถแก้ได้โดยไม่ต้องใช้ Webpack Import maps ช่วยให้กำหนดชื่อโมดูลใหม่ได้ทันทีในระดับเบราว์เซอร์ โดยไม่มีขั้นตอนการบิลด์ อย่างไรก็ตาม วิธีนี้ไม่ได้รับการรองรับใน React Native และสภาพแวดล้อมอื่นๆ ที่ไม่มี ESM ของเบราว์เซอร์ ดังนั้น shimming ผ่าน Webpack ยังคงมีความเกี่ยวข้องสำหรับการบิลด์แบบ production ที่ต้องควบคุมข้อมูลพึ่งพาและเวอร์ชันของมันได้อย่างสมบูรณ์ การเลือกระหว่าง import maps และ Webpack shims ขึ้นอยู่กับแพลตฟอร์มเป้าหมายและข้อกำหนดความเข้ากันได้กับเบราว์เซอร์เก่า

คำถามที่พบบ่อย

shimming ต่างจาก tree shaking อย่างไร?

Shimming เพิ่มโค้ดเพื่อให้มั่นใจถึงความเข้ากันได้ ในขณะที่ tree shaking ลบโค้ดที่ไม่ได้ใช้ เทคนิคทั้งสองนี้มีเป้าหมายตรงข้ามกัน: shimming เพิ่มขนาดบันเดิล tree shaking ลดขนาดลง ในการบิลด์แบบ production ทั้งสองถูกใช้ตามลำดับ

สามารถใช้ shimming โดยไม่ใช้ Webpack ได้หรือไม่?

ได้ shimming มีอยู่เป็นเทคนิคที่อิสระจาก Webpack — ตัวอย่างเช่น ผ่านสคริปต์ทั่วโลกใน HTML หรือผ่าน ES โมดูลที่มีการส่งออกซ้ำ อย่างไรก็ตาม Webpack ให้เครื่องมืออัตโนมัติที่สะดวกที่สุด: ProvidePlugin และโลเดอร์ที่ไม่ต้องแก้ไขโค้ดด้วยตนเอง

shimming ส่งผลต่อประสิทธิภาพการบิลด์อย่างไร?

ProvidePlugin ไม่ส่งผลต่อความเร็วในการบิลด์ เพราะมันทำงานในขั้นตอนการคอมไพล์ AST imports-loader และ exports-loader เพิ่มเวลาการประมวลผลเล็กน้อยสำหรับแต่ละไฟล์ เมื่อใช้กับไฟล์หลายร้อยไฟล์ ความแตกต่างอาจเป็น 5–15% ของเวลาการบิลด์ทั้งหมด

เมื่อใดควรเลิกใช้ shimming?

หากข้อมูลพึ่งพาทั้งหมดรองรับ ES โมดูล และระบบโมดูลแล้ว shimming ก็เกินความจำเป็น การเลิกใช้ shimming ทำให้คอนฟิกูเรชันง่ายขึ้น ลดขนาดบันเดิล และลดความเสี่ยงของความขัดแย้งของชื่อ แนะนำให้ตรวจสอบข้อมูลพึ่งพาบน caniuse.com

shimming ทำงานกับ TypeScript อย่างไร?

TypeScript ต้องการการประกาศชนิดเพิ่มเติมสำหรับตัวแปรที่ shim จำเป็นต้องเพิ่ม declare const $: any หรือติดตั้งชนิดผ่าน @types/jquery ProvidePlugin แทรกการนำเข้าในระดับ JavaScript หลังจากการคอมไพล์ TypeScript ดังนั้นชนิดจะถูกตรวจสอบแยกต่างหาก

สรุป

  • Shimming เป็นเทคนิคเพื่อให้มั่นใจถึงความเข้ากันได้ของโมดูลกับสภาพแวดล้อมผ่านการแทนที่ตัวแปรทั่วโลกและ API
  • ProvidePlugin นำเข้าโมดูลโดยอัตโนมัติเมื่อตรวจพบการอ้างอิงถึงตัวระบุที่กำหนดไว้ในโค้ด
  • imports-loader เพิ่มการนำเข้าลงในตอนต้นของไฟล์เฉพาะ ในขณะที่ exports-loader กำหนดค่าที่ส่งออก
  • Shim ต่างจาก polyfill ตรงที่ไม่ได้ใช้งานฟังก์ชัน แต่เปลี่ยนเส้นทางคำเรียกไปยังการใช้งานที่มีอยู่
  • ProvidePlugin ทำงานในขั้นตอนการคอมไพล์และไม่ประมวลผลการอ้างอิงตัวแปรแบบไดนามิก
  • ฟิลด์ globalObject ใน output กำหนดบริบทที่ถูกต้องสำหรับระดับบนสุดในสภาพแวดล้อมเป้าหมาย
  • ใช้ shimming เฉพาะกับโมดูลที่ไม่รองรับระบบโมดูลสมัยใหม่ และเลิกใช้เมื่อ ES โมดูลได้รับการรองรับอย่างเต็มที่

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

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