في عملي اليومي، أعمل على إطار عمل جافا سكريبت (LWC). وعلى الرغم من أنني كنت أعمل عليه منذ ما يقرب من ثلاث سنوات، إلا أنني ما زلت أشعر وكأنني هاوية. عندما أقرأ عما يحدث في عالم الإطار الأكبر، غالبًا ما أشعر بالإرهاق من كل الأشياء التي لا أعرفها.
ومع ذلك، فإن إحدى أفضل الطرق لمعرفة كيفية عمل شيء ما هي أن تقوم ببنائه بنفسك. وبالإضافة إلى ذلك، يجب علينا الاحتفاظ بها “”أيام منذ آخر إطار عمل لجافا سكريبت”” الميمات تسير. لذلك دعونا نكتب إطار عمل جافا سكريبت الحديث الخاص بنا!
ما هو “إطار عمل جافا سكريبت الحديث”؟
React هو إطار عمل رائع، وأنا لست هنا لأتعمق فيه. ولكن لأغراض هذا المنشور، يعني “إطار عمل JavaScript الحديث” “إطار عمل من عصر ما بعد React” – أي أشعل, صلب, ممشوق, فيو، إلخ.
لقد هيمنت React على مشهد الواجهة الأمامية لفترة طويلة حتى أن كل إطار عمل جديد قد نشأ في ظلها. كانت جميع هذه الأطر مستوحاة بشكل كبير من React، لكنها تطورت بعيدًا عنها بطرق مشابهة بشكل مدهش. وعلى الرغم من أن React نفسها استمرت في الابتكار، إلا أنني أجد أن أطر عمل ما بعد React أكثر تشابهًا مع بعضها البعض من React في الوقت الحاضر.
ولتبسيط الأمور، سأتجنب أيضًا الحديث عن أطر عمل الخادم أولاً مثل استرو, ماركو، و كويك. تعتبر هذه الأطر ممتازة بطريقتها الخاصة، ولكنها تأتي من تقاليد فكرية مختلفة قليلاً مقارنة بالأطر التي تركز على العميل. لذلك، في هذا المنشور، دعونا نتحدث فقط عن العرض من جانب العميل.
ما الذي يميز الأطر الحديثة؟
من وجهة نظري، فقد تقاربت جميع أطر عمل ما بعد React على نفس الأفكار الأساسية:
- باستخدام التفاعل (على سبيل المثال إشارات) لتحديثات DOM.
- استخدام القوالب المستنسخة لعرض DOM.
- باستخدام واجهات برمجة تطبيقات الويب الحديثة مثل
و
Proxy
، مما يجعل كل ما سبق أسهل.
الآن لنكون واضحين، تختلف هذه الأطر كثيرًا على المستوى الجزئي، وفي كيفية تعاملها مع أشياء مثل مكونات الويب، والتجميع، وواجهات برمجة التطبيقات التي تواجه المستخدم. ليس كل الأطر حتى الاستخدام Proxy
س. ولكن بشكل عام، يبدو أن معظم مؤلفي الإطار يتفقون على الأفكار المذكورة أعلاه، أو أنهم يتحركون في هذا الاتجاه.
لذا، بالنسبة لإطار العمل الخاص بنا، دعونا نحاول القيام بالحد الأدنى لتنفيذ هذه الأفكار، بدءًا من التفاعل.
التفاعل
كثيرا ما يقال ذلك “رد الفعل ليس رد الفعل”. ما يعنيه هذا هو أن React لديها نموذج يعتمد على السحب أكثر من نموذج يعتمد على الدفع. لتبسيط الأمور إلى حد كبير: تفترض React أن شجرة DOM الافتراضية بأكملها تحتاج إلى إعادة بنائها من البداية، والطريقة الوحيدة لمنع هذه التحديثات هي التنفيذ useMemo
(أو في الأيام الخوالي، shouldComponentUpdate
).
يؤدي استخدام DOM الافتراضي إلى تخفيف بعض تكلفة استراتيجية “ألغِ كل شيء وابدأ من الصفر”، لكنه لا يحل المشكلة بالكامل. ومطالبة المطورين بكتابة رمز المذكرة الصحيح هي معركة خاسرة. (يرى تفاعل ننسى لمحاولة مستمرة لحل هذا.)
بدلاً من ذلك، تستخدم الأطر الحديثة نموذجًا تفاعليًا قائمًا على الدفع. في هذا النموذج، تشترك الأجزاء الفردية من شجرة المكونات في تحديثات الحالة ولا تقوم بتحديث DOM إلا عندما تتغير الحالة ذات الصلة. يؤدي هذا إلى إعطاء الأولوية للتصميم “الأداء افتراضيًا” مقابل بعض تكاليف مسك الدفاتر المسبقة (خاصة فيما يتعلق بالذاكرة) لتتبع أجزاء الحالة المرتبطة بأجزاء واجهة المستخدم.
لاحظ أن هذه التقنية لا تتعارض بالضرورة مع أسلوب DOM الافتراضي: أدوات مثل إشارات مسبقة و مليون أظهر أنه يمكنك الحصول على نظام هجين. يعد هذا مفيدًا إذا كان هدفك هو الاحتفاظ بإطار عمل DOM الافتراضي الموجود لديك (مثل React) ولكن مع تطبيق النموذج القائم على الدفع بشكل انتقائي لسيناريوهات أكثر حساسية للأداء.
في هذا المنشور، لن أقوم بإعادة صياغة تفاصيل الإشارات نفسها، أو موضوعات أكثر دقة مثل تفاعل دقيق الحبيباتولكنني سأفترض أننا سنستخدم نظامًا تفاعليًا.
استنساخ أشجار DOM
لفترة طويلة، كانت الحكمة الجماعية في أطر عمل JavaScript هي أن أسرع طريقة لعرض DOM هي إنشاء كل عقدة DOM وتركيبها على حدة. وبعبارة أخرى، يمكنك استخدام واجهات برمجة التطبيقات مثل createElement
, setAttribute
، و textContent
لبناء DOM قطعة قطعة:
const div=document.createElement('div')div.setAttribute('class', 'blue')div.textContent='Blue!'
أحد البدائل هو إدخال سلسلة HTML كبيرة في الملف innerHTML
ودع المتصفح يحللها لك:
const container=document.createElement('div')container.innerHTML=`Blue!`
هذا النهج الساذج له جانب سلبي كبير: إذا كان هناك أي محتوى ديناميكي في HTML الخاص بك (على سبيل المثال، red
بدلاً من blue
)، فستحتاج إلى تحليل سلاسل HTML مرارًا وتكرارًا. بالإضافة إلى ذلك، فإنك تدمر DOM مع كل تحديث، مما سيؤدي إلى إعادة تعيين الحالة مثل value
ل س.
ومع ذلك، في مرحلة ما، اكتشف الناس أن تحليل HTML مرة واحدة ومن ثم الاتصال cloneNode(true)
على كل شيء يتم تنفيذه بسرعة كبيرة:
const template=document.createElement('template')template.innerHTML=`Blue!`template.content.cloneNode(true) // this is fast!
أنا هنا أستخدم أ العلامة، التي تتمتع بميزة إنشاء DOM “خامل”. وبعبارة أخرى، أشياء مثل
أو
لا تبدأ بتنزيل أي شيء تلقائيًا.
ما مدى سرعة ذلك مقارنة بواجهات برمجة تطبيقات DOM اليدوية؟ للتوضيح، هنا معيار صغير. مقياس سرعة الدوران تشير التقارير إلى أن تقنية الاستنساخ أسرع بنسبة 50% تقريبًا في Chrome، وأسرع بنسبة 15% في Firefox، وأسرع بنسبة 10% في Safari. (سيختلف هذا بناءً على حجم DOM وعدد التكرارات، ولكنك ستحصل على الجوهر.)
ما هو مثير للاهتمام هو ذلك هي واجهة برمجة تطبيقات متصفح جديدة، غير متوفرة في IE11، ومصممة في الأصل لمكونات الويب. ومن المفارقات إلى حد ما، أن هذه التقنية تُستخدم الآن في مجموعة متنوعة من أطر عمل JavaScript، بغض النظر عما إذا كانت تستخدم مكونات الويب أم لا.
هناك تحدٍ رئيسي واحد في هذه التقنية، وهو كيفية تحديث المحتوى الديناميكي بكفاءة دون التخلص من حالة DOM. سنغطي هذا لاحقًا عندما نبني إطار لعبتنا.
واجهات برمجة تطبيقات جافا سكريبت الحديثة
لقد واجهنا بالفعل واجهة برمجة تطبيقات جديدة تساعد كثيرًا، وهي . هناك شيء آخر يكتسب قوة جذب بشكل مطرد وهو
Proxy
، والتي يمكن أن تجعل بناء نظام التفاعل أسهل بكثير.
عندما نبني مثال لعبتنا، سنستخدم أيضًا حرفية القالب ذات العلامات لإنشاء API مثل هذا:
const dom=html`Hello ${ name }!`
لا تستخدم جميع الأطر هذه الأداة، ولكن من أبرزها Lit، هايبرHTML، و ArrowJS. يمكن أن تجعل القيم الحرفية للنماذج ذات العلامات من السهل جدًا إنشاء واجهات برمجة تطبيقات مريحة لقوالب HTML دون الحاجة إلى مترجم.
الخطوة 1: بناء التفاعل
التفاعل هو الأساس الذي سنبني عليه بقية الإطار. ستحدد التفاعلية كيفية إدارة الحالة، وكيفية تحديث DOM عندما تتغير الحالة.
لنبدأ مع بعض “كود الحلم” لتوضيح ما نريد:
const state={}state.a=1state.b=2createEffect(()=> { state.sum=state.a + state.b})
في الأساس، نريد اسم “كائن سحري”. state
، مع دعامتين: a
و b
. وكلما تغيرت تلك الدعائم، نريد أن نثبت sum
ليكون مجموع الاثنين.
على افتراض أننا لا نعرف الدعائم مقدما (أو لدينا مترجم لتحديدها)، فإن الكائن العادي لن يكون كافيا لهذا الغرض. لذلك دعونا نستخدم أ Proxy
، والتي يمكن أن تتفاعل كلما تم تعيين قيمة جديدة:
const state=new Proxy({}, { get(obj, prop) { onGet(prop) return obj[prop] }, set(obj, prop, value) { obj[prop]=value onSet(prop, value) return true }})
الآن، لدينا Proxy
لا يفعل أي شيء مثير للاهتمام، باستثناء أن يقدم لنا بعضًا منه onGet
و onSet
خطافات. لذلك دعونا نجعلها تتدفق التحديثات بعد القيام بمهمة صغيرة:
let queued=falsefunction onSet(prop, value) { if (!queued) { queued=true queueMicrotask(()=> { queued=false flush() }) }}
لماذا تدفق التحديثات؟ في الغالب لأننا لا نريد إجراء الكثير من العمليات الحسابية. إذا قمنا بتحديث كلما على حد سواء a
و b
التغيير، ثم سنقوم بحساب عديمة الفائدة sum
مرتين. ومن خلال دمج التدفق في مهمة صغيرة واحدة، يمكننا أن نكون أكثر كفاءة.
التالي، دعونا نجعل flush
تحديث المبلغ:
function flush() { state.sum=state.a + state.b}
وهذا أمر عظيم، ولكنه ليس “رمز أحلامنا” بعد. سنحتاج إلى التنفيذ createEffect
بحيث sum
يتم حسابها فقط عندما a
و b
التغيير (وليس عندما يتغير شيء آخر!).
للقيام بذلك، دعونا نستخدم كائنًا لتتبع التأثيرات التي يجب تشغيلها لكل من الخاصيات:
const propsToEffects={}
التالي يأتي الجزء الحاسم! نحن بحاجة للتأكد من أن آثارنا يمكن يشترك إلى الدعائم الصحيحة. للقيام بذلك، سنقوم بتشغيل التأثير، لاحظ أي get
المكالمات التي يقوم بها، وإنشاء تعيين بين الدعامة والتأثير.
لتفكيكها، تذكر أن “رمز حلمنا” هو:
createEffect(()=> { state.sum=state.a + state.b})
عند تشغيل هذه الدالة، فإنها تستدعي حرفين: state.a
و state.b
. يجب أن تؤدي هذه الحروف إلى تحفيز النظام التفاعلي لملاحظة أن الوظيفة تعتمد على الدعامتين.
لتحقيق ذلك، سنبدأ بعالمية بسيطة لتتبع التأثير “الحالي”:
let currentEffect
ثم، createEffect
ستقوم الدالة بتعيين هذا على المستوى العام قبل استدعاء الوظيفة:
function createEffect(effect) { currentEffect=effect effect() currentEffect=undefined}
الشيء المهم هنا هو أن التأثير في الحال تم استدعاؤه، مع currentEffect
يتم تحديدها عالميًا مسبقًا. هذه هي الطريقة التي يمكننا بها تتبع أي شيء قد يستدعيه التأثير.
والآن يمكننا تنفيذ onGet
في لدينا Proxy
، والتي سوف تقوم بإعداد رسم الخرائط بين العالمية currentEffect
والممتلكات:
function onGet(prop) { const effects=propsToEffects[prop] ?? (propsToEffects[prop]=[]) effects.push(currentEffect)}
بعد ذلك يتم تشغيله مرة واحدة، propsToEffects
يجب أن يبدو مثل هذا:
{ "a": [theEffect], "b": [theEffect]}
…أين theEffect
هي الدالة “المجموع” التي نريد تشغيلها.
التالي، لدينا onSet
يجب إضافة أي تأثيرات تحتاج إلى تشغيلها على ملف dirtyEffects
مجموعة مصفوفة:
const dirtyEffects=[]function onSet(prop, value) { if (propsToEffects[prop]) { dirtyEffects.push(...propsToEffects[prop]) // ... }}
في هذه المرحلة، لدينا كل القطع في مكانها الصحيح flush
لاستدعاء جميع dirtyEffects
:
function flush() { while (dirtyEffects.length) { dirtyEffects.shift()() }}
وبجمع كل ذلك معًا، أصبح لدينا الآن نظام تفاعلي يعمل بكامل طاقته! يمكنك اللعب بها بنفسك ومحاولة الإعداد state.a
و state.b
في وحدة تحكم DevTools – state.sum
سيتم التحديث كلما تغير أي منهما.
الآن، هناك الكثير من الحالات المتقدمة التي لدينا لا تغطي هنا:
- استخدام
try
/catch
في حالة حدوث خطأ - تجنب تشغيل نفس التأثير مرتين
- منع دورات لا نهاية لها
- تأثيرات الاشتراك في الدعائم الجديدة في عمليات التشغيل اللاحقة (على سبيل المثال، إذا تم استدعاء بعض الحروف فقط في ملف
if
حاجز)
ومع ذلك، هذا أكثر من كافٍ لمثال لعبتنا. دعنا ننتقل إلى عرض DOM.
الخطوة 2: عرض DOM
لدينا الآن نظام تفاعلي وظيفي، ولكنه في الأساس “بلا رأس”. يمكنه تتبع التغييرات وحساب التأثيرات، ولكن هذا كل ما في الأمر.
ومع ذلك، في مرحلة ما، يحتاج إطار عمل JavaScript لدينا إلى عرض بعض عناصر DOM على الشاشة. (هذا نوع من بيت القصيد.)
في هذا القسم، دعونا ننسى التفاعل للحظة ونتخيل أننا فقط محاولة إنشاء وظيفة يمكنها 1) إنشاء شجرة DOM و2) تحديثها بكفاءة.
مرة أخرى، لنبدأ ببعض رموز الأحلام:
function render(state) { return html`${state.text}`}
كما ذكرت، أنا أستخدم قوالب حرفية ذات علامات، لأنني وجدتها طريقة رائعة لكتابة قوالب HTML دون الحاجة إلى مترجم. (سنرى في لحظة لماذا قد نفعل ذلك بالفعل يريد مترجم بدلا من ذلك.)
نحن نعيد استخدام لدينا state
كائن من قبل، هذه المرة مع أ color
و text
ملكية. ربما تكون الدولة مثل:
state.color='blue'state.text='Blue!'
عندما نجتاز هذا state
داخل render
، يجب أن تعيد شجرة DOM مع الحالة المطبقة:
Blue!
قبل أن نذهب إلى أبعد من ذلك، نحتاج إلى كتاب تمهيدي سريع حول القيم الحرفية للقالب الموسوم. ملكنا html
العلامة هي مجرد دالة تتلقى وسيطتين: the tokens
(مجموعة من سلاسل HTML الثابتة) و expressions
(التعبيرات الديناميكية التي تم تقييمها):
function html(tokens, ...expressions) {}
في هذه الحالة، tokens
هي (تمت إزالة المسافة البيضاء):
[ "", ""]
و ال expressions
نكون:
[ "blue", "Blue!"]
ال tokens
ستكون المصفوفة دائمًا أطول بمقدار 1 بالضبط من expressions
المصفوفة، حتى نتمكن من تجميعها معًا بشكل تافه:
const allTokens=tokens .map((token, i)=> (expressions[i - 1] ?? '') + token)
سيعطينا هذا مجموعة من السلاسل:
[ "", "Blue!", ""]
يمكننا ضم هذه السلاسل معًا لإنشاء HTML الخاص بنا:
const htmlString=allTokens.join('')
ومن ثم يمكننا استخدامها innerHTML
لتحليلها إلى أ :
function parseTemplate(htmlString) { const template=document.createElement('template') template.innerHTML=htmlString return template}
يحتوي هذا القالب على DOM الخامل الخاص بنا (من الناحية الفنية أ DocumentFragment
) ، والتي يمكننا استنساخها حسب الرغبة:
const cloned=template.content.cloneNode(true)
بالطبع، تحليل HTML الكامل كلما كان html
لن يكون استدعاء الوظيفة رائعًا للأداء. لحسن الحظ، تحتوي القيم الحرفية للنماذج ذات العلامات على ميزة مدمجة ستساعد كثيرًا هنا.
بالنسبة لكل استخدام فريد لنموذج حرفي ذي علامة تمييز، فإن tokens
المصفوفة هي دائما نفس الشيء كلما تم استدعاء الدالة – في الواقع، إنها نفس الكائن بالضبط!
على سبيل المثال، النظر في هذه الحالة:
function sayHello(name) { return html`Hello ${name}`}
حينما sayHello
يسمى، tokens
ستكون المصفوفة متطابقة دائمًا:
[ "Hello ", ""]
الوقت الوحيد tokens
سيكون مختلفًا بالنسبة للمواقع المختلفة تمامًا للقالب الذي تم وضع علامة عليه:
html``html`` // Different from above
يمكننا استخدام هذا لصالحنا باستخدام WeakMap
للحفاظ على رسم الخرائط لل tokens
مجموعة إلى النتيجة template
:
const tokensToTemplate=new WeakMap()function html(tokens, ...expressions) { let template=tokensToTemplate.get(tokens) if (!template) { // ... template=parseTemplate(htmlString) tokensToTemplate.set(tokens, template) } return template}
هذا هو نوع من المفهوم المذهل، ولكن التفرد tokens
المصفوفة تعني في الأساس أنه يمكننا التأكد من أن كل استدعاء لـ html`...`
يوزع HTML مرة واحدة فقط.
بعد ذلك، نحتاج فقط إلى طريقة لتحديث عقدة DOM المستنسخة بامتداد expressions
المصفوفة (والتي من المحتمل أن تكون مختلفة في كل مرة، على عكس tokens
).
ولتبسيط الأمور، دعنا نستبدل expressions
صفيف مع عنصر نائب لكل فهرس:
const stubs=expressions.map((_, i)=> `__stub-${i}__`)
إذا قمنا بضغط هذا كما كان من قبل، فسيؤدي إلى إنشاء ملف HTML هذا:
__stub-1__
يمكننا كتابة دالة استبدال سلسلة بسيطة لاستبدال بذرة:
function replaceStubs (string) { return string.replaceAll(/__stub-(d+)__/g, (_, i)=> ( expressions[i] ))}
والآن كلما html
تم استدعاء الوظيفة، يمكننا استنساخ القالب وتحديث العناصر النائبة:
const element=cloned.firstElementChildfor (const { name, value } of element.attributes) { element.setAttribute(name, replaceStubs(value))}element.textContent=replaceStubs(element.textContent)
الآن، لا يزال هذا غير فعال بشكل كبير – لا سيما أننا نقوم بالتحديث textContent
والسمات التي لا تحتاج بالضرورة إلى التحديث. لكن بالنسبة لإطار لعبتنا، فهذا جيد بما فيه الكفاية.
يمكننا اختبار ذلك من خلال تقديم مختلف state
:
document.body.appendChild(render({ color: 'blue', text: 'Blue!' }))document.body.appendChild(render({ color: 'red', text: 'Red!' }))
هذا يعمل!
الخطوة 3: الجمع بين التفاعل وعرض DOM
وبما أن لدينا بالفعل createEffect
من نظام العرض أعلاه، يمكننا الآن دمج الاثنين لتحديث DOM بناءً على الحالة:
const container=document.getElementById('container')createEffect(()=> { const dom=render(state) if (container.firstElementChild) { container.firstElementChild.replaceWith(dom) } else { container.appendChild(dom) }})
هذا يعمل في الواقع! يمكننا دمج هذا مع مثال “المجموع” من قسم التفاعل بمجرد إنشاء تأثير آخر لتعيين text
:
createEffect(()=> { state.text=`Sum is: ${state.sum}`})
هذا يجعل “المجموع هو 3”:
يمكنك اللعب بمثال اللعبة هذا. إذا قمت بتعيين state.a=5
، فسيتم تحديث النص تلقائيًا ليقول “Sum is 7”.
الخطوات التالية
هناك الكثير من التحسينات التي يمكننا إجراؤها على هذا النظام، خاصة جزء عرض DOM.
والجدير بالذكر أننا نفتقد طريقة لتحديث محتوى العناصر داخل شجرة DOM العميقة، على سبيل المثال:
${text}
لهذا، سنحتاج إلى طريقة لتحديد كل عنصر داخل القالب بشكل فريد. هناك الكثير من الطرق للقيام بذلك:
- مضاء، عند تحليل HTML، يستخدم نظام التعابير المنطقية ومطابقة الأحرف لتحديد ما إذا كان العنصر النائب موجودًا ضمن سمة أو محتوى نصي، بالإضافة إلى فهرس العنصر الهدف (في العمق أولاً
TreeWalker
طلب). - تتمتع أطر العمل مثل Svelte وSolid برفاهية تحليل قالب HTML بأكمله أثناء التجميع، مما يوفر نفس المعلومات. كما يقومون أيضًا بإنشاء رمز يستدعي
firstChild
وnextSibling
لاجتياز DOM للعثور على العنصر المراد تحديثه.
سواء قررنا إجراء تحليل من جانب العميل على النمط Lit أو تحليل وقت الترجمة على النمط Svelte/Solid، فإن ما نريده هو نوع من التعيين مثل هذا:
[ { elementIndex: 0, //above attributeName: 'class', stubIndex: 0 // index in expressions array }, { elementIndex: 1 // above textContent: true, stubIndex: 1 // index in expressions array }]ستخبرنا هذه الارتباطات بالضبط بالعناصر التي تحتاج إلى تحديث، وأي سمة (أو
textContent
) يجب تعيينه، ومكان العثور علىexpression
لاستبدال كعب الروتين.ستكون الخطوة التالية هي تجنب استنساخ القالب في كل مرة، وتحديث DOM مباشرة بناءً على ملف
expressions
. بمعنى آخر، لا نريد التحليل مرة واحدة فقط، بل نريد استنساخ الارتباطات وإعدادها مرة واحدة فقط. وهذا من شأنه أن يقلل كل تحديث لاحق إلى الحد الأدنى منsetAttribute
وtextContent
المكالمات.هناك نمط آخر مثير للاهتمام يجب تنفيذه وهو التكرارات (أو المكررات)، والتي تأتي مع مجموعة التحديات الخاصة بها، مثل التوفيق بين القوائم بين التحديثات والتعامل مع "المفاتيح" من أجل الاستبدال الفعال.
أنا متعب، رغم ذلك، وقد استمرت مشاركة المدونة هذه لفترة كافية. لذلك أترك الباقي كتمرين للقارئ!
خاتمة
إذن هذا هو الحال. في مقالة مدونة واحدة (طويلة)، قمنا بتنفيذ إطار عمل JavaScript الخاص بنا. لا تتردد في استخدام هذا كأساس لإطار عمل JavaScript الجديد الخاص بك، لإصداره للعالم وإثارة غضب جمهور Hacker News.
أنا شخصياً وجدت هذا المشروع تعليميًا للغاية، وهذا هو سبب قيامي به في المقام الأول. كنت أتطلع أيضًا إلى استبدال الإطار الحالي لـ مكون منتقي الرموز التعبيرية الخاص بي مع حل أصغر حجمًا وأكثر تخصيصًا. وفي هذه العملية تمكنت من الكتابة إطار صغير يجتاز جميع الاختبارات الحالية وهو أصغر بحوالي 6 كيلو بايت من التطبيق الحالي، وهو ما أنا فخور به جدًا.
في المستقبل، أعتقد أنه سيكون من الرائع أن تكون واجهات برمجة تطبيقات المتصفح كاملة الميزات بما يكفي لتسهيل إنشاء إطار عمل مخصص. على سبيل المثال، اقتراح DOM Part API من شأنه أن يزيل الكثير من العمل الشاق في نظام التحليل والاستبدال DOM الذي أنشأناه أعلاه، بينما يفتح الباب أيضًا أمام تحسينات محتملة في أداء المتصفح. يمكنني أيضًا أن أتخيل (مع بعض الإيماءات الجامحة) أن هذا امتداد لـ
Proxy
يمكن أن يسهل بناء نظام تفاعل كامل دون القلق بشأن التفاصيل مثل التنظيف أو التجميع أو اكتشاف الدورة.إذا كانت كل هذه الأشياء موجودة، فيمكنك أن تتخيل بشكل فعال وجود "إضاءة في المتصفح"، أو على الأقل طريقة لإنشاء "إضاءة في المتصفح" الخاصة بك بسرعة. في هذه الأثناء، آمل أن يكون هذا التمرين الصغير قد ساعد في توضيح بعض الأشياء التي يفكر فيها مؤلفو إطار العمل، وبعض الآلات الموجودة ضمن إطار عمل JavaScript المفضل لديك.
شكرا ل بيير ماري دارتوس للحصول على تعليقات على مسودة هذا المنشور.
اقرأ أكثر