DevTips – วิธีรวบรวมทรัพย์สินของคุณอย่างมีประสิทธิภาพ

เผยแพร่แล้ว: 2022-08-25

เป้าหมายหนึ่งที่เรามีสำหรับปีนี้คือการปรับโครงสร้างปลั๊กอินหลักสองตัวของเรา (เนื้อหา Nelio และการทดสอบ Nelio A/B) เป็น TypeScript และ React Hooks เราผ่านไปเพียงครึ่งปีและเราสามารถพูดได้ว่าเป้าหมายนี้ประสบความสำเร็จอย่างสมบูรณ์ อย่างไรก็ตาม ฉันต้องยอมรับ: ถนนซับซ้อนกว่าที่คาดไว้เล็กน้อย… โดยเฉพาะอย่างยิ่งหากเราพิจารณาว่าหลังจากแนะนำ TypeScript ของเรา เวลาสร้างปลั๊กอินเปลี่ยนจากไม่กี่วินาทีเป็นมากกว่าสองนาที! มีบางอย่างผิดปกติ และเราไม่รู้ว่าอะไร

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

โครงการของเล่น

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

การสร้างปลั๊กอิน

สิ่งแรกที่คุณควรทำคือสร้างโฟลเดอร์ใหม่ที่มีชื่อปลั๊กอินของคุณ (เช่น nelio ) ในไดเร็กทอรี /wp-content/plugins ของ WordPress จากนั้นเพิ่มไฟล์หลัก ( nelio.php ) ด้วยเนื้อหาต่อไปนี้:

 <?php /** * Plugin Name: Nelio * Description: This is a test plugin. * Version: 0.0.1 * * Author: Nelio Software * Author URI: https://neliosoftware.com * License: GPL-2.0+ * License URI: http://www.gnu.org/licenses/gpl-2.0.txt * * Text Domain: nelio */ if ( ! defined( 'ABSPATH' ) ) { exit; }

หากคุณทำถูกต้องแล้ว คุณจะเห็นว่าขณะนี้คุณสามารถเปิดใช้งานปลั๊กอินใน WordPress ได้:

สกรีนช็อตของปลั๊กอินของเล่นของเราในรายการปลั๊กอิน
สกรีนช็อตของปลั๊กอินของเล่นของเราในรายการปลั๊กอิน

แน่นอนว่าปลั๊กอินยังไม่ได้ทำอะไรเลย… แต่อย่างน้อยก็ปรากฏขึ้น

TypeScript

มาเพิ่มโค้ด TypeScript กันเถอะ! สิ่งแรกที่เราจะทำคือเริ่มต้น npm ในโฟลเดอร์ปลั๊กอินของเรา เรียกใช้สิ่งนี้:

 npm init

และปฏิบัติตามคำแนะนำบนหน้าจอ จากนั้นติดตั้งการพึ่งพา:

 npm add -D @wordpress/scripts @wordpress/i18n

และแก้ไขไฟล์ package.json เพื่อเพิ่มสคริปต์บิลด์ที่ @wordpress/scripts ต้องการ:

 { ... "scripts": { "build": "wp-scripts build", "start": "wp-scripts start", }, ... }

เมื่อ npm พร้อมแล้ว มาปรับแต่ง TypeScript โดยเพิ่มไฟล์ tsconfig.json :

 { "compilerOptions": { "target": "es5", "module": "esnext", "moduleResolution": "node", "outDir": "build", "lib": [ "es7", "dom" ] }, "exclude": [ "node_modules" ] }

สุดท้าย มาเขียนโค้ด TS กัน เราต้องการให้สิ่งนี้ง่ายมาก แต่ "ใกล้เคียงพอ" กับสิ่งที่เรามีในการทดสอบ A/B ของ Nelio และเนื้อหา Nelio ดังนั้นให้สร้างโฟลเดอร์ src ในโครงการของเราด้วยไฟล์ TypeScript สองสามไฟล์ภายใน: index.ts และ utils/index.ts เอส

ในอีกด้านหนึ่ง สมมติว่า utils/index.ts เป็นแพ็คเกจยูทิลิตี้ กล่าวคือมีฟังก์ชันบางอย่างที่ไฟล์อื่นๆ ในโครงการของเราอาจจำเป็นต้องใช้ ตัวอย่างเช่น สมมติว่ามีฟังก์ชัน min และ max แบบคลาสสิก:

 export const min = ( a: number, b: number ): number => a < b ? a : b; export const max = ( a: number, b: number ): number => a > b ? a : b;

