เนื่องจากเว็บแอปพลิเคชันมีความซับซ้อนมากขึ้นการทำให้เว็บแอปของคุณสามารถปรับขนาดได้จึงมีความสำคัญสูงสุด ในขณะที่การเขียน ad-hoc JavaScript และ jQuery ในอดีตจะเพียงพอ แต่ในปัจจุบันการสร้างเว็บแอปนั้นจำเป็นต้องมีระเบียบวินัยและแนวทางปฏิบัติในการพัฒนาซอฟต์แวร์อย่างเป็นทางการมากขึ้นเช่น:
เว็บยังมีความท้าทายในการพัฒนาที่เป็นเอกลักษณ์ของตัวเอง ตัวอย่างเช่นเนื่องจากหน้าเว็บมีคำขอแบบอะซิงโครนัสจำนวนมากประสิทธิภาพของเว็บแอปอาจลดลงอย่างมากจากการที่ต้องขอไฟล์ JS และ CSS หลายร้อยไฟล์โดยแต่ละหน้าจะมีค่าใช้จ่ายเล็กน้อย (ส่วนหัวการจับมือและอื่น ๆ ) ปัญหานี้มักจะแก้ไขได้โดยการรวมไฟล์เข้าด้วยกันดังนั้นคุณจึงขอไฟล์ JS และ CSS ที่รวมเป็นชุดเดียวแทนที่จะเป็นไฟล์ทีละหลายร้อยไฟล์
นอกจากนี้ยังเป็นเรื่องปกติที่จะใช้ตัวประมวลผลล่วงหน้าของภาษาเช่น SASS และ JSX ที่คอมไพล์กับ JS และ CSS ดั้งเดิมตลอดจนตัวส่งสัญญาณ JS เช่น Babel เพื่อให้ได้รับประโยชน์จากโค้ด ES6 ในขณะที่ยังคงความเข้ากันได้กับ ES5
จำนวนนี้เป็นงานจำนวนมากที่ไม่เกี่ยวข้องกับการเขียนตรรกะของเว็บแอปเอง นี่คือสิ่งที่นักวิ่งเข้ามาจุดประสงค์ของนักวิ่งงานคือการทำให้งานเหล่านี้เป็นไปโดยอัตโนมัติเพื่อให้คุณได้รับประโยชน์จากสภาพแวดล้อมการพัฒนาที่ดีขึ้นในขณะที่เน้นการเขียนแอป เมื่อกำหนดค่า Task Runner แล้วสิ่งที่คุณต้องทำคือเรียกใช้คำสั่งเดียวในเทอร์มินัล
ฉันจะใช้ อึก ในฐานะนักวิ่งงานเพราะเป็นมิตรกับนักพัฒนาเรียนรู้ง่ายและเข้าใจได้ง่าย
API ของ Gulp ประกอบด้วยสี่ฟังก์ชัน:
การปรับแต่งประสิทธิภาพเซิร์ฟเวอร์ sql 2014
gulp.src
gulp.dest
gulp.task
gulp.watch
ตัวอย่างเช่นในที่นี้เป็นงานตัวอย่างที่ใช้ฟังก์ชันสามในสี่ฟังก์ชันนี้:
gulp.task('my-first-task', function() { gulp.src('/public/js/**/*.js') .pipe(concat()) .pipe(minify()) .pipe(gulp.dest('build')) });
เมื่อ my-first-task
จะดำเนินการไฟล์ทั้งหมดที่ตรงกับรูปแบบ glob /public/js/**/*.js
ถูกลดขนาดแล้วโอนไปที่ build
โฟลเดอร์
ความงามของสิ่งนี้อยู่ใน .pipe()
การล่ามโซ่ คุณนำชุดของไฟล์อินพุตไปป์ผ่านชุดของการแปลงจากนั้นส่งคืนไฟล์เอาต์พุต เพื่อให้สิ่งต่างๆสะดวกยิ่งขึ้นการแปลงท่อจริงเช่น minify()
มักจะทำโดยไลบรารี NPM ด้วยเหตุนี้จึงมีน้อยมากในทางปฏิบัติที่คุณต้องเขียนการเปลี่ยนแปลงของคุณเองนอกเหนือจากการเปลี่ยนชื่อไฟล์ในไพพ์
ขั้นตอนต่อไปในการทำความเข้าใจ Gulp คือการทำความเข้าใจอาร์เรย์ของการอ้างอิงงาน
gulp.task('my-second-task', ['lint', 'bundle'], function() { ... });
ที่นี่ my-second-task
เรียกใช้ฟังก์ชันการโทรกลับหลังจาก lint
เท่านั้น และ bundle
งานจะเสร็จสมบูรณ์ สิ่งนี้ช่วยให้แยกข้อกังวล: คุณสร้างชุดงานเล็ก ๆ ด้วยความรับผิดชอบเดียวเช่นการแปลง LESS
เป็น CSS
และสร้างงานหลักที่เรียกใช้งานอื่น ๆ ทั้งหมดผ่านอาร์เรย์ของการอ้างอิงงาน
ในที่สุดเราก็มี gulp.watch
ซึ่งคอยดูรูปแบบไฟล์ glob สำหรับการเปลี่ยนแปลงและเมื่อตรวจพบการเปลี่ยนแปลงจะเรียกใช้ชุดของงาน
gulp.task('my-third-task', function() { gulp.watch('/public/js/**/*.js', ['lint', 'reload']) })
ในตัวอย่างข้างต้นการเปลี่ยนแปลงใด ๆ ในการจับคู่ไฟล์ /public/js/**/*.js
จะเรียก lint
และ reload
งาน. การใช้งานทั่วไปของ gulp.watch
คือการเรียกใช้การโหลดซ้ำในเบราว์เซอร์ซึ่งเป็นคุณลักษณะที่มีประโยชน์มากสำหรับการพัฒนาที่คุณจะขาดไม่ได้เมื่อได้สัมผัส
และเช่นเดียวกับที่คุณเข้าใจทั้งหมดที่คุณจำเป็นต้องรู้ gulp
เมื่อใช้รูปแบบ CommonJS การรวมไฟล์ JavaScript ไม่ง่ายเหมือนการต่อไฟล์เข้าด้วยกัน แต่คุณมีจุดเริ่มต้น (โดยปกติเรียกว่า index.js
หรือ app.js
) พร้อมด้วยชุดของ require
หรือ import
คำสั่งที่ด้านบนของไฟล์:
var Component1 = require('./components/Component1'); var Component2 = require('./components/Component2');
import Component1 from './components/Component1'; import Component2 from './components/Component2';
การอ้างอิงต้องได้รับการแก้ไขก่อนโค้ดที่เหลือใน app.js
และการอ้างอิงเหล่านั้นอาจมีการอ้างอิงเพิ่มเติมเพื่อแก้ไข นอกจากนี้คุณอาจ require
การอ้างอิงเดียวกันในหลาย ๆ ที่ในแอปพลิเคชันของคุณ แต่คุณต้องการแก้ไขการอ้างอิงนั้นเพียงครั้งเดียว อย่างที่คุณสามารถจินตนาการได้ว่าเมื่อคุณมีแผนผังการพึ่งพาสองสามระดับขั้นตอนการรวม JavaScript ของคุณจะค่อนข้างซับซ้อน นี่คือที่บันเดิลเลอร์เช่น Browserify และ Webpack เข้ามา
Webpack เป็นบันเดิลเลอร์ในขณะที่ Gulp เป็นนักวิ่งงานดังนั้นคุณคาดว่าจะเห็นเครื่องมือทั้งสองนี้ใช้ร่วมกัน แต่มีแนวโน้มเพิ่มขึ้นเรื่อย ๆ โดยเฉพาะในชุมชน React ที่จะใช้ Webpack แทน ของ Gulp. ทำไมถึงเป็นแบบนี้?
พูดง่ายๆคือ Webpack เป็นเครื่องมือที่ทรงพลังที่สามารถทำงานส่วนใหญ่ที่คุณทำผ่านนักวิ่งงานได้ ตัวอย่างเช่น Webpack มีตัวเลือกสำหรับการย่อขนาดและซอร์สแมปสำหรับบันเดิลของคุณอยู่แล้ว นอกจากนี้ Webpack ยังสามารถทำงานเป็นมิดเดิลแวร์ผ่านเซิร์ฟเวอร์ที่กำหนดเองชื่อ webpack-dev-server
ซึ่งรองรับทั้งการโหลดซ้ำแบบสดและการโหลดซ้ำแบบร้อน (เราจะพูดถึงคุณลักษณะเหล่านี้ในภายหลัง) เมื่อใช้รถตักคุณยังสามารถเพิ่ม ES6 ไปยัง ES5 Transpilation และ CSS pre-and post-processors นั่นเป็นเพียงแค่ปล่อยให้การทดสอบหน่วยและการขุยเป็นงานหลักที่ Webpack ไม่สามารถจัดการได้อย่างอิสระ เนื่องจากเราได้ลดงานอึกอย่างน้อยครึ่งโหลลงเหลือสองงานนักพัฒนาจำนวนมากเลือกที่จะใช้สคริปต์ NPM โดยตรงแทนเนื่องจากจะหลีกเลี่ยงค่าใช้จ่ายในการเพิ่ม Gulp ในโครงการ (ซึ่งเราจะพูดถึงในภายหลัง) .
ข้อเสียเปรียบที่สำคัญในการใช้ Webpack คือการกำหนดค่าค่อนข้างยากซึ่งไม่น่าสนใจหากคุณพยายามทำให้โปรเจ็กต์ทำงานได้อย่างรวดเร็ว
ฉันจะตั้งค่าโปรเจ็กต์ด้วยการตั้งค่าตัววิ่งงานที่แตกต่างกันสามแบบ การตั้งค่าแต่ละครั้งจะทำงานต่อไปนี้:
การตั้งค่าทั้งสามของเราคือ:
แอปพลิเคชันจะใช้ ตอบสนอง สำหรับส่วนหน้า เดิมทีฉันต้องการใช้วิธีการที่ไม่เชื่อเรื่องพระเจ้าของกรอบงาน แต่การใช้ React ช่วยลดความซับซ้อนของความรับผิดชอบของนักวิ่งงานเนื่องจากต้องการไฟล์ HTML เพียงไฟล์เดียวและ React ทำงานได้ดีกับรูปแบบ CommonJS
เราจะกล่าวถึงประโยชน์และข้อเสียของการตั้งค่าแต่ละครั้งเพื่อให้คุณสามารถตัดสินใจได้อย่างชาญฉลาดว่าการตั้งค่าประเภทใดที่เหมาะสมกับความต้องการของโครงการของคุณมากที่สุด
ฉันได้ตั้งค่าที่เก็บ Git โดยมีสามสาขาหนึ่งแห่งสำหรับแต่ละแนวทาง ( ลิงค์ ). การทดสอบการตั้งค่าแต่ละครั้งทำได้ง่ายๆดังนี้:
git checkout npm prune (optional) npm install gulp (or npm start, depending on the setup)
มาตรวจสอบรหัสในแต่ละสาขาโดยละเอียด ...
- app - components - fonts - styles - index.html - index.js - index.test.js - routes.js
ไฟล์ HTML ที่ตรงไปตรงมา แอปพลิเคชัน React ถูกโหลดเข้ามาและเราใช้ไฟล์ JS และ CSS ที่มาพร้อมกับไฟล์เดียวเท่านั้น ในความเป็นจริงในการตั้งค่าการพัฒนา Webpack เราไม่จำเป็นต้องมี bundle.css
ด้วยซ้ำ
ซึ่งทำหน้าที่เป็นจุดเข้า JS ของแอปของเรา โดยพื้นฐานแล้วเราแค่โหลด React Router ลงใน div
ด้วย id app
ที่เรากล่าวถึงก่อนหน้านี้
ฟรีหมายเลขบัตรเครดิตที่ถูกแฮ็กด้วย cvv
ไฟล์นี้กำหนดเส้นทางของเรา URL /
, /about
และ /contact
ถูกแมปกับ HomePage
, AboutPage
และ ContactPage
ส่วนประกอบตามลำดับ
นี่คือชุดของการทดสอบหน่วยที่ทดสอบพฤติกรรม JavaScript ดั้งเดิม ในแอปคุณภาพการผลิตจริงคุณจะต้องเขียนการทดสอบหน่วยต่อองค์ประกอบการตอบสนอง (อย่างน้อยก็การทดสอบที่ปรับเปลี่ยนสถานะ) ทดสอบพฤติกรรมเฉพาะของปฏิกิริยา อย่างไรก็ตามสำหรับจุดประสงค์ของโพสต์นี้เพียงแค่มีการทดสอบหน่วยการทำงานที่สามารถทำงานในโหมดนาฬิกาได้ก็เพียงพอแล้ว
สิ่งนี้ถือได้ว่าเป็นคอนเทนเนอร์สำหรับการดูหน้าเว็บทั้งหมดของเรา แต่ละหน้ามีความสอดคล้องเช่นเดียวกับ this.props.children
ซึ่งประเมินเป็นการดูหน้าเว็บ (เช่น / ContactPage
ถ้าที่ /contact
ในเบราว์เซอร์)
ส่วนประกอบ / home / HomePage.js
นี่คือมุมมองบ้านของเรา ฉันเลือกใช้ react-bootstrap
เนื่องจากระบบกริดของ bootstrap นั้นยอดเยี่ยมสำหรับการสร้างเพจที่ตอบสนอง ด้วยการใช้ bootstrap อย่างเหมาะสมจำนวนคิวรีสื่อที่คุณต้องเขียนสำหรับวิวพอร์ตขนาดเล็กจะลดลงอย่างมาก
ส่วนประกอบที่เหลือ (Header
, AboutPage
, ContactPage
) มีโครงสร้างคล้ายกัน (react-bootstrap
มาร์กอัปไม่มีการจัดการสถานะ)
ตอนนี้เรามาพูดถึงการจัดแต่งทรงผมกันดีกว่า
แนวทางที่ฉันชอบในการจัดแต่งทรงผมส่วนประกอบ React คือการมีหนึ่งสไตล์ชีตต่อหนึ่งองค์ประกอบซึ่งสไตล์จะถูกกำหนดขอบเขตให้ใช้กับส่วนประกอบเฉพาะนั้นเท่านั้น คุณจะสังเกตเห็นว่าในองค์ประกอบการตอบสนองแต่ละส่วนของฉันคือระดับบนสุด div
มีชื่อคลาสที่ตรงกับชื่อของส่วนประกอบ ตัวอย่างเช่น HomePage.js
มีมาร์กอัปห่อโดย:
...
นอกจากนี้ยังมี HomePage.scss
ไฟล์ที่มีโครงสร้างดังนี้:
@import '../../styles/variables'; .HomePage { // Content here }
เหตุใดแนวทางนี้จึงมีประโยชน์มาก ส่งผลให้ CSS แบบโมดูลาร์สูงซึ่งส่วนใหญ่ช่วยขจัดปัญหาของพฤติกรรมการเรียงซ้อนที่ไม่ต้องการ
สมมติว่าเรามีส่วนประกอบของปฏิกิริยาสองตัว Component1
และ Component2
. ในทั้งสองกรณีเราต้องการแทนที่ h2
ขนาดตัวอักษร.
/* Component1.scss */ .Component1 { h2 { font-size: 30px; } } /* Component2.scss */ .Component2 { h2 { font-size: 60px; } }
h2
ขนาดตัวอักษรของ Component1
และ Component2
เป็นอิสระไม่ว่าส่วนประกอบจะอยู่ติดกันหรือส่วนประกอบหนึ่งซ้อนอยู่ภายในอีกส่วนหนึ่ง ตามหลักการแล้วนี่หมายความว่าการจัดแต่งทรงผมของส่วนประกอบนั้นมีอยู่ในตัวเองโดยสมบูรณ์หมายความว่าส่วนประกอบจะมีลักษณะเหมือนกันทุกประการไม่ว่าจะวางไว้ที่ใดในมาร์กอัปของคุณ ในความเป็นจริงมันไม่ง่ายเสมอไป แต่มันเป็นก้าวที่ยิ่งใหญ่ในทิศทางที่ถูกต้อง
เครื่องมือทดสอบหน่วยสำหรับ net
นอกจากรูปแบบต่อองค์ประกอบแล้วฉันยังต้องการ styles
โฟลเดอร์ที่มีสไตล์ชีตส่วนกลาง global.scss
พร้อมกับส่วน SASS ที่จัดการความรับผิดชอบเฉพาะ (ในกรณีนี้คือ _fonts.scss
และ _variables.scss
สำหรับฟอนต์และตัวแปรตามลำดับ) สไตล์ชีตส่วนกลางช่วยให้เราสามารถกำหนดรูปลักษณ์ทั่วไปของแอปทั้งหมดได้ในขณะที่สไตล์ชีตส่วนช่วยสามารถนำเข้าโดยใช้สไตล์ชีตต่อองค์ประกอบได้ตามต้องการ
ตอนนี้โค้ดทั่วไปในแต่ละสาขาได้รับการสำรวจในเชิงลึกแล้วเรามาเปลี่ยนโฟกัสของเราไปที่แนวทางของนักวิ่งภารกิจแรก / บันเดิลเลอร์
สิ่งนี้ออกมาเป็น gulpfile ขนาดใหญ่อย่างน่าประหลาดใจโดยมีการนำเข้า 22 รายการและโค้ด 150 บรรทัด ดังนั้นเพื่อความกระชับฉันจะตรวจสอบเฉพาะ js
, css
, server
, watch
และ default
งานโดยละเอียด
// Browserify specific configuration const b = browserify({ entries: [config.paths.entry], debug: true, plugin: PROD ? [] : [hmr, watchify], cache: {}, packageCache: {} }) .transform('babelify'); b.on('update', bundle); b.on('log', gutil.log); (...) gulp.task('js', bundle); (...) // Bundles our JS using Browserify. Sourcemaps are used in development, while minification is used in production. function bundle() { return b.bundle() .on('error', gutil.log.bind(gutil, 'Browserify Error')) .pipe(source('bundle.js')) .pipe(buffer()) .pipe(cond(PROD, minifyJS())) .pipe(cond(!PROD, sourcemaps.init({loadMaps: true}))) .pipe(cond(!PROD, sourcemaps.write())) .pipe(gulp.dest(config.paths.baseDir)); }
วิธีนี้ค่อนข้างน่าเกลียดด้วยเหตุผลหลายประการ ประการหนึ่งคืองานแบ่งออกเป็นสามส่วนแยกกัน ขั้นแรกให้คุณสร้างออบเจ็กต์กลุ่ม Browserify b
ส่งผ่านตัวเลือกบางตัวและกำหนดตัวจัดการเหตุการณ์บางอย่าง จากนั้นคุณจะมีงาน Gulp เองซึ่งจะต้องส่งผ่านฟังก์ชันที่มีชื่อเป็นการเรียกกลับแทนการฝังใน (เนื่องจาก b.on('update')
ใช้การโทรกลับแบบเดียวกัน) สิ่งนี้แทบจะไม่มีความสง่างามของงานอึกที่คุณเพิ่งผ่านไป gulp.src
และท่อการเปลี่ยนแปลงบางอย่าง
อีกประเด็นหนึ่งคือสิ่งนี้บังคับให้เรามีวิธีการโหลดซ้ำ html
, css
และ js
ในเบราว์เซอร์ มองไปที่อึกของเรา watch
งาน:
gulp.task('watch', () => { livereload.listen({basePath: 'dist'}); gulp.watch(config.paths.html, ['html']); gulp.watch(config.paths.css, ['css']); gulp.watch(config.paths.js, () => { runSequence('lint', 'test'); }); });
เมื่อไฟล์ HTML มีการเปลี่ยนแปลง html
งานถูกรันใหม่
gulp.task('html', () => { return gulp.src(config.paths.html) .pipe(gulp.dest(config.paths.baseDir)) .pipe(cond(!PROD, livereload())); });
ท่อสุดท้ายเรียก livereload()
ถ้า NODE_ENV
ไม่ใช่ production
ซึ่งทำให้เกิดการรีเฟรชในเบราว์เซอร์
ใช้ตรรกะเดียวกันกับนาฬิกา CSS เมื่อไฟล์ CSS มีการเปลี่ยนแปลง css
งานถูกรันอีกครั้งและไปป์สุดท้ายใน css
ทริกเกอร์งาน livereload()
และรีเฟรชเบราว์เซอร์
อย่างไรก็ตาม js
นาฬิกาไม่เรียกว่า js
งานเลย ตัวจัดการเหตุการณ์ของ Browserify b.on('update', bundle)
จัดการการรีโหลดโดยใช้วิธีการที่แตกต่างกันโดยสิ้นเชิง (กล่าวคือการเปลี่ยนโมดูลร้อน) ความไม่สอดคล้องกันในแนวทางนี้เป็นสิ่งที่น่ารำคาญ แต่น่าเสียดายที่จำเป็นเพื่อให้มี เพิ่มขึ้น สร้าง ถ้าเราเรียกอย่างไร้เดียงสาว่า livereload()
ในตอนท้ายของ bundle
ฟังก์ชันนี้จะสร้างไฟล์ ทั้งหมด JS บันเดิลในการเปลี่ยนแปลงไฟล์ JS แต่ละไฟล์ เห็นได้ชัดว่าแนวทางดังกล่าวไม่ได้ขยายขนาด ยิ่งคุณมีไฟล์ JS มากเท่าไหร่การรวมกลุ่มใหม่แต่ละครั้งก็จะใช้เวลานานขึ้น ทันใดนั้นการคืนเงิน 500 มิลลิวินาทีของคุณจะเริ่มขึ้นโดยใช้เวลา 30 วินาทีซึ่งขัดขวางการพัฒนาที่คล่องตัวอย่างแท้จริง
gulp.task('css', () => { return gulp.src( [ 'node_modules/bootstrap/dist/css/bootstrap.css', 'node_modules/font-awesome/css/font-awesome.css', config.paths.css ] ) .pipe(cond(!PROD, sourcemaps.init())) .pipe(sass().on('error', sass.logError)) .pipe(concat('bundle.css')) .pipe(cond(PROD, minifyCSS())) .pipe(cond(!PROD, sourcemaps.write())) .pipe(gulp.dest(config.paths.baseDir)) .pipe(cond(!PROD, livereload())); });
ประเด็นแรกคือการรวม CSS ของผู้ขายที่ยุ่งยาก เมื่อใดก็ตามที่มีการเพิ่มไฟล์ CSS ของผู้ให้บริการรายใหม่ในโปรเจ็กต์เราต้องจำไว้ว่าให้เปลี่ยน gulpfile ของเราเพื่อเพิ่มองค์ประกอบลงใน gulp.src
อาร์เรย์แทนที่จะเพิ่มการนำเข้าลงในตำแหน่งที่เกี่ยวข้องในซอร์สโค้ดจริงของเรา
ปัญหาหลักอื่น ๆ คือตรรกะที่ซับซ้อนในแต่ละท่อ ฉันต้องเพิ่มไลบรารี NPM ชื่อ gulp-cond
เพียงเพื่อตั้งค่าตรรกะเงื่อนไขในไปป์ของฉันและผลลัพธ์สุดท้ายก็ไม่สามารถอ่านได้ (วงเล็บสามอันทุกที่!)
gulp.task('server', () => { nodemon({ script: 'server.js' }); });
งานนี้ตรงไปตรงมามาก โดยพื้นฐานแล้วมันเป็นกระดาษห่อหุ้มรอบการเรียกใช้บรรทัดคำสั่ง nodemon server.js
ซึ่งรัน server.js
ในสภาพแวดล้อมโหนด nodemon
ใช้แทน node
เพื่อให้การเปลี่ยนแปลงใด ๆ ในไฟล์ทำให้ไฟล์รีสตาร์ท โดยค่าเริ่มต้น nodemon
จะรีสตาร์ทกระบวนการทำงานบน ใด ๆ การเปลี่ยนไฟล์ JS ด้วยเหตุนี้การรวม nodemon.json
จึงเป็นเรื่องสำคัญ ไฟล์เพื่อ จำกัด ขอบเขต:
{ 'watch': 'server.js' }
มาดูรหัสเซิร์ฟเวอร์ของเรากัน
const baseDir = process.env.NODE_ENV === 'production' ? 'build' : 'dist'; const port = process.env.NODE_ENV === 'production' ? 8080: 3000; const app = express();
สิ่งนี้ตั้งค่าไดเร็กทอรีฐานของเซิร์ฟเวอร์และพอร์ตตามสภาพแวดล้อมโหนดและสร้างอินสแตนซ์ของ Express
app.use(require('connect-livereload')({port: 35729})); app.use(express.static(path.join(__dirname, baseDir)));
สิ่งนี้เพิ่ม connect-livereload
มิดเดิลแวร์ (จำเป็นสำหรับการตั้งค่าการโหลดซ้ำของเรา) และมิดเดิลแวร์แบบคงที่ (จำเป็นสำหรับการจัดการสินทรัพย์คงที่ของเรา)
app.get('/api/sample-route', (req, res) => { res.send({ website: 'ApeeScape', blogPost: true }); });
นี่เป็นเพียงเส้นทาง API ธรรมดา หากคุณไปที่ localhost:3000/api/sample-route
ในเบราว์เซอร์คุณจะเห็น:
{ website: 'ApeeScape', blogPost: true }
ในแบ็กเอนด์จริงคุณจะมีทั้งโฟลเดอร์เฉพาะสำหรับเส้นทาง API ไฟล์แยกต่างหากสำหรับสร้างการเชื่อมต่อ DB และอื่น ๆ ตัวอย่างเส้นทางนี้ถูกรวมไว้เพื่อแสดงให้เห็นว่าเราสามารถสร้างแบ็กเอนด์ที่ด้านบนของส่วนหน้าที่เราตั้งไว้ได้อย่างง่ายดาย
app.get('*', (req, res) => { res.sendFile(path.join(__dirname, './', baseDir ,'/index.html')); });
นี่คือเส้นทางที่ตรวจจับทั้งหมดซึ่งหมายความว่าไม่ว่าคุณจะพิมพ์ url ใดลงในเบราว์เซอร์เซิร์ฟเวอร์จะส่งคืนข้อความเดียวของเรา index.html
หน้า. จากนั้นเป็นความรับผิดชอบของ React Router ในการแก้ไขเส้นทางของเราในฝั่งไคลเอ็นต์
app.listen(port, () => { open(`http://localhost:${port}`); });
สิ่งนี้จะบอกให้อินสแตนซ์ด่วนของเราฟังพอร์ตที่เราระบุและเปิดเบราว์เซอร์ในแท็บใหม่ที่ URL ที่ระบุ
สิ่งเดียวที่ฉันไม่ชอบเกี่ยวกับการตั้งค่าเซิร์ฟเวอร์คือ:
app.use(require('connect-livereload')({port: 35729}));
ระบุว่าเราใช้ gulp-livereload
อยู่แล้ว ใน gulpfile ของเราสิ่งนี้ทำให้สองสถานที่แยกกันซึ่งต้องใช้ livereload
ตอนนี้สุดท้าย แต่ไม่ท้ายสุด:
gulp.task('default', (cb) => { runSequence('clean', 'lint', 'test', 'html', 'css', 'js', 'fonts', 'server', 'watch', cb); });
นี่คืองานที่ทำงานเมื่อพิมพ์ gulp
เข้าไปในเทอร์มินัล ความแปลกอย่างหนึ่งคือต้องใช้ runSequence
เพื่อให้งานรันตามลำดับ โดยปกติอาร์เรย์ของงานจะดำเนินการควบคู่กันไป แต่นี่ไม่ใช่พฤติกรรมที่ต้องการเสมอไป ตัวอย่างเช่นเราต้องมี clean
เรียกใช้งานก่อน html
เพื่อให้แน่ใจว่าโฟลเดอร์ปลายทางของเราว่างเปล่าก่อนที่จะย้ายไฟล์เข้ามา เมื่อปล่อยอึก 4 มันจะรองรับ gulp.series
และ gulp.parallel
วิธีการโดยกำเนิด แต่สำหรับตอนนี้เราต้องทิ้งไว้ด้วยมุมแหลมเล็กน้อยนี้ในการตั้งค่าของเรา
ยิ่งไปกว่านั้นนี่คือความสง่างามจริงๆ การสร้างและโฮสต์แอปทั้งหมดของเราดำเนินการในคำสั่งเดียวและการทำความเข้าใจส่วนใดส่วนหนึ่งของเวิร์กโฟลว์นั้นทำได้ง่ายเพียงแค่ตรวจสอบงานแต่ละงานในลำดับการรัน นอกจากนี้เราสามารถแบ่งลำดับทั้งหมดออกเป็นกลุ่มย่อย ๆ เพื่อเป็นแนวทางที่ละเอียดยิ่งขึ้นในการสร้างและโฮสต์แอป ตัวอย่างเช่นเราสามารถตั้งค่างานแยกต่างหากที่เรียกว่า validate
ที่เรียกใช้ lint
และ test
งาน หรือเราอาจมี host
งานที่รัน server
และ watch
. ความสามารถในการจัดระเบียบงานนี้มีประสิทธิภาพมากโดยเฉพาะอย่างยิ่งเมื่อแอปพลิเคชันของคุณปรับขนาดและต้องการงานอัตโนมัติมากขึ้น
if (argv.prod) { process.env.NODE_ENV = 'production'; } let PROD = process.env.NODE_ENV === 'production';
การใช้ yargs
ไลบรารี NPM เราสามารถจัดหาแฟล็กบรรทัดคำสั่งให้กับ Gulp ที่นี่ฉันสั่งให้ gulpfile ตั้งค่าสภาพแวดล้อมโหนดเป็นใช้งานจริงถ้า --prod
ถูกส่งไปที่ gulp
ในเทอร์มินัล ของเรา PROD
จากนั้นจะใช้ตัวแปรเป็นเงื่อนไขเพื่อแยกความแตกต่างของพัฒนาการและพฤติกรรมการผลิตใน gulpfile ของเรา ตัวอย่างเช่นหนึ่งในตัวเลือกที่เราส่งต่อไปยัง browserify
ของเรา config คือ:
plugin: PROD ? [] : [hmr, watchify]
สิ่งนี้บอก browserify
เพื่อไม่ใช้ปลั๊กอินใด ๆ ในโหมดการผลิตและใช้ hmr
และ watchify
ปลั๊กอินในสภาพแวดล้อมอื่น ๆ
นี้ PROD
conditional มีประโยชน์มากเพราะช่วยให้เราไม่ต้องเขียน gulpfile แยกต่างหากสำหรับการผลิตและการพัฒนาซึ่งท้ายที่สุดจะมีการซ้ำโค้ดจำนวนมาก แต่เราสามารถทำสิ่งต่างๆเช่น gulp --prod
เพื่อรันงานเริ่มต้นในการผลิตหรือ gulp html --prod
เพื่อเรียกใช้ html
งานในการผลิต ในทางกลับกันเราเห็นก่อนหน้านี้ว่าทิ้งขยะไปป์ไลน์ของเราด้วยข้อความเช่น .pipe(cond(!PROD, livereload()))
ไม่ได้อ่านมากที่สุด ในท้ายที่สุดมันก็ขึ้นอยู่กับความชอบว่าคุณต้องการใช้วิธีการตัวแปรบูลีนหรือตั้งค่าสอง gulpfiles แยกกัน
ตอนนี้เรามาดูกันว่าจะเกิดอะไรขึ้นเมื่อเรายังคงใช้ Gulp เป็นตัวเรียกใช้งาน แต่แทนที่ Browserify ด้วย Webpack
วิธีการสร้างกองทุนรวมการลงทุนด้านอสังหาริมทรัพย์
ทันใดนั้น gulpfile ของเรามีความยาวเพียง 99 บรรทัดโดยมีการนำเข้า 12 ครั้งซึ่งค่อนข้างลดลงจากการตั้งค่าก่อนหน้านี้! หากเราตรวจสอบงานเริ่มต้น:
gulp.task('default', (cb) => { runSequence('lint', 'test', 'build', 'server', 'watch', cb); });
ตอนนี้การตั้งค่าแอปพลิเคชันเว็บเต็มรูปแบบของเราต้องการเพียงห้างานแทนที่จะเป็นเก้าอย่างซึ่งเป็นการปรับปรุงอย่างมาก
นอกจากนี้เรายังไม่จำเป็นต้องมี livereload
ของเรา watch
ตอนนี้งานเป็นเพียง:
gulp.task('watch', () => { gulp.watch(config.paths.js, () => { runSequence('lint', 'test'); }); });
ซึ่งหมายความว่าผู้เฝ้าดูอึกทึกของเราไม่ได้กระตุ้นให้เกิดพฤติกรรมการคืนเงินทุกประเภท เราไม่จำเป็นต้องโอน index.html
เพื่อเป็นโบนัสเพิ่มเติม จาก app
ถึง dist
หรือ build
อีกต่อไป.
กลับโฟกัสของเราไปที่การลดภารกิจ html
, css
, js
ของเรา และ fonts
งานทั้งหมดถูกแทนที่ด้วย build
งาน:
gulp.task('build', () => { runSequence('clean', 'html'); return gulp.src(config.paths.entry) .pipe(webpack(require('./webpack.config'))) .pipe(gulp.dest(config.paths.baseDir)); });
ง่ายพอ เรียกใช้ clean
และ html
งานตามลำดับ เมื่อเสร็จแล้วให้จับจุดเข้าใช้งานของเราไปป์ผ่าน Webpack ผ่าน webpack.config.js
ไฟล์เพื่อกำหนดค่าและส่งบันเดิลผลลัพธ์ไปยัง baseDir
ของเรา (อย่างใดอย่างหนึ่ง dist
หรือ build
ขึ้นอยู่กับโหนด env)
มาดูไฟล์กำหนดค่า Webpack:
นี่เป็นไฟล์กำหนดค่าที่ค่อนข้างใหญ่และน่ากลัวดังนั้นเรามาอธิบายคุณสมบัติที่สำคัญบางอย่างที่ตั้งค่าไว้ใน module.exports
ของเรา วัตถุ.
devtool: PROD ? 'source-map' : 'eval-source-map',
ตั้งค่าประเภทของซอร์สแมปที่ Webpack จะใช้ Webpack ไม่เพียงรองรับซอร์สแมปนอกกรอบเท่านั้น แต่ยังรองรับตัวเลือกซอร์สแมปที่หลากหลาย แต่ละตัวเลือกจะให้ความสมดุลของรายละเอียดแผนที่แหล่งที่มาแตกต่างกันกับความเร็วในการสร้างใหม่ (เวลาที่ใช้ในการรวมกลุ่มใหม่เมื่อมีการเปลี่ยนแปลง) ซึ่งหมายความว่าเราสามารถใช้ตัวเลือกซอร์สแมป 'ราคาถูก' สำหรับการพัฒนาเพื่อให้โหลดซ้ำได้อย่างรวดเร็วและตัวเลือกซอร์สแมปที่แพงกว่าในการผลิต
entry: PROD ? './app/index' : [ 'webpack-hot-middleware/client?reload=true', // reloads the page if hot module reloading fails. './app/index' ]
นี่คือจุดเข้าบันเดิลของเรา สังเกตว่ามีการส่งอาร์เรย์ซึ่งหมายความว่ามีจุดเข้าใช้งานหลายจุด ในกรณีนี้เรามีจุดเริ่มต้นที่คาดหวัง app/index.js
เช่นเดียวกับ webpack-hot-middleware
จุดเริ่มต้นที่ใช้เป็นส่วนหนึ่งของการตั้งค่าการโหลดโมดูลใหม่
output: { path: PROD ? __dirname + '/build' : __dirname + '/dist', publicPath: '/', filename: 'bundle.js' },
นี่คือที่ที่จะแสดงบันเดิลที่คอมไพล์ ตัวเลือกที่สับสนที่สุดคือ publicPath
ตั้งค่า URL พื้นฐานสำหรับตำแหน่งที่บันเดิลของคุณจะโฮสต์บนเซิร์ฟเวอร์ ตัวอย่างเช่นถ้าคุณ publicPath
คือ /public/assets
จากนั้นกลุ่มจะปรากฏใต้ /public/assets/bundle.js
บนเซิร์ฟเวอร์
devServer: { contentBase: PROD ? './build' : './app' }
สิ่งนี้จะบอกเซิร์ฟเวอร์ว่าจะใช้โฟลเดอร์ใดในโครงการของคุณเป็นไดเรกทอรีรากของเซิร์ฟเวอร์
หากคุณเคยสับสนเกี่ยวกับวิธีที่ Webpack แมปบันเดิลที่สร้างขึ้นในโปรเจ็กต์ของคุณกับบันเดิลบนเซิร์ฟเวอร์โปรดจำสิ่งต่อไปนี้:
path
+ filename
: ตำแหน่งที่แน่นอนของบันเดิลในซอร์สโค้ดโครงการของคุณcontentBase
(ในฐานะรูท /
) + publicPath
: ตำแหน่งของบันเดิลบนเซิร์ฟเวอร์plugins: PROD ? [ new webpack.optimize.OccurenceOrderPlugin(), new webpack.DefinePlugin(GLOBALS), new ExtractTextPlugin('bundle.css'), new webpack.optimize.DedupePlugin(), new webpack.optimize.UglifyJsPlugin({compress: {warnings: false}}) ] : [ new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin() ],
นี่คือปลั๊กอินที่ช่วยเพิ่มประสิทธิภาพการทำงานของ Webpack ในทางใดทางหนึ่ง ตัวอย่างเช่น webpack.optimize.UglifyJsPlugin
เป็นผู้รับผิดชอบในการลดขนาด
loaders: [ {test: /.js$/, include: path.join(__dirname, 'app'), loaders: ['babel']}, { test: /.css$/, loader: PROD ? ExtractTextPlugin.extract('style', 'css?sourceMap'): 'style!css?sourceMap' }, { test: /.scss$/, loader: PROD ? ExtractTextPlugin.extract('style', 'css?sourceMap!resolve-url!sass?sourceMap') : 'style!css?sourceMap!resolve-url!sass?sourceMap' }, gif)(?S*)?$/, loader: 'url?limit=100000&name=img/[name].[ext]', woff2 ]
เหล่านี้คือรถตัก โดยพื้นฐานแล้วพวกเขาประมวลผลไฟล์ล่วงหน้าที่โหลดผ่าน require()
งบ พวกมันค่อนข้างคล้ายกับท่อ Gulp ตรงที่คุณสามารถโซ่รถตักเข้าด้วยกันได้
ลองตรวจสอบวัตถุตัวโหลดของเรา:
{test: /.scss$/, loader: 'style!css?sourceMap!resolve-url!sass?sourceMap'}
test
คุณสมบัติบอก Webpack ว่าตัวโหลดที่กำหนดจะใช้ถ้าไฟล์ตรงกับรูปแบบ regex ที่ระบุในกรณีนี้ /.scss$/
loader
คุณสมบัติสอดคล้องกับการกระทำที่ตัวโหลดดำเนินการ ที่นี่เราเชื่อมโยงกัน style
, css
, resolve-url
และ sass
รถตักซึ่งดำเนินการในลำดับย้อนกลับ
ฉันต้องยอมรับว่าฉันไม่พบ loader3!loader2!loader1
ไวยากรณ์ที่สวยงามมาก ท้ายที่สุดคุณต้องอ่านอะไรในโปรแกรมจากขวาไปซ้ายเมื่อใด อย่างไรก็ตามสิ่งนี้รถตักเป็นคุณลักษณะที่มีประสิทธิภาพมากของ webpack อันที่จริงตัวโหลดที่ฉันเพิ่งพูดถึงช่วยให้เราสามารถนำเข้าไฟล์ SASS ไปยัง JavaScript ของเราได้โดยตรง! ตัวอย่างเช่นเราสามารถนำเข้าผู้จัดจำหน่ายและสไตล์ชีตส่วนกลางในไฟล์จุดเริ่มต้นของเรา:
import React from 'react'; import {render} from 'react-dom'; import {Router, browserHistory} from 'react-router'; import routes from './routes'; // CSS imports import '../node_modules/bootstrap/dist/css/bootstrap.css'; import '../node_modules/font-awesome/css/font-awesome.css'; import './styles/global.scss'; render(, document.getElementById('app'));
ในทำนองเดียวกันในองค์ประกอบส่วนหัวของเราเราสามารถเพิ่ม import './Header.scss'
เพื่อนำเข้าสไตล์ชีตที่เกี่ยวข้องของคอมโพเนนต์ นอกจากนี้ยังใช้กับส่วนประกอบอื่น ๆ ทั้งหมดของเรา
ในความคิดของฉันสิ่งนี้แทบจะถือได้ว่าเป็นการเปลี่ยนแปลงครั้งใหม่ในโลกของการพัฒนา JavaScript ไม่จำเป็นต้องกังวลเกี่ยวกับการรวม CSS การย่อขนาดหรือซอร์สแมปเนื่องจากตัวโหลดของเราจัดการทั้งหมดนี้ให้เรา แม้แต่การรีโหลดโมดูลแบบร้อนก็ใช้ได้กับไฟล์ CSS ของเรา จากนั้นความสามารถในการจัดการการนำเข้า JS และ CSS ในไฟล์เดียวกันทำให้การพัฒนาง่ายขึ้นตามแนวคิด: มีความสม่ำเสมอมากขึ้นสลับบริบทน้อยลงและหาเหตุผลได้ง่ายขึ้น
เพื่อให้สรุปสั้น ๆ ว่าคุณลักษณะนี้ทำงานอย่างไร: Webpack จะแทรก CSS ลงในบันเดิล JS ของเรา ในความเป็นจริง Webpack สามารถทำได้สำหรับรูปภาพและแบบอักษรเช่นกัน:
gif)(?S*)?$/, loader: 'url?limit=100000&name=img/[name].[ext]', ttf)(?S*)?$/, loader: 'url?limit=100000&name=fonts/[name].[ext]'
ตัวโหลด URL สั่งให้ Webpack แทรกรูปภาพและฟอนต์ของเราเป็น URL ข้อมูลหากมีขนาดไม่เกิน 100 KB มิฉะนั้นจะใช้เป็นไฟล์แยกกัน แน่นอนว่าเราสามารถกำหนดขนาดคัตออฟเป็นค่าอื่นได้เช่น 10 KB
และนั่นคือการกำหนดค่า Webpack โดยสรุป ฉันยอมรับว่ามีการตั้งค่าในระดับที่เหมาะสม แต่ประโยชน์ของการใช้งานนั้นเป็นเรื่องที่น่าอัศจรรย์ แม้ว่า Browserify จะมีปลั๊กอินและการแปลง แต่ก็ไม่สามารถเปรียบเทียบกับตัวโหลด Webpack ในแง่ของฟังก์ชันการทำงานที่เพิ่มเข้ามา
ในการตั้งค่านี้เราใช้สคริปต์ npm โดยตรงแทนที่จะใช้ gulpfile ในการทำงานอัตโนมัติ
'scripts': { 'start': 'npm-run-all --parallel lint:watch test:watch build', 'start:prod': 'npm-run-all --parallel lint test build:prod', 'clean-dist': 'rimraf ./dist && mkdir dist', 'clean-build': 'rimraf ./build && mkdir build', 'clean': 'npm-run-all clean-dist clean-build', 'test': 'mocha ./app/**/*.test.js --compilers js:babel-core/register', 'test:watch': 'npm run test -- --watch', 'lint': 'esw ./app/**/*.js', 'lint:watch': 'npm run lint -- --watch', 'server': 'nodemon server.js', 'server:prod': 'cross-env NODE_ENV=production nodemon server.js', 'build-html': 'node tools/buildHtml.js', 'build-html:prod': 'cross-env NODE_ENV=production node tools/buildHtml.js', 'prebuild': 'npm-run-all clean-dist build-html', 'build': 'webpack', 'postbuild': 'npm run server', 'prebuild:prod': 'npm-run-all clean-build build-html:prod', 'build:prod': 'cross-env NODE_ENV=production webpack', 'postbuild:prod': 'npm run server:prod' }
ในการรันรุ่นการพัฒนาและการผลิตให้ป้อน npm start
และ npm run start:prod
ตามลำดับ
สิ่งนี้มีขนาดกะทัดรัดกว่า gulpfile ของเราอย่างแน่นอนเนื่องจากเราได้ตัดโค้ด 99 ถึง 150 บรรทัดลงเหลือ 19 NPM scripts หรือ 12 ถ้าเราไม่รวมสคริปต์การผลิต (ซึ่งส่วนใหญ่จะสะท้อนสคริปต์การพัฒนาด้วยสภาพแวดล้อมโหนดที่ตั้งค่าเป็น production ). ข้อเสียเปรียบคือคำสั่งเหล่านี้ค่อนข้างคลุมเครือเมื่อเทียบกับงาน Gulp ของเราและไม่ค่อยแสดงออก ตัวอย่างเช่นไม่มีทาง (อย่างน้อยก็ที่ฉันรู้) ที่จะให้สคริปต์ npm เดียวรันคำสั่งบางคำสั่งในซีรีส์และอื่น ๆ ควบคู่กันไป เป็นอย่างใดอย่างหนึ่ง
อย่างไรก็ตามมีข้อได้เปรียบอย่างมากสำหรับแนวทางนี้ โดยใช้ไลบรารี NPM เช่น mocha
โดยตรงจากบรรทัดคำสั่งคุณไม่จำเป็นต้องติดตั้ง Gulp wrapper ที่เท่ากันสำหรับแต่ละตัว (ในกรณีนี้คือ gulp-mocha
)
แทนที่จะติดตั้ง NPM
เราติดตั้งแพ็คเกจต่อไปนี้:
อ้างถึงโพสต์ของ Cory House ทำไมฉันถึงทิ้ง Gulp และ Grunt สำหรับสคริปต์ NPM :
ฉันเป็นแฟนตัวยงของอึก แต่ในโปรเจ็กต์สุดท้ายของฉันฉันจบลงด้วยหลายร้อยบรรทัดใน gulpfile ของฉันและปลั๊กอิน Gulp ประมาณหนึ่งโหล ฉันกำลังดิ้นรนเพื่อรวม Webpack, Browsersync, การโหลดซ้ำแบบร้อน, Mocha และอื่น ๆ อีกมากมายโดยใช้ Gulp ทำไม? ปลั๊กอินบางตัวมีเอกสารไม่เพียงพอสำหรับกรณีการใช้งานของฉัน ปลั๊กอินบางตัวเปิดเผยเฉพาะบางส่วนของ API ที่ฉันต้องการ มีข้อผิดพลาดแปลก ๆ ซึ่งจะดูไฟล์จำนวนเล็กน้อยเท่านั้น สีลอกอีกสีหนึ่งเมื่อส่งออกไปยังบรรทัดคำสั่ง
เขาระบุประเด็นหลักสามประการเกี่ยวกับ Gulp:
วิธีคอมไพล์โค้ด c++
ฉันมักจะเห็นด้วยกับสิ่งเหล่านี้ทั้งหมด
เมื่อใดก็ตามที่ห้องสมุดเช่น eslint
ได้รับการอัปเดตที่เกี่ยวข้อง gulp-eslint
ไลบรารีต้องการการอัปเดตที่เกี่ยวข้อง หากผู้ดูแลห้องสมุดสูญเสียความสนใจเวอร์ชันอึกของห้องสมุดจะไม่ซิงค์กับไลบรารีดั้งเดิม เช่นเดียวกับเมื่อสร้างไลบรารีใหม่ ถ้ามีคนสร้างห้องสมุด xyz
และมันจับได้ทันใดนั้นคุณต้องมี gulp-xyz
ห้องสมุดเพื่อใช้ในงานอึกของคุณ
ในแง่หนึ่งวิธีนี้ไม่ได้ปรับขนาด ตามหลักการแล้วเราต้องการแนวทางเช่น Gulp ที่สามารถใช้ไลบรารีดั้งเดิมได้
แม้ว่าไลบรารีเช่น gulp-plumber
ช่วยบรรเทาปัญหานี้ได้มาก แต่อย่างไรก็ตามข้อผิดพลาดในการรายงานใน gulp
ก็ไม่เป็นประโยชน์มาก หากแม้แต่ท่อเดียวส่งข้อยกเว้นที่ไม่สามารถจัดการได้คุณจะได้รับสแต็กแทร็กสำหรับปัญหาที่ดูเหมือนว่าไม่เกี่ยวข้องกับสิ่งที่ทำให้เกิดปัญหาในซอร์สโค้ดของคุณ สิ่งนี้สามารถทำให้การแก้ไขจุดบกพร่องกลายเป็นฝันร้ายได้ในบางกรณี ไม่มีจำนวนการค้นหาบน Google หรือ Stack Overflow ที่สามารถช่วยคุณได้หากข้อผิดพลาดนั้นคลุมเครือหรือทำให้เข้าใจผิดเพียงพอ
บ่อยครั้งที่ฉันพบว่ามันเล็ก gulp
ห้องสมุดมักจะมีเอกสารประกอบที่ จำกัด มาก ฉันสงสัยว่านี่เป็นเพราะผู้เขียนมักจะสร้างห้องสมุดเพื่อการใช้งานของตนเองเป็นหลัก นอกจากนี้เป็นเรื่องปกติที่จะต้องดูเอกสารประกอบสำหรับทั้งปลั๊กอิน Gulp และไลบรารีเนทีฟซึ่งหมายถึงการสลับบริบทจำนวนมากและการอ่านข้อมูลที่ต้องทำมากกว่าสองเท่า
ดูเหมือนชัดเจนสำหรับฉันว่า Webpack เป็นที่ต้องการของ Browserify และสคริปต์ NPM นั้นดีกว่าสำหรับ Gulp แม้ว่าแต่ละตัวเลือกจะมีประโยชน์และข้อเสียก็ตาม Gulp มีความชัดเจนและสะดวกในการใช้งานมากกว่าสคริปต์ NPM แต่คุณจ่ายราคาในสิ่งที่เป็นนามธรรมที่เพิ่มเข้ามาทั้งหมด
ชุดค่าผสมบางอย่างอาจไม่สมบูรณ์แบบสำหรับแอปของคุณ แต่หากคุณต้องการหลีกเลี่ยงการพึ่งพาการพัฒนาจำนวนมากและประสบการณ์การดีบักที่น่าหงุดหงิด Webpack พร้อมสคริปต์ NPM คือหนทางที่จะไป ฉันหวังว่าบทความนี้จะเป็นประโยชน์ในการเลือกเครื่องมือที่เหมาะสมสำหรับโครงการต่อไปของคุณ
ที่เกี่ยวข้อง: