أسطورة إطار القوة

في الآونة الأخيرة ، أصبح اتجاه "الأطر المختفية" يحظى بشعبية ، ويمكن اعتبار قاطرته ، دون أدنى شك ، SvelteJS - إطار بناء وقت ومترجم جافا سكريبت بالفانيليا.

على الرغم من حقيقة أن Svelte من الناحية النظرية بسيطة للغاية ، وأكثر سهولة في الاستخدام ، يتساءل العديد من المطورين ما هي الميزة القاتلة لهذا الإطار ، والنهج بأكمله؟ لماذا هذا ليس "إطار جافا سكريبت آخر"؟

في هذه المقالة سأتحدث عن واحدة من العديد من القوى الخارقة Svelte التي يمكن أن تجعل حياتك أسهل.

دعونا نكتشف ذلك ، ولكن أولاً سأخبرك بأسطورة واحدة ...



أسطورة إطار القوة


تم إنشاء بعض الأطر من قبل الرجال من Google و Facebook ، والبعض الآخر - رجال رائعون ، ولكن جميعها تحت "الاهتمام" الوثيق لريتش هاريس .

تم إنشاء تسعة أطر للإنسان ، يبدو أن سبعة إطارات للأقزام. ثلاثة أطر أخرى (رد فعل ، زاوية ، زاوية) كانت للأقزام.

بعد إنشاء الأطر وتنفيذها في آلاف المشاريع ، أنشأ ريتش هاريس شخصياً وسراً إطاراً واحداً ...

إطار واحد لحكمهم جميعًا ،
إطار واحد للعثور عليهم ،
إطار واحد لجلبهم جميعًا
وربطهما معا.

- رب الأطر

المشكلة


أنا متأكد من أن العديد منكم الذين شاركوا بجدية وطويلة في تطوير الواجهة الأمامية واجهوا بشكل متكرر مشكلة اختيار الأدوات لمشروعك الحالي و / أو التالي.
مجموعة متنوعة من جميع أنواع الحزم ، والمرافق ، والحيتان ، والأطر ، والمكتبات والحلول الأخرى خارج نطاقها كما لم يحدث من قبل. والأهم من ذلك ، أن كل هذه الحركة مستمرة في التسارع.

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


إذا حكمنا بأنفسك ، فإن تكوين وخصائص الفريق تعتمد إلى حد كبير على الإطار الذي تختاره ، وتعتمد عملية الصيد بأكملها بالتأكيد. في بعض الأحيان تعتمد الميزانية والجداول الزمنية على ذلك. باختصار brrr.

لكن المشاكل الحقيقية تبدأ ، إذا أدركت في مكان ما في منتصف المشروع ، أنك اتخذت خيارًا غير صحيح. شيء لم ينمو معًا وتدور. يتطلب الإطار وقتًا أطول قليلاً لإتقان ، فريق أكبر قليلاً ، تبين أنه أقل سرعة قليلاً ، قليلًا غير مناسب لأهدافك أو أسلوبك في التطوير ، إلخ. والأهم من ذلك ، أن مشروعك الآن مرتبط بنسبة 100٪ بهذا الإطار ولا يمكنك فقط كتابته وإعادة كتابته في شيء آخر.

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

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

اختلاف آخر لنفس المشكلة - تخيل أنك شركة كبيرة ، مثل Yandex. لديك مجموعة من المشاريع ، حتى أن بعضها معروف فقط لبعض الموظفين ، وقد شهد كل مشروع بالفعل كل ما وصفته أعلاه. تكمن المشكلة في أن كل هذه المشاريع تكره الأطر المختلفة التي اختارتها في البداية.

وها هي قيادتك الرائعة ، قررت التنافس مع Google Material Design وإرسالك في حملة صليبية إلى الواجهات المتنوعة لمشاريعك من أجل جلبها إلى قاسم مشترك. يقوم المصممون الماكرون بالفعل برسم أزرار ومحددات جديدة ويقومون بتخطيط آلاف الصفحات من الإرشادات لمجموعة أدوات واجهة المستخدم الفردية الجديدة لمكوناتك. يا رفاق مرح!

