استكشاف Parcel - بديل Webpack للمشاريع الصغيرة





يوم جيد ، أيها الأصدقاء!



الغرض الرئيسي من مُنشئ الوحدات أو المُجمِّع مثل Webpack أو Parcel هو التأكد من تضمين جميع الوحدات النمطية المطلوبة لتشغيل التطبيق بالترتيب الصحيح في نص برمجي واحد مصغر (لبناء الإنتاج) مضمن في الفهرس. لغة البرمجة.



في الواقع ، يمكن للمطورين ، كقاعدة عامة ، تحسين ليس فقط JS ، ولكن أيضًا ملفات HTML و CSS ، يمكنهم تحويل Less و Sass إلى CSS و TypeScript و React و Vue (JSX) إلى JavaScript ، والعمل مع الصور والصوت والفيديو وغيرها تنسيقات البيانات ، وتوفر أيضًا إمكانات إضافية ، مثل: إنشاء خريطة للموارد (المستخدمة) أو المصادر (خريطة المصدر) ، والتمثيل المرئي لحجم الحزمة بأكملها وأجزائها الفردية (الوحدات النمطية ، والمكتبات) ، وتقسيم الكود إلى أجزاء (أجزاء) ، بما في ذلك number ، لأغراض إعادة الاستخدام (على سبيل المثال ، المكتبات المستخدمة في عدة وحدات يتم إخراجها في ملف منفصل وتحميلها مرة واحدة فقط) ، والتحميل الذكي للحزم من npm (على سبيل المثال ، تحميل الترجمة الروسية فقط من moment.js) ، وجميع أنواع المكونات الإضافية لحل مهام محددة إلخ



في هذا الصدد ، تنتمي القيادة بالطبع إلى Webpack. ومع ذلك ، ماذا لو كنا نطور مشروعًا لا تحتاج فيه إلى معظم الوظائف التي توفرها هذه الأداة الرائعة؟ هل توجد بدائل لهذه التقنية يسهل تعلمها واستخدامها؟ بالنسبة لي ، كان الجواب على هذا السؤال هو Parcel . بالمناسبة ، إذا كنت مهتمًا بالتعرف على Webpack ، فإنني أوصي بمشاهدة هذا الفيديو . يوجد ملفي المزود بإعدادات Webpack لهذا البرنامج التعليمي هنا .



















بعد إذنك ، لن أعيد سرد الوثائق بكلماتي الخاصة ، خاصةً أنها متوفرة باللغة الروسية ، لكنني سأركز على المكون العملي ، أي باستخدام سلاسل القوالب والاستيراد الديناميكي ، سننشئ SPA تتكون من ثلاث صفحات في JavaScript ، صمم التطبيق باستخدام CSS ، واكتب وظيفة بسيطة في TypeScript ، وقم باستيرادها إلى التطبيق ، وقم بتصميم الحاوية لنتائج هذه الوظيفة باستخدام Sass ، وقم ببناء التطبيق باستخدام Parcel في وضعي التطوير والإنتاج.



كود المشروع هنا .



إذا كنت مهتمًا ، من فضلك اتبعني.



تطبيق



جاهز؟ إذا دعنا نذهب.



أنشئ الدليل التعليمي للطرود.



ندخل فيه ونبدأ المشروع باستخدام npm init -y.



قم بإنشاء ملف index.html. سنستخدم أحد قوالب غلاف Bootstrap القياسية:



<head>
    ...
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head>

<!-- Bootstrap class -->
<body class="text-center">

    <! -- Main script -->
    <script src="index.js"></script>
</body>


انتقل إلى موقع Bootstrap الرسمي ، وانتقل إلى قسم الأمثلة ، وابحث عن الغلاف في المكونات المخصصة ، واضغط على Ctrl + U (Cmd + U) لعرض كود الصفحة.











قم بإنشاء دليل src ، وهناك مجلدان آخران فيه - js و css.



أنشئ الملفات التالية في دليل js: header.js و footer.js و home.js و projects.js و contact.js. هذه وحدات أو ، إذا أردت ، مكونات تطبيقنا: رأس الصفحة وتذييل الصفحة ومحتوى الصفحة الرئيسية والصفحات الأخرى.