ในทางกลับกัน มาดูไฟล์หลักของแอปของเรากัน: index.ts เพื่อจุดประสงค์ในการทดสอบ เราต้องการเพียงสคริปต์ง่ายๆ ที่ใช้แพ็คเกจยูทิลิตี้ของเราและการพึ่งพา WordPress บางอย่างเช่นนี้:

 import { __, sprintf } from '@wordpress/i18n'; import { min } from './utils'; const a = 2; const b = 3; const m = min( a, b ); console.log( sprintf( /* translators: 1 -> num, 2 -> num, 3 -> num */ __( 'Min between %1$s and %2$s is %3$s', 'nelio' ), a, b, m ) );

@wordpress/scripts การตั้งค่าเริ่มต้น

ถ้าเราจะสร้างโครงการตอนนี้โดยใช้ npm run build ทุกอย่างจะทำงานนอกกรอบ และนั่นเป็นเพียงเพราะ @wordpress/scripts (เช่น เครื่องมือพื้นฐานที่เราใช้สำหรับสร้างโครงการของเรา) ได้รับการออกแบบให้ทำงานกับ codebase เช่นเดียวกับในตัวอย่างของเรา นั่นคือ หากเรามีไฟล์ index.ts ในโฟลเดอร์ src ไฟล์นั้นจะสร้างไฟล์ index.js ในโฟลเดอร์ build พร้อมกับไฟล์อ้างอิง index.asset.php :

 > ls build index.asset.php index.js

ทำไมต้องสองไฟล์? อันหนึ่งเป็นไฟล์ JavaScript ที่คอมไพล์แล้ว (duh) และอีกอันเป็นไฟล์อ้างอิงที่มีข้อมูลที่เป็นประโยชน์เกี่ยวกับสคริปต์ของเรา โดยเฉพาะอย่างยิ่ง มันบอกเราว่าไลบรารี JavaScript ใดจากไลบรารี่ที่รวมอยู่ใน WordPress มันอาศัย ตัวอย่างเช่น index.ts ของเราใช้แพ็คเกจ @wordpress/i18n เพื่อทำให้สตริงเป็นสากล และนั่นคือไลบรารีที่รวมอยู่ใน WordPress ดังนั้น... ใช่ wp-i18n จะแสดงใน index.asset.php :

 build/index.asset.php <?php return array( 'dependencies' => array( 'wp-i18n' ), 'version' => 'c6131c7f24df4fa803b7', );

ขออภัย การกำหนดค่าเริ่มต้นไม่สมบูรณ์แบบ ถ้าคุณถามฉัน นี่คือเหตุผล

หากเราแนะนำจุดบกพร่องในโค้ดของคุณ (เช่น เรียกใช้ฟังก์ชัน min ด้วย string arg แทน number ):

 const m = min( `${ a }`, b );

สิ่งนี้ควรทำให้เกิดข้อผิดพลาด แต่มันไม่ได้ มันรวบรวมโดยไม่มีปัญหา

การตรวจสอบประเภทระหว่างการคอมไพล์ด้วย TypeScript

เพื่อแก้ไข "ข้อจำกัด" ดังกล่าว เราเพียงแค่ต้องสร้างไฟล์กำหนดค่า webpack ของเราเองและบอกให้ใช้ tsc (คอมไพเลอร์ TypeScript) เมื่อใดก็ตามที่พบโค้ด TS กล่าวอีกนัยหนึ่ง เราต้องการไฟล์ webpack.config.json ต่อไปนี้:

 const defaultConfig = require( '@wordpress/scripts/config/webpack.config' ); const config = { ...defaultConfig, module: { ...defaultConfig.module, rules: [ ...defaultConfig.module.rules, { test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/, }, ], }, }; module.exports = { ...config, entry: './src/index', output: { path: __dirname + '/build', filename: 'index.js', }, };

อย่างที่คุณเห็น มันเริ่มต้นด้วยการโหลดการกำหนดค่า webpack เริ่มต้นที่รวมอยู่ในแพ็คเกจ @wordpress/scripts จากนั้นขยาย defaultConfig โดยการเพิ่ม ts-loader ให้กับไฟล์ .ts ทั้งหมด ง่ายนิดเดียว!

และตอนนี้ ดูเถิด:

 > npm run build ... ERROR in ...src/index.ts TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. webpack 5.74.0 compiled with 1 error in 4470 ms

การรวบรวมโครงการของเราส่งผลให้เกิดข้อผิดพลาด เย่! แน่นอนว่ามันช้ากว่าเล็กน้อย แต่อย่างน้อยเราก็มีการตรวจสอบความปลอดภัยก่อนที่จะอัปโหลดอะไรไปยังการผลิต