ليست الحياة ، ولكن قصة خرافية ، أليس كذلك؟ يبقى فقط التوصل إلى كيفية سحب كل هذه المكونات الجديدة في جميع تلك المشاريع التي تمكنت بالفعل من كتابتها على جميع الأطر الممكنة. إذا كان هناك حقًا الكثير من الوقت والمال ، وهناك رغبة جمالية ، والأهم من ذلك الاعتقاد بأن "كل شيء يحتاج إلى التوحيد" ، فيمكنك حينئذٍ وضع بضع عشرات من الفرق لإعادة كتابة كل هذا مرة أخرى ، على سبيل المثال ، على React. هذا صحيح ، لأن الهراء البليد الذي كتبت عليه في السنوات 2-3 الماضية قد أصبح عفا عليه الزمن من الناحية الأخلاقية ، لكن رد الفعل سيكون إلى الأبد. حسنًا ، حسنًا)

هناك طريقة أخرى. يمكنك كتابة مجموعة واجهة مستخدم جديدة رائعة في إطار واحد ، وإنشاء مكتبة من المكونات القابلة لإعادة الاستخدام ، كما كانت ، ثم ببساطة استخدام مجموعة واجهة المستخدم في جميع مشاريعك. تبدو باردة؟ بالطبع ، ولكن لا تزال هناك مشكلة واحدة - وقت التشغيل.

إذا كان مشروعك مكتوبًا بزاوية (500 كيلوبايت تقريبًا) ، وقررت كتابة مجموعة واجهة مستخدم في رد فعل (~ 98 كيلوبايت) ، ثم اسحب في كل مشروع على إطار واحد وإطار عمل آخر ، وحتى مع مجموعة من التبعيات ، فإن مجموعة واجهة المستخدم نفسها مباشرة دعنا نقول أنها لا تبدو مثالية.

الحل


لمساعدتنا على الوصول إلى الأطر "المختفية" ، دون وقت تشغيل. الشرط الرئيسي هنا هو أن تكون معزولة قدر الإمكان من حيث نظامها البيئي وأن يكون لها آليات تكامل خارجية وواجهات برمجة تطبيقات مقابلة.

مثال رائع على مثل هذا الإطار هو SvelteJS ، حيث تم بالفعل كتابة عدد قليل من المقالات حول حبري .

لذا ، تخيل الموقف الذي لدينا تطبيق على React. ربما تعبنا من ذلك ، ونريد التخلص منه ، لكن إعادة كتابة كل شيء دفعة واحدة هو رفاهية غير مقبولة. أو ربما تتطلب بعض أجزاء التطبيق تحسينًا أو إعادة بيع ديون. حسنًا ، أو قررنا إنشاء مكتبة مكونة واحدة ، والآن سنكتب جميع المكونات في Svelte ونستخدمها في جميع المشاريع. قدم؟ نعم ، بالطبع لا ، لا أحد لديه مثل هذا الخيال. دعونا نلقي نظرة على مثال حقيقي.


تنويه
على الفور ، أود أن ألفت انتباهك إلى حقيقة أنني لست مطورًا لبرنامج React وآخر مرة شعرت فيها برد الفعل في عام 2015. لذلك ، أفترض أن الطريقة التي كتبت بها جزءًا من مثال رد الفعل يمكن أن تؤذي مشاعر المؤمنين المؤمنين . يؤسفني عدم الحكم بشكل صارم ، خاصة وأن معنى المقالة لا يتغير من هذا.


لذا ، فإن المهمة هي تنفيذ مكون Svelte الجاهز في تطبيق React ، دون تغيير المكون نفسه وبدون تضمين وقت تشغيل إضافي في التطبيق. على سبيل المثال ، سآخذ المكون الذي يبحث عن مستخدمي GitHub ، الذي كتبته عن المقالة السابقة "كيفية البحث عن المستخدمين على GitHub بدون React + RxJS 6 + Recompose" .

يمكن عرض التعليمات البرمجية لهذا المكون في REPL ، ونموذج التعليمات البرمجية من هذه المقالة إلى المستودع .

إنشاء تطبيق رد فعل


أولاً ، قم بإنشاء مشروع React جديد باستخدام الأداة القياسية الفعلية - create-response-app :

npx create-react-app my-app cd my-app npm start 

حسنًا ، إذا ذهبت إلى المنفذ 3000 ، يبدو أنه يعمل.

تخصيص Svelte


إذا كنت لا تعرف أي شيء عن Svelte ، فحينئذٍ سأقول هذا ، في سياق مهمة Svelte ، هذه ليست سوى خطوة أخرى من جامعك (webpack / rollup / gupl / grunt / etc) ، والتي ستسمح لك بكتابة المكونات بتنسيق SFC وتجميعها بالفانيليا جافا سكريبت.

