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