การจัดคิวสคริปต์

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

เปิด nelio.php และต่อท้ายตัวอย่างต่อไปนี้:

 add_action( 'admin_enqueue_scripts', function() { $path = untrailingslashit( plugin_dir_path( __FILE__ ) ); $url = untrailingslashit( plugin_dir_url( __FILE__ ) ); $asset = require( $path . '/build/index.asset.php' ); wp_enqueue_script( 'nelio', $url . '/build/index.js', $asset['dependencies'], $asset['version'] ); } );

ถัดไป ไปที่แดชบอร์ดของ WordPress (หน้าใดก็ได้ตามคำแนะนำ) และดูที่คอนโซล JavaScript ของเบราว์เซอร์ของคุณ คุณควรเห็นข้อความต่อไปนี้:

 Min between 2 and 3 is 2

ดี!

สิ่งที่เกี่ยวกับการพึ่งพาของฉัน?

มาพูดคุยกันสักครู่เกี่ยวกับการจัดการการพึ่งพาใน JavaScript/webpack/WordPress @wordpress/scripts ได้รับการกำหนดค่าในลักษณะที่ตามค่าเริ่มต้น หากโปรเจ็กต์ของคุณใช้การขึ้นต่อกันที่บรรจุอยู่ใน WordPress core โปรเจ็กต์นั้นก็จะถูกระบุไว้ในไฟล์ . .asset.php ตัวอย่างเช่น สิ่งนี้อธิบายว่าทำไม @wordpress/i18n จึงถูกแสดงอยู่ในไฟล์การขึ้นต่อกันของสคริปต์ของเรา