في مجتمع Svelte ، يفضل Rollup أكثر ، وهذا ليس مفاجئًا ، نظرًا لأن لديهم مؤلفًا واحدًا - Rich Harris. ومع ذلك ، نظرًا لأن CRA تستخدم Webpack ، فسنقوم بتكوين Svelte من خلاله. للقيام بذلك ، تحتاج أولاً إلى نقل تكوينات حزم الويب من البرامج النصية التفاعلية إلى المشروع حتى نتمكن من تغييرها. يتم ذلك باستخدام الأمر المضمن:

 npm run eject 


على حد علمي ، هذا ليس نهج كوشير ، ولكن على سبيل المثال هذا هو الخيار الأكثر ملاءمة.

الآن بعد أن تم تكوينات حزمة الويب في جذر المشروع ، يمكنك تثبيت Svelte:

 npm i --save-dev svelte svelte-loader 


انتبه إلى علم - حفظ-التنمية ، تذكر نعم ، ليس هناك وقت تشغيل.))))

اللمسة الأخيرة ، تحتاج إلى توصيل اللودر المناسب في التكوينات:

  { test: /\.svelte$/, use: { loader: 'svelte-loader', } }, 


بشكل عام ، من المعتاد في مجتمع Svelte كتابة ملفات المكونات بالملحق .html ، لأن مكون Svelte هو ملف HTML صالح. ومع ذلك ، لتجنب الاصطدامات المحتملة ، في بعض الحالات ، من الأفضل استخدام تنسيق ملف .svelte المخصص.

لذا فعلنا ، الآن سيتم اعتراض جميع ملفات .svelte المضمنة في المشروع بواسطة هذا المحمل وتجميعها بواسطة Svelte.

كتابة مكون Svelte


أولاً ، من الأفضل تهيئة محرر الشفرة ، على سبيل المثال ، بحيث يطبق تمييز بناء html على الملفات ذات الامتداد المقابل. شيء من هذا القبيل يتم في VS Code:

  "files.associations": { "*.svelte": "html" } 

الآن قم بإنشاء مجلد ./src/svelte_components/ وهناك مجلد المكون نفسه. بعد ذلك ، نقوم ببساطة بنقل جميع الملفات من مثال REPL إلى هذا المجلد ، ومنحهم في الوقت نفسه امتدادًا جديدًا .svelte ، واستدعاء الملف App.html Widget.svelte.

يجب أن تكون النتيجة شيء من هذا القبيل:


في نفس المكان نقوم بإنشاء ملف index.js حيث سيكون لدينا كود التكامل Svelte و React.

دمج


ربما تريد الآن معرفة ما هو السحر؟ السحر هو أننا قمنا بالفعل بكل السحر. بطريقة سحرية ، أليس كذلك؟

على محمل الجد ، يمكننا الآن استخدام مكونات Svelte في تطبيق React الخاص بنا كمنشئين JS عاديين تمامًا ، مما يعني أن رمز التكامل مع Svelte لن يكون مختلفًا عن التكامل مع أي مستقل آخر. تحتوي وثائق React على قسم مخصص لهذا: التكامل مع مكتبات أخرى .

قد يبدو رمز الدمج كما يلي:

 import React, { PureComponent } from 'react'; import Widget from './Widget.svelte'; export default class extends PureComponent { componentDidMount() { const { username } = this.props; this.widget = new Widget({ target: this.el, data: { username } }); } componentWillUnmount() { this.widget.destroy(); } render() { return ( <div ref={el => this.el = el}></div> ); } } 

في المجموع ، قمنا ببساطة بتغليف كود مكون Svelte المعقد في مكون React بسيط للغاية ، والذي يقوم ببساطة بإنشاء مثيل جديد لمكون Svelte ، ونقل عنصر التثبيت والبيانات من الدعائم عند إنشائه. بالإضافة إلى ذلك ، لا ننسى إلغاء تثبيت مكون Svelte في خطاف المكون WillUnmount.

الشيء الوحيد الذي لم نفعله بعد هو أنهم لم يزامنوا قيم حالة المكون. بمعنى ، إذا قام المكون الأصلي بإلقاء الدعائم الأخرى في مكون المجمع ، فيجب أن تنطبق على مكون Svelte. على العكس ، إذا تم تغيير البيانات داخل مكون Svelte ، فيجب التراجع عنها.

للقيام بذلك ، نفترض أن مكون React ذو المستوى الأعلى سيرسل رد الاتصال onChange ، والذي يجب أن نسحبه عندما تحدث التغييرات في الداخل ، وسوف نتوقع تغييرات في الدعائم لمكون المجمع في ربط مكون WillReceiveProps. لنقم بذلك:

  componentDidMount() { ... this.widget.on('state', ({ current: { username }, changed }) => { if (changed.username) { this.props.onChange({ username }); } }); } componentWillReceiveProps({ username }) { this.widget.set({ username }); } 

