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


173

ฉันกำลังพยายามทำสิ่งที่ฉันเชื่อว่าควรจะเป็นไปได้ แต่ฉันไม่เข้าใจจริงๆว่าจะทำอย่างไรจากเอกสารของ webpack

ฉันกำลังเขียนไลบรารี JavaScript ที่มีหลายโมดูลที่อาจหรือไม่ขึ้นอยู่กับกัน ยิ่งไปกว่านั้น jQuery ใช้โดยโมดูลทั้งหมดและบางโมดูลอาจต้องใช้ปลั๊กอิน jQuery ไลบรารีนี้จะใช้ในเว็บไซต์ต่าง ๆ ซึ่งอาจต้องใช้โมดูลบางส่วนหรือทั้งหมด

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

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

ตัวอย่าง : ลองนึกภาพว่าห้องสมุดของฉันมีโมดูลต่อไปนี้:

  • a (ต้องการ: jquery, jquery.plugin1)
  • b (ต้องการ: jquery, a)
  • c (ต้องการ: jquery, jquery.ui, a, b)
  • d (ต้องการ: jquery, jquery.plugin2, a)

และฉันมีแอพ (ดูเป็นไฟล์รายการที่ไม่ซ้ำ) ที่ต้องใช้โมดูล a, b และ c Webpack สำหรับกรณีนี้ควรสร้างไฟล์ต่อไปนี้:

  • ชุดผู้ขาย : ด้วย jquery, jquery.plugin1 และ jquery.ui;
  • ชุดเว็บไซต์ : มีโมดูล a, b และ c;

ในตอนท้ายฉันอยากจะมี jQuery เป็นโกลบอลดังนั้นฉันไม่จำเป็นต้องใช้มันในทุก ๆ ไฟล์ (ฉันอาจต้องการมันเฉพาะไฟล์หลักเท่านั้น) และปลั๊กอิน jQuery จะขยาย $ global ในกรณีที่จำเป็น (ไม่ใช่ปัญหาหากมีโมดูลอื่น ๆ ที่ไม่ต้องการ)

สมมติว่าเป็นไปได้สิ่งที่จะเป็นตัวอย่างของไฟล์กำหนดค่า webpack สำหรับกรณีนี้? ฉันลองตัวโหลดหลายตัว externals และปลั๊กอินบนแฟ้มการกำหนดค่าของฉัน แต่ฉันไม่ได้สิ่งที่พวกเขากำลังทำและฉันควรใช้อันไหน ขอบคุณ!


2
ทางออกของคุณคืออะไร คุณจัดการเพื่อหาแนวทางที่เหมาะสมหรือไม่ ถ้าเป็นเช่นนั้นโปรดโพสต์มัน! ขอบคุณ
GeekOnGadgets

คำตอบ:


140

ใน webpack.config.js ของฉัน (เวอร์ชัน 1,2,3) ไฟล์ฉันมี

function isExternal(module) {
  var context = module.context;

  if (typeof context !== 'string') {
    return false;
  }

  return context.indexOf('node_modules') !== -1;
}

ในอาร์เรย์ปลั๊กอินของฉัน

plugins: [
  new CommonsChunkPlugin({
    name: 'vendors',
    minChunks: function(module) {
      return isExternal(module);
    }
  }),
  // Other plugins
]

ตอนนี้ฉันมีไฟล์ที่เพิ่ม libs ของบุคคลที่สามให้เป็นหนึ่งไฟล์ตามต้องการ

หากคุณต้องการได้รับรายละเอียดเพิ่มเติมที่คุณแยกผู้ขายและไฟล์จุดเข้าใช้งานของคุณ:

plugins: [
  new CommonsChunkPlugin({
    name: 'common',
    minChunks: function(module, count) {
      return !isExternal(module) && count >= 2; // adjustable
    }
  }),
  new CommonsChunkPlugin({
    name: 'vendors',
    chunks: ['common'],
    // or if you have an key value object for your entries
    // chunks: Object.keys(entry).concat('common')
    minChunks: function(module) {
      return isExternal(module);
    }
  })
]