في دليل css ، قم بإنشاء ملف style.css.



في الوقت الحالي ، يبدو هيكل المشروع كما يلي:



-- parcel-tutorial
    -- src
        -- css
            -- style.css
        -- js
            -- contact.js
            -- footer.js
            -- header.js
            -- home.js
            -- projects.js
    -- index.html
    -- index.js
    -- package.json


العودة إلى Bootstrap.



انسخ رمز الصفحة والصقه في الوحدات النمطية المقابلة مع تغييرات طفيفة.



header.js:



export default `
  <header class="masthead mb-auto">
      <div class="inner">
          <h3 class="masthead-brand">Parcel Tutorial</h3>
          <nav class="nav nav-masthead justify-content-center">
            <a class="nav-link active" name="home">Home</a>
            <a class="nav-link" name="projects">Projects</a>
            <a class="nav-link" name="contact">Contact</a>
        </nav>
      </div>
  </header>
`.trim()


يرجى ملاحظة أننا قمنا بتغيير href إلى الاسم في الروابط.



footer.js:



export default `
  <footer class="mastfoot mt-auto">
    <div class="inner">
      <p>© 2020. All rights reserved.</p>
    </div>
  </footer>
`.trim()


home.js:



export default `
  <h1 class="cover-heading">Home page</h1>
  <p class="lead">Home page content</p>
`.trim()


المشاريع. js:



export default `
  <h1 class="cover-heading">Projects page</h1>
  <p class="lead">Projects page content</p>
`.trim()


contact.js:



export default `
  <h1 class="cover-heading">Contact page</h1>
  <p class="lead">Contact page content</p>
`.trim()


لا تنس نسخ الأنماط من cover.css إلى style.css.



افتح index.js.



دعنا نستورد الرأس والتذييل والأنماط:



import header from './src/js/header.js'
import footer from './src/js/footer.js'
import './src/css/style.css'


سيتم تحميل محتوى الصفحة الرئيسية والصفحات الأخرى ديناميكيًا عند النقر على الرابط ، لذلك نقوم بإنشاء كائن مثل هذا:



const pages = {
    home: import('./src/js/home.js'),
    projects: import('./src/js/projects.js'),
    contact: import('./src/js/contact.js')
}


اسم خاصية هذا الكائن هو المقابلة (التي يطلبها المستخدم) الصفحة.



نقوم بإنشاء صفحة البداية باستخدام مكونات الرأس والتذييل التي تم استيرادها مسبقًا:



// Bootstrap classes
document.body.innerHTML = `
<div class="cover-container d-flex w-100 h-100 p-3 mx-auto flex-column">
    ${header}
    <main role="main" class="inner cover"></main>
    ${footer}
</div>
`.trim()


سيتم عرض محتوى الصفحات في العنصر الرئيسي ، لذلك نحدده:



const mainEl = document.querySelector('main')


قم بإنشاء وظيفة عرض الصفحة:



const renderPage = async name => {
    const template = await pages[name]
    mainEl.innerHTML = template.default
}


نحتاج إلى الانتظار حتى يتم تحميل الوحدة المقابلة ، لذلك نستخدم غير متزامن / انتظار (توقف الكلمة الأساسية في انتظار مؤقتًا تنفيذ الوظيفة). تأخذ الوظيفة اسم الصفحة المطلوبة (الاسم) وتستخدمها للوصول إلى الخاصية المقابلة لكائن الصفحات (الصفحات [الاسم]). ثم نقوم بإدخال القالب الناتج في mainEl. في الواقع ، انتظار إرجاع كائن الوحدة النمطية الذي يحتوي على القالب. لذلك ، عند إدراج قالب كترميز في mainEl ، من الضروري الرجوع إلى الخاصية الافتراضية لكائن الوحدة النمطية (يتم تصدير الوحدات النمطية افتراضيًا) ، وإلا فسوف نتلقى خطأ - من المستحيل تحويل الكائن إلى HTML.



تقديم الصفحة الرئيسية:



renderPage('home')


يحتوي الرابط النشط المقابل للصفحة الحالية على الفئة النشطة. نحن بحاجة إلى تبديل الفئات عند تقديم صفحة جديدة. دعنا ننفذ وظيفة المساعد:



const toggleClass = (activeLink, currentLink) => {
    if (activeLink === currentLink) {
        return;
    } else {
        activeLink.classList.remove('active')
        currentLink.classList.add('active')
    }
}


تأخذ الوظيفة وسيطتين - ارتباط بالفئة النشطة (activeLink) والرابط الذي تم النقر فوقه (currentLink). إذا تطابق الروابط المحددة ، فإننا لا نفعل شيئًا. خلاف ذلك ، نقوم بتغيير الفئات.



أخيرًا ، نحتاج إلى إضافة معالج النقر فوق الارتباط. دعنا ننفذ وظيفة مساعدة أخرى:



const initClickHandlers = () => {
    const navEl = document.querySelector('nav')

    navEl.addEventListener('click', ev => {
        if (ev.target.tagName === 'A') {
            const activeLink = navEl.querySelector('.active')
            const currentLink = ev.target
            toggleClass(activeLink, currentLink)
            renderPage(currentLink.name)
        }
    })
}


في هذه الدالة ، نجد أولًا عنصر التنقل. بعد ذلك ، من خلال التفويض ، نقوم بمعالجة النقرات على الروابط: إذا كان العنصر الهدف هو العلامة A ، نحصل على الرابط النشط (الرابط مع الفصل النشط) ، والرابط الحالي (الرابط الذي تم النقر عليه) ، وتغيير الفئات وعرض الصفحة. يتم تمرير قيمة سمة الاسم للرابط الحالي كوسيطة renderPage.



نحن على وشك الانتهاء من التطبيق. ومع ذلك ، قبل الشروع في بناء المشروع باستخدام Parcel ، من الضروري ملاحظة ما يلي: اليوم ، دعم الوحدات الديناميكية وفقًا هل يمكنني استخدام البيانات هو 90٪. هذا كثير ، لكننا لسنا مستعدين لخسارة 10٪ من مستخدمينا. لذلك ، يجب تحويل الكود الخاص بنا إلى بنية أقل حداثة. بابل يستخدم في النتح. نحتاج إلى توصيل وحدتين إضافيتين:



import "core-js/stable";
import "regenerator-runtime/runtime";


يرجى ملاحظة أننا لا نقوم بتثبيت هذه الحزم مع npm.



أيضًا ، دعنا ننفذ على الفور وظيفة في TypeScript ، شيء بسيط جدًا ، مثل وظيفة لإضافة رقمين.



قم بإنشاء ملف index.ts في دليل js بالمحتوى التالي:



export const sum = (a: number, b: number): number => a + b


الاختلاف الوحيد عن JavaScript ، بالإضافة إلى امتداد الملف (.ts) ، هو أننا نحدد صراحة أنواع القيم المقبولة والعودة بواسطة الوظيفة - في هذه الحالة ، الرقم. في الواقع ، يمكننا أن نقتصر على تحديد نوع الإرجاع ، TypeScript ذكي بما يكفي لمعرفة أنه إذا كانت القيمة المعادة هي رقم ، فيجب أن تكون الوسيطات التي تم تمريرها أرقامًا. لا يهم.



دعنا نستورد هذه الوظيفة إلى index.js:



import { sum } from './src/js/index.ts'


ونسميها بالوسيطتين 1 و 2 في RenderPage:



const renderPage = async name => {
    // ...
    mainEl.insertAdjacentHTML('beforeend', `
        <output>Result of 1 + 2 -> <span>${sum(1, 2)}<span></output>
    `)
}


تصميم حاوية نتيجة الوظيفة باستخدام Sass. في مجلد css ، أنشئ ملف style.scss بالمحتوى التالي:



$color: #8e8e8e;

output {
    color: $color;
    border: 1px solid $color;
    border-radius: 4px;
    padding: .5rem;
    user-select: none;
    transition: transform .2s;

    & span {
        color: #eee;
    }

    &:hover {
        transform: scale(1.1);
    }
}


دعنا نستورد هذه الأنماط إلى index.js:



import './src/css/style.scss'


لاحظ مرة أخرى أننا لا نقوم بتثبيت TypeScript و Sass باستخدام npm.