استخدمنا هنا الحدث المدمج في الحالة ، والذي ينطلق في كل مرة تتغير فيها حالة مكون Svelte. يتم نقل كائن يحتوي على الحالة الحالية للمكون ( الحالي ) ، والحالة السابقة ( السابقة ) وقائمة الخصائص المتغيرة (التي تم تغييرها ) إلى رد الاتصال. وفقًا لذلك ، نتحقق ببساطة مما إذا كان اسم المستخدم قد تم تغييره واستدعاء رد الاتصال onChange ، إذا كان الأمر كذلك.

في ربط hookReceiveProps ، قمنا بتعيين اسم المستخدم الجديد باستخدام طريقة set () المضمنة.

بالإضافة إلى المكونات المضمنة ، يمكن لمكونات Svelte تنفيذ الأحداث والأساليب المخصصة. هذه الميزات الرائعة هي التي تسمح لك بوصف واجهة المكون وتنظيم الاتصال مع "العالم الخارجي" بشكل ملائم.

استخدم


الآن دعنا نحاول استخدام تطبيقنا المصغر مباشرة في تطبيق React. للقيام بذلك ، قم بتحرير ملف App.js الذي تم إنشاؤه بواسطة المبدئ:

 import React, { Component } from 'react'; import './App.css'; import GithubWidget from './svelte_components/GithubWidget'; class App extends Component { constructor() { super(); this.state = { username: '' }; } handleChange = (state) => { this.setState({ ...state }); } render() { return ( <div className="App"> <header className="App-header"> <h1>Github Widget for: {this.state.username}</h1> <GithubWidget onChange={this.handleChange} username={this.state.username} /> </header> </div> ); } } export default App; 

باختصار ، نستخدمه كعنصر رد فعل منتظم. ونتيجة لذلك نحصل على:


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

سننتهي


دعنا الآن نعلم تطبيقنا المصغر بالبحث والعرض ليس فقط بطاقة مستخدم GitHub ، ولكن أيضًا بطاقة المستودع.

أولاً ، تحتاج إلى إنشاء مكون Repo.svelte جديد ، والذي سيرسم بطاقة المستودع. من أجل البساطة ، قمت للتو بنسخ القالب والأنماط من User.svelte وتكييفها مع بيانات المستودع. ومع ذلك ، نظريا هذا مكون منفصل.

بعد ذلك ، تحتاج إلى تعليم عنصر التحكم Widget.svelte لتبديل هذين النوعين من البطاقات بسرعة. بالإضافة إلى ذلك ، تحتاج إلى تعليمه سحب طلبات مختلفة للمستخدم والمستودع.

سنستخدم حقلاً واحدًا للإدخال ونحدد نوع البيانات من خلال وجود "/" في القيمة. أي ، إذا كنت بحاجة إلى البحث عن المستخدم ، فأدخل اسم المستخدم الخاص به ، وإذا كان المستودع ، فأدخل اسم المستخدم للمستخدم ، ثم "/" واسم المستودع.

للوهلة الأولى ، يبدو الأمر مرتبكًا إلى حد ما ، ولكن في Svelte ، سيأخذ الحل حرفياً 5-6 أسطر من التعليمات البرمجية. أولاً ، دعنا نربط مكونًا جديدًا وطريقة واجهة برمجة التطبيقات ، والتي نلفها في التنقيح:

 ... import Repo from './Repo.svelte'; ... import { getUserCard, getRepoCard } from './api.js'; ... const getRepo = debounce(getRepoCard, 1000); 

بعد ذلك ، قم بإنشاء خاصية محسوبة ستحدد نوع البطاقة التي يجب أن نعرضها:

 computed: { ... repo: ({ username }) => username.includes('/'), ... } 

أضف الآن تبديل الطلبات إلى API:

 computed: { ... card: ({ username, repo }) => username && (repo ? getRepo : getUser)(username), ... } 

وأخيرًا ، تبديل مكونات البطاقة اعتمادًا على النوع:

 computed: { ... Card: ({ repo }) => repo ? Repo : User, ... } 

بالإضافة إلى ذلك ، من أجل استبدال المكونات ديناميكيًا ، نحتاج إلى استخدام علامة Svelte خاصة ، والتي ترسم المكون الذي يتم تمرير قيمته إلى هذه السمة:

 <svelte:component this={Card} {...card} /> 