แต่การพึ่งพาแพ็คเกจ "อื่นๆ" ล่ะ เกิดอะไรขึ้นกับแพ็คเกจ utils ของเรา เรื่องสั้นโดยย่อ: โดยค่าเริ่มต้น webpack จะรวบรวมและรวมการพึ่งพาทั้งหมดเข้ากับสคริปต์เอาต์พุต เพียงแค่ดูที่ไฟล์ JS ที่สร้างขึ้น (คอมไพล์ด้วย npm run start เพื่อปิดใช้งานการลดขนาด):

 ... var __webpack_modules__ = ({ "./src/utils/index.ts": ((...) => ... var min = function (a, b) { return a < b ? a : b; }; var max = function (a, b) { return a > b ? a : b; }; }), ...

ดู? รหัส utils ของเราอยู่ที่นั่น ฝังอยู่ในสคริปต์เอาต์พุตของเรา

แล้ว @wordpress/i18n ล่ะ? เป็นเพียงการอ้างอิงง่ายๆ ถึงตัวแปรส่วนกลาง:

 ... var __webpack_modules__ = ({ "./src/utils/index.ts": ..., "@wordpress/i18n": ((module)) => { module.exports = window["wp"]["i18n"]; }) ...

อย่างที่ฉันพูด @wordpress/scripts มาพร้อมกับปลั๊กอิน Dependency Extraction Webpack Plugin ที่ "ยกเว้น" การพึ่งพาบางอย่างจากกระบวนการรวบรวมและสร้างโค้ดโดยสมมติว่าจะพร้อมใช้งานในขอบเขตทั่วโลก ในตัวอย่างของเรา เราจะเห็นว่า @wordpress/i18n อยู่ใน wp.i18n นั่นเป็นเหตุผลที่เมื่อจัดคิวสคริปต์ของเรา เราจึงต้องจัดคิวการพึ่งพาสคริปต์ด้วย

การกำหนดค่าแบบกำหนดเองเพื่อสร้างสคริปต์แยกกันสองชุด

จากทั้งหมดนี้ สมมติว่าเราต้องการบรรลุสิ่งเดียวกันด้วยแพ็คเกจ utils ของเรา นั่นคือ เราไม่ต้องการให้เนื้อหาถูกฝังใน index.js แต่ควรคอมไพล์เป็นไฟล์ . .js ของตัวเอง และปรากฏเป็นการพึ่งพา index.asset.php เราจะทำอย่างนั้นได้อย่างไร?

อันดับแรก เราควรเปลี่ยนชื่อคำสั่ง import ใน index.js เพื่อให้ ดูเหมือน เป็นแพ็คเกจจริง กล่าวอีกนัยหนึ่งแทนที่จะนำเข้าสคริปต์โดยใช้เส้นทางสัมพัทธ์ ( ./utils ) คงจะดีถ้าเราสามารถใช้ชื่อเช่น @nelio/utils ในการทำเช่นนี้ สิ่งที่คุณต้องทำคือแก้ไขไฟล์ package.json ของโปรเจ็กต์เพื่อเพิ่มการพึ่งพาใหม่ใน dependencies :

 { ... "dependencies": { "@nelio/utils": "./src/utils" }, "devDependencies": { "@wordpress/i18n": ..., "@wordpress/scripts": ... }, ... }

รันการ npm install เพื่อสร้าง symlink ใน node_modules ที่ชี้ไปที่แพ็คเกจ "ใหม่" นี้ และสุดท้ายให้รัน npm init ใน src/utils ดังนั้นจากมุมมองของ @nelio/utils เป็นแพ็คเกจที่ถูกต้อง

จากนั้น ในการคอมไพล์ @nelio/utils เป็นสคริปต์ของตัวเอง เราต้องแก้ไขการกำหนดค่า webpack.config.js และกำหนดการส่งออกสองรายการ:

  • อันที่เรามีอยู่แล้ว ( ./src/index.ts )
  • การส่งออกอื่นเพื่อคอมไพล์ ./src/utils ไปยังไฟล์อื่น โดยเปิดเผยการส่งออกในตัวแปรส่วนกลางที่มีชื่อ เช่น nelio.utils

กล่าวอีกนัยหนึ่งเราต้องการสิ่งนี้:

 module.exports = [ { ...config, entry: './src/index', output: { path: __dirname + '/build', filename: 'index.js', }, }, { ...config, entry: './src/utils', output: { path: __dirname + '/build', filename: 'utils.js', library: [ 'nelio', 'utils' ], libraryTarget: 'window', }, }, ];

รวบรวมโค้ดอีกครั้งแล้วดูที่โฟลเดอร์ ./build คุณจะเห็นว่าตอนนี้เราทุกคนมีสคริปต์อยู่ 2 ตัว ดูที่ . ./build/utils.js แล้วคุณจะเห็นว่ามันกำหนด nelio.utils อย่างไร ตามที่คาดไว้:

 ... var min = function (a, b) { return a < b ? a : b; }; var max = function (a, b) { return a > b ? a : b; }; (window.nelio = window.nelio || {}).utils = __webpack_exports__; ... ... var min = function (a, b) { return a < b ? a : b; }; var max = function (a, b) { return a > b ? a : b; }; (window.nelio = window.nelio || {}).utils = __webpack_exports__; ...

ขออภัย เราเพิ่งผ่านไปเพียงครึ่งทาง หากคุณดูที่ . ./build/index.js ด้วย คุณจะเห็นว่า src/utils ยังคงฝังอยู่ในนั้น… มันควรจะเป็น “การพึ่งพาภายนอก” และใช้ตัวแปรส่วนกลางที่เราเพิ่งกำหนดไว้ไม่ใช่หรือ

กำหนดค่าเองเพื่อสร้างการพึ่งพาภายนอก

ในการแปลง @nelio/utils เป็นการพึ่งพาภายนอกที่แท้จริง เราจำเป็นต้องปรับแต่ง webpack ของเราเพิ่มเติมและใช้ประโยชน์จากปลั๊กอินการแยกส่วนการพึ่งพาที่เรากล่าวถึงก่อนหน้านี้ เพียงเปิดไฟล์ webpack.config.js อีกครั้งและแก้ไขตัวแปร config ดังนี้:

 const DependencyExtractionWebpackPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' ); const config = { ...defaultConfig, module: { ...defaultConfig.module, rules: [ ...defaultConfig.module.rules, { test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/, }, ], }, plugins: [ ...defaultConfig.plugins.filter( ( p ) => p.constructor.name !== 'DependencyExtractionWebpackPlugin' ), new DependencyExtractionWebpackPlugin( { requestToExternal: ( request ) => '@nelio/utils' === request ? [ 'nelio', 'utils' ] : undefined, requestToHandle: ( request ) => '@nelio/utils' === request ? 'nelio-utils' : undefined, outputFormat: 'php', } ), ], };

เพื่อให้การอ้างอิงถึง @nelio/utils ทั้งหมดถูกแปลเป็น nelio.utils ในโค้ด และมีการพึ่งพาตัวจัดการสคริปต์ nelio-utils หากเราดูที่การขึ้นต่อกันของทั้งสองสคริปต์ เราจะเห็นสิ่งต่อไปนี้:

 build/index.asset.php <?php return array( 'dependencies' => array('nelio-utils', 'wp-i18n')... ?> build/utils.asset.php <?php return array( 'dependencies' => array()... ?>

และถ้าเราดูใน . ./build/index.js เรายืนยันว่าการ @nelio/utils อยู่ภายนอก:

 ... var __webpack_modules__ = ({ "@nelio/utils": ((module)) => { module.exports = window["nelio"]["utils"]; }), "@wordpress/i18n": ((module)) => { module.exports = window["wp"]["i18n"]; }) ...

มีปัญหาสุดท้ายที่เราต้องแก้ไข ไปที่เบราว์เซอร์ของคุณ รีเฟรชหน้าแดชบอร์ด แล้วดูที่คอนโซล ไม่มีอะไรปรากฏขึ้นใช่ไหม ทำไม ตอนนี้ nelio ขึ้นอยู่กับ nelio-utils แต่สคริปต์นี้ไม่ได้ลงทะเบียนใน WordPress... ดังนั้นจึงไม่สามารถตอบสนองการพึ่งพาได้ในขณะนี้ ในการแก้ไขปัญหานี้ ให้แก้ไข nelio.php และลงทะเบียนสคริปต์ใหม่:

 add_action( 'admin_enqueue_scripts', function() { $path = untrailingslashit( plugin_dir_path( __FILE__ ) ); $url = untrailingslashit( plugin_dir_url( __FILE__ ) ); $asset = require( $path . '/build/utils.asset.php' ); wp_register_script( 'nelio-utils', $url . '/build/utils.js', $asset['dependencies'], $asset['version'] ); } );

วิธีเร่งความเร็วกระบวนการสร้าง

หากเรารันกระบวนการบิลด์หลายครั้งและเฉลี่ยระยะเวลาที่ใช้ในการทำให้เสร็จ เราจะเห็นว่าบิลด์ทำงานในเวลาประมาณ 10 วินาที:

 > yarn run build ... ./src/index.ts + 2 modules ... webpack 5.74.0 compiled successfully in 5703 ms ... ./src/utils/index.ts ... webpack 5.74.0 compiled successfully in 5726 m Done in 10.22s.

ซึ่งอาจดูเหมือนไม่มากนัก แต่นี่เป็นโครงการของเล่นง่ายๆ และอย่างที่ฉันพูด โครงการจริงอย่าง Nelio Content หรือ Nelio A/B Testing ต้องใช้เวลาในการรวบรวม

เหตุใดจึง "ช้ามาก" และเราจะทำอย่างไรเพื่อให้เร็วขึ้น เท่าที่ฉันสามารถบอกได้ ปัญหาอยู่ในการกำหนดค่า webpack ของเรา ยิ่งคุณมีการส่งออกใน module.exports ของ module.exports เวลาในการรวบรวมก็จะช้าลงเท่านั้น อย่างไรก็ตาม การส่งออกครั้งเดียวเป็นวิธีที่เร็วกว่ามาก

มาปรับโครงสร้างโปรเจ็กต์ของเราใหม่เล็กน้อยเพื่อใช้การส่งออกเดียว ก่อนอื่น สร้างไฟล์ export.ts ใน src/utils ด้วยเนื้อหาต่อไปนี้:

 export * as utils from './index';

จากนั้น แก้ไข webpack.config.js ของคุณเพื่อให้มีการส่งออกเดียวที่มีสองรายการ:

 module.exports = { ...config, entry: { index: './src/index', utils: './src/utils/export', }, output: { path: __dirname + '/dist', filename: 'js/[name].js', library: { name: 'nelio', type: 'assign-properties', }, }, };

สุดท้าย สร้างโครงการอีกครั้ง:

 > yarn run build ... built modules 522 bytes [built] ./src/index.ts + 2 modules ... ./src/utils/export.ts + 1 modules ... webpack 5.74.0 compiled successfully in 4339 ms Done in 6.02s.

ใช้เวลาเพียง 6 วินาที ซึ่งเกือบครึ่งหนึ่งของเวลาเดิม! ค่อนข้างเรียบร้อยใช่มั้ย

สรุป

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

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

ฉันหวังว่าคุณจะชอบโพสต์ ถ้าเป็นเช่นนั้นโปรดแบ่งปัน หากคุณทราบวิธีอื่นๆ ในการเพิ่มประสิทธิภาพ webpack โปรดบอกฉันในส่วนความคิดเห็นด้านล่าง ขอให้เป็นวันที่ดี!

ภาพเด่นโดย Saffu บน Unsplash