โปรดทราบว่าลำดับของปลั๊กอินมีความสำคัญมาก

นอกจากนี้สิ่งนี้จะเปลี่ยนแปลงในเวอร์ชัน 4 เมื่อเป็นทางการฉันจะอัปเดตคำตอบนี้

อัปเดต: indexOf เปลี่ยนแปลงการค้นหาสำหรับผู้ใช้ windows


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

1
isExternalในminChunksทำวันของฉัน เอกสารนี้มีวิธีการอย่างไร? มีข้อเสียหรือไม่
Wesley Schleumer de Góes

ขอบคุณ แต่เปลี่ยน userRequest.indexOf ('/ node_modules /') เป็น userRequest.indexOf ('node_modules') สำหรับ
เส้นทาง

@ WesleySchleumerde มันเป็นเอกสาร แต่ไม่มีตัวอย่างoptions.minChunks (number|Infinity|function(module, count) -> boolean):ฉันยังไม่เห็นข้อเสีย
Rafael De Leon

2
สิ่งนี้จะไม่ทำงานเมื่อใช้ตัวโหลดเนื่องจากเส้นทางของตัวโหลดจะทำงานด้วยmodule.userRequest(และตัวโหลดอาจทำงานnode_modules) รหัสของฉันสำหรับisExternal():return typeof module.userRequest === 'string' && !!module.userRequest.split('!').pop().match(/(node_modules|bower_components|libraries)/);
cdauth

54

ฉันไม่แน่ใจว่าฉันเข้าใจปัญหาของคุณทั้งหมดหรือไม่ แต่เนื่องจากฉันมีปัญหาที่คล้ายกันเมื่อเร็ว ๆ นี้ฉันจะพยายามช่วยเหลือคุณ

กำผู้ขาย

คุณควรใช้CommonsChunkPluginเพื่อสิ่งนั้น ในการกำหนดค่าที่คุณระบุชื่อของก้อน (เช่นvendor) และชื่อไฟล์ที่จะถูกสร้างขึ้น ( vendor.js)

new webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js", Infinity),

ตอนนี้ส่วนที่สำคัญตอนนี้คุณต้องระบุว่าvendorไลบรารีหมายความว่าอะไรและคุณทำเช่นนั้นในส่วนของรายการ อีกหนึ่งรายการไปยังรายการที่มีชื่อเดียวกันกับชื่อของอันที่ประกาศใหม่ (เช่น 'ผู้ขาย' ในกรณีนี้) ค่าของรายการนั้นควรเป็นรายการของโมดูลทั้งหมดที่คุณต้องการย้ายไปยังvendorบันเดิล ในกรณีของคุณควรมีลักษณะดังนี้:

entry: {
    app: 'entry.js',
    vendor: ['jquery', 'jquery.plugin1']
}

JQuery เป็นสากล

มีปัญหาเดียวกันและแก้ไขมันด้วยProvidePlugin ที่นี่คุณไม่ได้กำหนดวัตถุโกลบอล แต่เป็น shurtcuts ให้กับโมดูล เช่นคุณสามารถกำหนดค่าแบบนั้นได้:

new webpack.ProvidePlugin({
    $: "jquery"
})

และตอนนี้คุณสามารถใช้$ที่ใดก็ได้ในรหัสของคุณ - webpack จะแปลงให้เป็นโดยอัตโนมัติ

require('jquery')

ฉันหวังว่ามันจะช่วย คุณยังสามารถดูไฟล์การกำหนดค่า webpack ของฉันได้ที่นี่

ฉันรัก webpack แต่ฉันเห็นด้วยว่าเอกสารไม่ใช่สิ่งที่อร่อยที่สุดในโลก ... แต่เฮ้ .. ผู้คนพูดอย่างเดียวกันเกี่ยวกับเอกสารเชิงมุมในจุดเริ่มต้น :)