لقد انتهينا من التطبيق. الانتقال إلى الطرود.



الطرد



لتثبيت Parcel عالميًا ، تحتاج إلى تشغيل الأمر npm i parcel-bundler -gفي الجهاز.



افتح package.json وقم بتهيئة Parcel للتشغيل في وضعي التطوير والإنتاج:



"scripts": {
    "dev": "parcel index.html --no-source-maps --open",
    "pro": "parcel build index.html --no-source-maps --no-cache"
  },


npm run devيبدأ الفريق في بناء المشروع من أجل التطوير ، ويبدأ الفريق npm run proفي الإنتاج. لكن ماذا تعني كل هذه الأعلام؟ ولماذا لم نثبت Babel و TypeScript و Sass عبر npm؟



الحقيقة هي أن Parcel يقوم تلقائيًا بتثبيت جميع التبعيات عندما يكتشف استيرادها أو استخدامها في التطبيق. على سبيل المثال ، إذا رأى Parcel أوراق أنماط مستوردة من ملف .scss ، فإنه يقوم بتثبيت Sass.



الآن عن الفرق والأعلام.



لإنشاء المشروع في وضع التطوير ، استخدم الأمر parcel index.html، حيث يمثل index.html نقطة دخول التطبيق ، أي ملف يحتوي على ارتباط إلى النص الرئيسي أو البرامج النصية. أيضًا ، يبدأ هذا الأمر خادمًا محليًا على المضيف المحلي: 1234. يعني



العلم --no-source-mapsأننا لسنا بحاجة إلى خرائط الموارد.



علم--openيخبر Parcel بفتح index.html بعد إنشاء متصفح على الخادم المحلي.



لإنشاء مشروع في وضع الإنتاج ، استخدم الأمر parcel build index.html. يفترض هذا التجميع تصغير ملفات JS و CSS و HTML. تعني



العلامة --no-cacheتعطيل التخزين المؤقت للمورد. يوفر التخزين المؤقت سرعة عالية في بناء وإعادة بناء المشروع في الوقت الفعلي. يكون هذا مناسبًا عند التطوير ، ولكن ليس كثيرًا عند تجميع منتج نهائي.



نقطة أخرى: بشكل افتراضي ، يضع Parcel الملفات التي تم إنشاؤها في مجلد dist ، والذي يتم إنشاؤه إذا كان مفقودًا. المشكلة هي أن الملفات القديمة لا يتم حذفها عند إعادة البناء. لإزالة هذه الملفات ، تحتاج إلى مكون إضافي خاص ، على سبيل المثال parcel-plugin-clean-easy .



قم بتثبيت هذا البرنامج المساعد باستخدامnpm i parcel-plugin-clean-easy -D وأضف ما يلي إلى package.json:



"parcelCleanPaths": [
    "dist",
    ".cache"
  ]


parcelCleanPaths هي أدلة يجب إزالتها عند إعادة البناء.



تم تكوين الطرود بالكامل الآن. افتح Terminal واكتب npm run devواضغط على Enter.















يبني Parcel المشروع في وضع التطوير ، ويبدأ الخادم المحلي ، ويفتح التطبيق في المستعرض. ممتاز.



الآن دعنا نحاول وضع مشروع للإنتاج.



نحن ننفذ الأمر npm run pro.











نطلق التطبيق في المتصفح.







عفوًا ، يبدو أنه حدث خطأ ما.



دعنا نلقي نظرة على index.html الذي تم إنشاؤه. ماذا نرى هناك؟ تلميح: انتبه إلى المسارات الموجودة في علامات الارتباط والبرنامج النصي. لا أعرف بالضبط ما هو مرتبط به ، ولكن Parcel يحول الروابط النسبية إلى "/ path-to-file" ، ولا يقرأ المتصفح مثل هذه الروابط.



لحل هذه المشكلة ، تحتاج إلى إضافة علامة "--public-url." إلى النص "pro".



نبدأ في إعادة البناء.



المسارات النسبية صحيحة ويعمل التطبيق. رائع.







هذا كل شيء بالنسبة لي. شكرآ لك على أهتمامك.



All Articles