يعمل. هل لاحظت؟ نحن نكتب بالفعل على Svelte داخل تطبيق React! )))

الآن دعنا نعلم القطعة لدينا لإخفاء حقل الإدخال ومحاولة إدخال اسم المستخدم ليس داخل القطعة ، ولكن داخل تطبيق React. هل يهم كيف سيحصل منطق أعمالنا على هذه القيمة.

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

 {#if search} <input bind:value=username placeholder="username or username/repo"> {/if} ... <script> export default { ... data() { return { username: '', search: false }; }, ... }; </script> 

الآن في App.js ، سننشئ حقل إدخال على جانب رد الفعل من التطبيق ونكتب المعالجة المقابلة لحدث الإدخال:

  ... handleUsername = (e) => { this.setState({ username: e.target.value }); } ... <h1>Github Widget for: {this.state.username}</h1> <input value={this.state.username} onChange={this.handleUsername} className="Username" placeholder="username or username/repo" /> 

ونسخه أيضًا إلى المجلد باستخدام الأداة المصغرة هنا هو مثل svg spinner على Svelte:

 <svg height={size} width={size} style="animation-duration:{speed}ms;" class="svelte-spinner" viewbox="0 0 32 32" > <circle role="presentation" cx="16" cy="16" r={radius} stroke={color} fill="none" stroke-width={thickness} stroke-dasharray="{dash},100" stroke-linecap="round" /> </svg> <script> export default { data() { return { size: 25, speed: 750, color: 'rgba(0,0,0,0.4)', thickness: 2, gap: 40, radius: 10 }; }, computed: { dash: ({radius, gap}) => 2 * Math.PI * radius * (100 - gap) / 100 } }; </script> <style> .svelte-spinner { transition-property: transform; animation-name: svelte-spinner_infinite-spin; animation-iteration-count: infinite; animation-timing-function: linear; } @keyframes svelte-spinner_infinite-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } </style> 

وسنطبقها في الأداة بحيث تكون جميلة جدًا:

 ... {#await card} <Spinner size="50" speed="750" color="#38b0ee" thickness="2" gap="40" /> {:then card} ... 

في رأيي ، تبين أنها ليست سيئة للغاية:


القبعة ذات الخلفية السوداء وحقل الإدخال هو تطبيق React ، الكتلة البيضاء أدناه هي أداة Svelte. هذه هي الفطائر. )))

مستودع

الاستنتاجات


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

Svelte مثالي لك إذا:


  1. تريد أن تبدأ مشروعًا جديدًا ولا تعرف إطار العمل الذي تختاره لهذا الغرض.
  2. لديك مشروع ويعمل بشكل أفضل ومن الأفضل عدم لمسه. مكونات ووحدات جديدة ، يمكنك الكتابة في Svelte والاندماج بسلاسة في التعليمات البرمجية الموجودة.
  3. لديك بالفعل مشروع ، ولكنه عفا عليه الزمن جزئيًا أو كليًا و / أو يتطلب إعادة هيكلة جادة ، حتى إعادة كتابة كاملة. يمكنك البدء في إعادة كتابته في أجزاء. في هذه الحالة ، لا تحتاج إلى التوصل إلى تكوينات معقدة. ما عليك سوى أخذ بعض المكونات ، وإعادة كتابتها إلى Svelte ولف المكون الجديد بالمكون القديم. ومع ذلك ، فإن بقية التطبيق لا يدرك حتى التغييرات.
  4. لديك العديد من المشاريع على أسس تعليمات برمجية مختلفة وفي نفس الوقت ، ترغب في الحصول على مجموعة واجهة مستخدم واحدة واستخدامها في أي من هذه المشاريع. اكتب مجموعة واجهة المستخدم على Svelte واستخدمها في أي مكان. هذا جميل.

هل تريد معرفة حالات أكثر إثارة للاهتمام؟ انضم إلى قناة Telegram !

تحديث: شكرا justboris على السؤال الصحيح. استمرار المثال:

 import React, { PureComponent } from 'react'; import Widget from './Widget.svelte'; export default class extends PureComponent { componentDidMount() { ... this.widget = new Widget({ target: this.el, data: { username }, slots: { default: this.slot } }); ... } ... render() { return ( <div ref={el => this.el = el}> <div ref={el => this.slot = el}> {this.props.children} </div> </div> ); } } 


 <GithubWidget onChange={this.handleChange} username={this.state.username}> <p>Hello world</p> </GithubWidget> 

Source: https://habr.com/ru/post/ar420113/


All Articles