แก้ไข:

หากต้องการให้ผู้ขายเฉพาะจุดเข้าใช้งานเพียงแค่ใช้ CommonsChunkPlugins หลายครั้ง:

new webpack.optimize.CommonsChunkPlugin("vendor-page1", "vendor-page1.js", Infinity),
new webpack.optimize.CommonsChunkPlugin("vendor-page2", "vendor-page2.js", Infinity),

จากนั้นประกาศไลบรารี extenral ที่แตกต่างกันสำหรับไฟล์ต่าง ๆ :

entry: {
    page1: ['entry.js'],
    page2: ['entry2.js'],
    "vendor-page1": [
        'lodash'
    ],
    "vendor-page2": [
        'jquery'
    ]
},

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


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

ขอบคุณดังนั้นฉันได้เรียนรู้บางสิ่งบางอย่างด้วย :) ฉันได้อัปเดตคำตอบของฉันด้วยการสร้างผู้ขายหลายชิ้น บางทีตอนนี้มันจะเหมาะกับคุณมากกว่า
Michał Margiel

4
ปัญหาเกี่ยวกับวิธีแก้ปัญหานี้คือมันถือว่าฉันรู้ว่าสิ่งที่อ้างอิงสำหรับแต่ละหน้า แต่ฉันไม่สามารถคาดเดาได้ว่า ... ควรรวม jQuery ไว้ในบันเดิลผู้ขายหากจำเป็นต้องใช้หนึ่งในโมดูลที่ใช้ในหน้า โดยการระบุว่าในไฟล์ปรับแต่งมันจะอยู่ในกลุ่มผู้ขายเสมอแม้ว่าจะไม่จำเป็นต้องใช้โมดูลใด ๆ ที่ใช้ในหน้าใช่มั้ย โดยพื้นฐานแล้วฉันไม่สามารถคาดการณ์เนื้อหาของกลุ่มผู้ขายมิฉะนั้นฉันจะมีงานที่แย่เพราะฉันไม่มี 2 หน้าฉันมีหลายร้อย ... คุณได้รับปัญหาหรือไม่ ความคิดใด ๆ :)
bensampaio

ฉันเข้าใจสิ่งที่คุณกำลังพูด แต่ฉันไม่เห็นว่าเป็นปัญหา หากคุณใช้ไลบรารีใหม่ในหน้าเว็บให้เพิ่มไปยังรายการไลบรารีผู้ขายสำหรับหน้านั้น มันเป็นเพียงตัวละครน้อย ต่อไปในโซลูชันของคุณคุณต้องทำโดยระบุตัวโหลดหากคุณไม่ทราบว่าหน้าใดจะใช้โมดูลที่สร้างขึ้นใหม่ของคุณ - จากนั้นให้ปลั๊กอิน CommonChuncks แยกไลบรารีทั่วไปออกจากโมดูลของคุณโดยอัตโนมัติ
Michał Margiel

ฉันจะตั้งค่าบริบทแยกต่างหากสำหรับไฟล์ผู้ขายได้อย่างไร
harshes53

44

ในกรณีที่คุณสนใจในการรวมสคริปต์ของคุณแยกจากผู้ขายโดยอัตโนมัติ:

var webpack = require('webpack'),
    pkg     = require('./package.json'),  //loads npm config file
    html    = require('html-webpack-plugin');

module.exports = {
  context : __dirname + '/app',
  entry   : {
    app     : __dirname + '/app/index.js',
    vendor  : Object.keys(pkg.dependencies) //get npm vendors deps from config
  },
  output  : {
    path      : __dirname + '/dist',
    filename  : 'app.min-[hash:6].js'
  },
  plugins: [
    //Finally add this line to bundle the vendor code separately
    new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.min-[hash:6].js'),
    new html({template : __dirname + '/app/index.html'})
  ]
};

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับคุณลักษณะนี้ในเอกสารอย่างเป็นทางการ


