DevTips - كيفية تجميع أصولك بكفاءة
نشرت: 2022-08-25كان أحد أهدافنا لهذا العام هو إعادة تشكيل المكونين الإضافيين الرائدين لدينا (Nelio Content و Nelio A / B Testing) إلى TypeScript و React Hooks. حسنًا ، لقد مرت نصف عام فقط ويمكننا أن نقول بالفعل إن هذا الهدف قد حقق نجاحًا تامًا ، ومع ذلك ، يجب أن أعترف: لقد كان الطريق أكثر تعقيدًا قليلاً مما كان متوقعًا ... خاصة إذا أخذنا في الاعتبار ، بعد تقديم TypeScript ، ذهب وقت بناء البرنامج المساعد من بضع ثوان إلى أكثر من دقيقتين! كان هناك شيء خاطئ ولم نكن نعرف ماذا.
حسنًا ، في مقال اليوم ، أود أن أخبركم قليلاً عن تلك التجربة وما فعلناه لإصلاحها. بعد كل شيء ، نعلم جميعًا أن TypeScript ستعمل دائمًا على إبطاء عملية الإنشاء قليلاً (التحقق من النوع يأتي بسعر) ، لكن لا ينبغي أن يكون كثيرًا! حسنًا ، تنبيه المفسد: لم تكن المشكلة أبدًا في TypeScript ... لقد كان التكوين الخاص بي. TypeScript جعلها "واضحة" فقط. لذلك دعونا نبدأ ، أليس كذلك؟
مشروع لعبة
لمساعدتك في فهم المشكلة التي واجهناها قبل بضعة أسابيع وكيفية إصلاحها ، فإن أفضل ما يمكننا فعله هو إنشاء مثال بسيط للغاية لتتبعه. لنقم ببناء ملحق WordPress بسيط يستخدم TypeScript وكيف يمكن أن يؤدي التهيئة الخاطئة إلى بطء شديد في أوقات التجميع. إذا كنت بحاجة إلى بعض المساعدة للبدء ، فراجع هذا المنشور حول بيئات تطوير WordPress.
إنشاء البرنامج المساعد
أول شيء يجب عليك فعله هو إنشاء مجلد جديد باسم المكون الإضافي الخاص بك (على سبيل المثال ، nelio
) في دليل WordPress ' /wp-content/plugins
. ثم أضف الملف الرئيسي ( 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! أول شيء سنفعله هو تهيئة 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. نريد أن يكون هذا بسيطًا جدًا ولكن "قريبًا بدرجة كافية" مما كان لدينا في اختبار Nelio A / B ومحتوى 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
(أي الأداة الأساسية التي نستخدمها لبناء مشروعنا) قد تم تصميمها للعمل مع قاعدة بيانات مثل تلك الموجودة في مثالنا. أي ، إذا كان لدينا ملف 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
أحرف بدلاً من 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 الأساسية ، فسيتم إدراجه على هذا النحو في ملف .asset.php
. هذا ، على سبيل المثال ، يفسر سبب إدراج @wordpress/i18n
في ملف التبعيات في البرنامج النصي الخاص بنا.
ولكن ماذا عن التبعيات للحزم "الأخرى"؟ ماذا حدث لحزمة utils
؟ قصة طويلة قصيرة: تقوم حزمة الويب افتراضيًا بتجميع جميع التبعيات ودمجها في البرنامج النصي الناتج ، ما عليك سوى إلقاء نظرة على ملف 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
لإنشاء ارتباط رمزي في node_modules
للإشارة إلى هذه الحزمة "الجديدة" ، وأخيراً قم بتشغيل npm init
في src/utils
بحيث تكون @nelio/utils
من وجهة نظر npm حزمة صالحة.
بعد ذلك ، لتجميع @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
- سترى أن لدينا الآن نصين برمجيين. ألق نظرة على ./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.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
، كلما أصبح وقت الترجمة أبطأ. ومع ذلك ، فإن التصدير الفردي أسرع بكثير.
دعونا نعيد هيكلة مشروعنا قليلاً لاستخدام تصدير واحد. بادئ ذي بدء ، قم بإنشاء ملف 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 لها ثمن: يصبح تجميع الشفرة أبطأ قليلاً.
لقد رأينا في منشور اليوم أنه بناءً على تهيئة حزمة الويب الخاصة بك ، يمكن أن يكون تجميع مشروعك أسرع (أو أبطأ). تتطلب البقعة الجيدة تصديرًا واحدًا ... وهذا ما تحدثنا عنه اليوم.
أتمنى أن يعجبك المنشور. لو كان كذلك انشره من فضلك. إذا كنت تعرف طرقًا أخرى لتحسين حزمة الويب ، فيرجى إخباري في قسم التعليقات أدناه. اتمنى لك يوم جيد!
صورة مميزة بواسطة Saffu على Unsplash.