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

هذه المقالة هي ترجمة لـ
React Internals ، الجزء الأول: التقديم الأساسيهذا هو في الواقع المادة الأولى من أصل خمسة
- أساسيات التقديم <- نحن هنا
- ComponentWillMount و componentDidMount
- تحديث
- setState
- المعاملات
تم إنشاء المادة عندما يكون React 15.3 ذا صلة ، ولا سيما استخدام ReactDOM وموفق المكدس. الرد 16 وما فوق له بعض التغييرات. ومع ذلك ، تظل هذه المادة ذات صلة ، لأنها تعطي فكرة عامة عما يحدث "تحت الغطاء".
الجزء 1. تقديم الأساسيات
العناصر والمكونات
هناك ثلاثة أنواع من الكيانات في React: عنصر DOM أصلي ، وعنصر تفاعل افتراضي ، ومكون.
عناصر DOM الأصلية
هذه هي عناصر DOM التي يستخدمها المستعرض لإنشاء صفحة الويب ، على سبيل المثال ، div ، span ، h1. يقوم React بإنشائها عن طريق استدعاء document.createElement () ، ويتفاعل مع الصفحة باستخدام أساليب DOM API المستندة إلى المستعرض مثل element.insertBefore () ، element.nodeValue ، وغيرها.
رد فعل الظاهري عنصر
عنصر React الظاهري (يُشار إليه غالبًا باسم "عنصر") هو كائن javascript يحتوي على الخصائص الضرورية لإنشاء أو تحديث عنصر DOM أصلي أو شجرة من هذه العناصر. استنادًا إلى عنصر React الظاهري ، يتم إنشاء عناصر DOM الأصلية ، مثل div و span و h1 وغيرها. يمكننا القول أن عنصر React الظاهري هو مثيل لمركب مركب يحدده المستخدم ، المزيد حول هذا أدناه.
عنصر
المكون هو مصطلح عام إلى حد ما في React. المكونات هي الكيانات التي بها React تلاعب مختلف. مكونات مختلفة تخدم أغراض مختلفة. على سبيل المثال ، ReactDomComponent من مكتبة ReactDom مسؤولة عن الارتباط بين عناصر React وعناصر DOM الأصلية المقابلة لها.
مكونات مجمع مخصص
على الأرجح واجهت بالفعل هذا النوع من المكونات. عند استدعاء React.createClass () أو استخدام فئات ES6 من خلال تمديد React.Component ، يمكنك إنشاء مكون مركب مخصص. يحتوي هذا المكون على أساليب دورة الحياة ، مثل componentWillMount و shouldComponentUpdate ، وغيرها. يمكننا إعادة تعريفهم لإضافة نوع من المنطق. بالإضافة إلى ذلك ، يتم إنشاء طرق أخرى ، مثل mountComponent ، receComponent. لا تستخدم React هذه الطرق إلا لأغراضها الداخلية ؛ لا نتفاعل معها بأي طريقة.
ZanudaMode = علىفي الواقع ، لم تكتمل المكونات التي أنشأها المستخدمون في البداية. React يلفها في ReactCompositeComponentWrapper ، والذي يضيف جميع أساليب دورة الحياة إلى مكوناتنا ، وبعد ذلك React يمكن إدارتها (إدراج ، تحديث ، وما إلى ذلك).
رد الفعل التعريفي
عندما يتعلق الأمر بالمكونات المخصصة ، فإن مهمتنا هي تحديد فئات هذه المكونات ، لكننا لا نقوم بإنشاء مثيل لهذه الفئات. React يخلق لهم عند الحاجة.
أيضًا ، لا نقوم بإنشاء عناصر بشكل صريح باستخدام نمط ضروري ؛ بدلاً من ذلك ، نكتب بأسلوب تعريفي باستخدام JSX:
class MyComponent extends React.Component { render() { return <div>hello</div>; } }
تتم ترجمة هذا الرمز مع JSX بواسطة المحول البرمجي إلى ما يلي:
class MyComponent extends React.Component { render() { return React.createElement('div', null, 'hello'); } }
وهذا ، في جوهره ، يتحول إلى بنية ضرورية لإنشاء عنصر من خلال دعوة واضحة إلى React.createElement (). ولكن هذا البناء موجود داخل طريقة التقديم () ، والتي لا ندعوها بشكل صريح ، فإن React ستطلق على هذه الطريقة نفسها عند الضرورة. لذلك ، فإن إدراك React هو نفس التصريح: نصف ما نريد استلامه ، و React يحدد كيفية القيام بذلك.
اكتب رد فعلك الصغير
بعد تلقي الأساس التقني اللازم ، سنبدأ في إنشاء تطبيق React الخاص بنا. ستكون هذه نسخة مبسطة للغاية ، دعنا نسميها Feact.
لنفترض أننا نريد إنشاء تطبيق Feact بسيط الذي سيكون رمزه كما يلي:
Feact.render(<h1>hello world</h1>, document.getElementById('root'));
أولاً ، دعنا نتطرق إلى JSX. هذا هو بالضبط "تراجع" ، لأن تحليل JSX هو موضوع كبير منفصل سنحذفه كجزء من تطبيقنا لـ Feact. إذا كنا نتعامل مع JSX التي تمت معالجتها ، فسنرى الرمز التالي:
Feact.render( Feact.createElement('h1', null, 'hello world'), document.getElementById('root') );
أي أننا نستخدم Feact.createElement بدلاً من JSX. لذلك نحن نطبق هذه الطريقة:
const Feact = { createElement(type, props, children) { const element = { type, props: props || {} }; if (children) { element.props.children = children; } return element; } };
العنصر المرتجع هو كائن بسيط يمثل ما نريد تقديمه.
ماذا تفعل Feact.render ()؟
عن طريق استدعاء Feact.render () ، نمرر معلمتين: ما نريد تقديمه وأين. هذه هي نقطة الانطلاق لأي تطبيق React. دعنا نكتب تنفيذ طريقة تقديم () لـ Feact:
const Feact = { createElement() { }, render(element, container) { const componentInstance = new FeactDOMComponent(element); return componentInstance.mountComponent(container); } };
عند الانتهاء من تقديم () ، نحصل على صفحة ويب منتهية. يتم إنشاء عناصر DOM بواسطة FeactDOMComponent. دعنا نكتب تنفيذه:
class FeactDOMComponent { constructor(element) { this._currentElement = element; } mountComponent(container) { const domElement = document.createElement(this._currentElement.type); const text = this._currentElement.props.children; const textNode = document.createTextNode(text); domElement.appendChild(textNode); container.appendChild(domElement); this._hostNode = domElement; return domElement; } }
الأسلوب mountComponent ينشئ عنصر DOM ويخزنه في هذا. _hostNode. لن نستخدمها الآن ، لكننا سنعود إلى ذلك في الأجزاء التالية.
يمكن الاطلاع على الإصدار الحالي من التطبيق في
كمان .
حرفيًا 40 سطرًا من الشفرة كانت كافية لإجراء تنفيذ بدائي لـ React. من غير المرجح أن يغزو العالم Feact ، لكنه يعكس جيدًا ما يحدث تحت غطاء React.
إضافة مكونات مخصصة
يجب أن يكون Feact الخاص بنا قادرًا على عرض عناصر HTML فقط (div ، span ، إلخ) ، ولكن أيضًا المكونات المركبة المعرفة من قبل المستخدم:
طريقة Feact.createElement () الموضحة مسبقًا جيدة حاليًا ، لذلك لن أكررها في قائمة الأكواد البرمجية.
const Feact = { createClass(spec) { function Constructor(props) { this.props = props; } Constructor.prototype.render = spec.render; return Constructor; }, render(element, container) {
اسمحوا لي أن أذكرك ، إذا كانت JSX متوفرة ، فإن استدعاء طريقة التقديم () سيبدو كما يلي:
Feact.render( <MyTitle message="hey there Feact" />, document.getElementById('root') );
لقد مررنا فئة المكون المخصص لإنشاء العنصر. يمكن أن يمثل عنصر React الظاهري عنصر DOM عادي أو مكون مخصص. سنميزها كما يلي: إذا مررنا بنوع سلسلة ، فهذا عنصر DOM ؛ إذا كانت وظيفة ، فإن هذا العنصر يمثل مكونًا مخصصًا.
تحسين Feact.render ()
إذا نظرت عن كثب إلى الشفرة في الوقت الحالي ، سترى أن Feact.render () لا يمكنه معالجة المكونات المخصصة. دعونا إصلاح هذا:
Feact = { render(element, container) { const componentInstance = new FeactCompositeComponentWrapper(element); return componentInstance.mountComponent(container); } } class FeactCompositeComponentWrapper { constructor(element) { this._currentElement = element; } mountComponent(container) { const Component = this._currentElement.type; const componentInstance = new Component(this._currentElement.props); const element = componentInstance.render(); const domComponentInstance = new FeactDOMComponent(element); return domComponentInstance.mountComponent(container); } }
لقد أنشأنا مجمعا للعنصر مرت. داخل المجمّع ، نقوم بإنشاء مثيل لفئة مكون المستخدم ونستدعي طريقة componentInstance.render () الخاصة به. يمكن تمرير نتيجة هذه الطريقة إلى FeactDOMComponent ، حيث سيتم إنشاء عناصر DOM المقابلة.
الآن يمكننا إنشاء وتقديم مكونات مخصصة. ستنشئ Feact عقد DOM بناءً على المكونات المخصصة ، وتغييرها وفقًا للخصائص (الدعائم) للمكونات المخصصة لدينا. هذا هو تحسن كبير في Feact لدينا.
لاحظ أن FeactCompositeComponentWrapper مباشرة بإنشاء FeactDOMComponent. هذه العلاقة الوثيقة سيئة. سوف نصلح هذا لاحقا. إذا كان React له نفس الارتباط الوثيق ، فيمكن إنشاء تطبيقات الويب فقط. تتيح لك إضافة طبقة إضافية ReactCompositeComponentWrapper فصل منطق React لإدارة العناصر الافتراضية والعرض النهائي للعناصر الأصلية ، مما يسمح لك باستخدام React ليس فقط عند إنشاء تطبيقات الويب ، ولكن أيضًا ، على سبيل المثال ، React Native للجوال.
تعزيز مكون مخصص
يمكن للمكونات المخصصة التي تم إنشاؤها فقط إرجاع عناصر DOM الأصلية ، إذا حاولنا إرجاع مكونات مخصصة أخرى ، فسيظهر لنا خطأ. صحح هذا العيب. تخيل أننا نود تنفيذ التعليمات البرمجية التالية دون أخطاء:
const MyMessage = Feact.createClass({ render() { if (this.props.asTitle) { return Feact.createElement(MyTitle, { message: this.props.message }); } else { return Feact.createElement('p', null, this.props.message); } } }
يمكن أن تعرض طريقة التقديم () للمكون المخصص إما عنصر DOM أصلي أو مكون مخصص آخر. إذا كانت الخاصية asTitle صحيحة ، فسوف تُرجع FeactCompositeComponentWrapper المكون المخصص لـ FeactDOMComponent حيث سيحدث الخطأ. إصلاح FeactCompositeComponentWrapper:
class FeactCompositeComponentWrapper { constructor(element) { this._currentElement = element; } mountComponent(container) { const Component = this._currentElement.type; const componentInstance = new Component(this._currentElement.props); let element = componentInstance.render(); while (typeof element.type === 'function') { element = (new element.type(element.props)).render(); } const domComponentInstance = new FeactDOMComponent(element); domComponentInstance.mountComponent(container); } }
في الحقيقة ، لقد حققنا الآن عكاز لتلبية الاحتياجات الحالية. تؤدي استدعاء طريقة التجسيد إلى إرجاع المكونات الفرعية حتى تقوم بإرجاع عنصر DOM أصلي. هذا أمر سيئ لأن هذه المكونات الفرعية لن تشارك في دورة الحياة. على سبيل المثال ، في هذه الحالة ، لن نتمكن من تنفيذ استدعاء componentWillMount. سوف نصلح هذا لاحقا.
ومرة أخرى نقوم بإصلاح Feact.render ()
يمكن أن يعالج الإصدار الأول من Feact.render () عناصر DOM الأصلية فقط. الآن تتم معالجة المكونات المعرفة من قبل المستخدم بشكل صحيح دون دعم محلي. من الضروري التعامل مع كلتا الحالتين. يمكنك كتابة المصنع الذي سينشئ مكونًا بناءً على نوع العنصر الذي تم تمريره ، لكن React اختار طريقة مختلفة: مجرد لف أي مكون وارد في مكون آخر:
const TopLevelWrapper = function(props) { this.props = props; }; TopLevelWrapper.prototype.render = function() { return this.props; }; const Feact = { render(element, container) { const wrapperElement = this.createElement(TopLevelWrapper, element); const componentInstance = new FeactCompositeComponentWrapper(wrapperElement);
TopLevelWrapper هو في الأساس مكون مخصص. يمكن تعريفه أيضًا عن طريق استدعاء Feact.createClass (). تقوم طريقة التجسيد ببساطة بإرجاع العنصر الذي تم تمريره إليها. الآن يتم التفاف كل عنصر في TopLevelWrapper ، وسوف يتلقى FeactCompositeComponentWrapper دائمًا مكونًا مخصصًا كمدخل.
اختتام الجزء الاول
قمنا بتنفيذ Feact ، والتي يمكن أن تجعل المكونات. يظهر الرمز الذي تم إنشاؤه المفاهيم الأساسية للتقديم. العرض الحقيقي في React أكثر تعقيدًا ، وهو يغطي الأحداث والتركيز وتصفح النوافذ والأداء وما إلى ذلك.
jsfiddle النهائي
للجزء الأول.