4
โปรดทราบว่าvendor : Object.keys(pkg.dependencies) อาจไม่ได้ผลเสมอไปและขึ้นอยู่กับการสร้างแพ็คเกจ
markyph

1
คุณมักจะขึ้นอยู่กับวิธีการpackage.jsonตั้งค่าของคุณ วิธีแก้ปัญหานี้ใช้งานได้ในกรณีส่วนใหญ่ แต่มีข้อยกเว้นที่คุณจะต้องใช้เส้นทางที่แตกต่างกัน อาจเป็นเรื่องที่น่าสนใจที่จะโพสต์คำตอบของคุณสำหรับคำถามเพื่อช่วยชุมชน
Freezystem

16
ฉันชอบสิ่งนี้. มันทำให้ฉันฉี่เล็กน้อย
cgatian

3
โปรดทราบว่ามันจะรวมแพ็คเกจที่คุณอาจไม่ได้ใช้เลยในโค้ดของคุณ ... เนื่องจากObject.keys(pkg.dependencies)จะรวมทุกอย่าง !!!! ให้บอกว่าคุณมีพวงของตัวตักที่ระบุไว้ที่นั่น ... ใช่ที่จะรวม !!! ดังนั้นจงระวัง ... แยกกันอย่างระมัดระวังว่าอะไรคือการพึ่งพาและการพึ่งพา
Rafael Milewski

1
@RafaelMilewski ทำไมคุณถึงมีรถตักdependencies?
กางเกง

13

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

entry: {
  bundle1: './build/bundles/bundle1.js',
  bundle2: './build/bundles/bundle2.js',
  'vendor-bundle1': [
    'react',
    'react-router'
  ],
  'vendor-bundle2': [
    'react',
    'react-router',
    'flummox',
    'immutable'
  ]
},

plugins: [
  new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor-bundle1',
    chunks: ['bundle1'],
    filename: 'vendor-bundle1.js',
    minChunks: Infinity
  }),
  new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor-bundle2',
    chunks: ['bundle2'],
    filename: 'vendor-bundle2-whatever.js',
    minChunks: Infinity
  }),
]

และลิงก์ไปยังCommonsChunkPluginเอกสาร: http://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin


ฉันเชื่อว่าปัญหาในการแก้ไขปัญหานี้เหมือนกับปัญหาที่ Michal จัดไว้ให้ คุณกำลังสมมติว่าฉันรู้ว่าผู้จำหน่ายพึ่งพาสำหรับบันเดิล 1 และบันเดิล 2 แต่ฉันไม่ ... ลองนึกภาพคุณมี 200 กลุ่มคุณต้องการระบุทั้งหมดในไฟล์กำหนดค่าหรือไม่ ใช้ตัวอย่างของคุณreactควรมีอยู่ในบันเดิลผู้ขายเฉพาะเมื่อมันถูกกำหนดโดยบันเดิล 1 และบันเดิล 2 เท่านั้น ฉันไม่จำเป็นต้องระบุว่าในไฟล์ปรับแต่ง ... มันสมเหตุสมผลหรือไม่ ความคิดใด ๆ
bensampaio

@Anakin คำถามคือเหตุผลที่คุณต้องการรวมกลุ่มผู้ขาย 200 เครื่องมือเป็นไฟล์แยกต่างหาก ฉันจะรวมกลุ่มเครื่องมือทั่วไปเป็นไฟล์แยกต่างหากและเก็บส่วนที่เหลือไว้กับชุดโครงการ
maxisam

@Anakin ฉันคิดว่าฉันจัดการกับปัญหาเดียวกันแก้ไขฉันถ้าฉันผิด? stackoverflow.com/questions/35944067/…
pjdicke
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.