Vue: [تم التخلي عنه] RFC: تبسيط استخدام فتحة النطاق

تم إنشاؤها على ١١ ديسمبر ٢٠١٨  ·  36تعليقات  ·  مصدر: vuejs/vue

هذه متابعة https://github.com/vuejs/vue/issues/7740#issuecomment -371309357

عاقل

مشاكل الاستخدام الحالي للفتحة المحددة النطاق:

  • الإسهاب في حالة استخدام <template slot-scope>
  • يقتصر على عنصر / مكون واحد باستخدام slot-scope مباشرة على عنصر الفتحة.

عرض

قدم توجيهًا جديدًا v-scope ، والذي يمكن استخدامه فقط مع المكونات:

<comp v-scope="scope">
  {{ scope.msg }}
</comp>

سيعمل بنفس الطريقة مثل slot-scope للفتحة الافتراضية ذات النطاق لـ <comp> (مع تقديم <comp> قيمة النطاق). لذلك فهي تعمل أيضًا مع التفكيك:

<comp v-scope="{ msg }">
  {{ msg }}
</comp>

لماذا التوجيه الجديد

أعتقد أن الفريق ناقش لفترة وجيزة ما اقترحته في https://github.com/vuejs/vue/issues/7740#issuecomment -371309357 على Slack منذ فترة ، لكن لم يعد بإمكاني العثور على سجل الدردشة. إليك السبب وراء التوجيه الجديد:

  • slot-scope كسمة خاصة بدلاً من توجيه (السمات التي تبدأ بـ v- بادئة) لأن slot هي سمة ، وأردنا الحفاظ على اتساق السمات المتعلقة بالفتحة . slot بدوره كسمة غير توجيهية لأننا نريد أن يعكس الاستخدام الاستخدام الفعلي للفتحة في Shadow DOM القياسي. لقد توصلنا إلى أنه سيكون من الأفضل تجنب وجود موازٍ لنا v-slot عندما يكون هناك شيء مماثل من الناحية المفاهيمية في المعيار.

  • في الأصل ، تم تصميم slot-scope ليكون قابلاً للاستخدام فقط على عناصر <template> التي تعمل كحاويات مجردة. لكن هذا كان مطولًا - لذلك قدمنا ​​إمكانية استخدامه مباشرة على عنصر فتحة بدون التفاف <template> . ومع ذلك ، فإن هذا أيضًا يجعل من المستحيل السماح باستخدام slot-scope مباشرة على المكون نفسه ، لأنه قد يؤدي إلى الغموض كما هو موضح هنا .

  • فكرت في إضافة معدِّلات أو بادئات خاصة إلى slot-scope حتى نتمكن من استخدامها على أحد المكونات مباشرةً للإشارة إلى أن محتوى الفتحة الخاص به يجب أن يُعامل على أنه الفتحة الافتراضية ذات النطاق ، ولكن ليس معدلاً أو بادئة مثل $ يبدو أن

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

مخاوف

  • يختلف التعبير المقبول بواسطة v-scope عن معظم التوجيهات الأخرى: فهو يتوقع اسم متغير مؤقت (والذي يمكن أن يكون أيضًا تفكيكًا) ، ولكن ليس بدون أسبقية: فهو يعمل تمامًا مثل الجزء المستعار من v-for . لذا من الناحية النظرية ، يقع v-scope في نفس المعسكر مع v-for كتوجيه هيكلي يخلق متغيرات مؤقتة لنطاقه الداخلي.

  • سيؤدي هذا إلى كسر رمز المستخدم إذا كان لدى المستخدم توجيه مخصص باسم v-scope ويستخدم في أحد المكونات.

    • نظرًا لأن التوجيهات المخصصة في الإصدار 2 تركز بشكل أساسي على التلاعب المباشر في DOM ، فمن النادر نسبيًا رؤية توجيهات مخصصة مستخدمة على المكونات ، والأكثر من ذلك أن الأمر يتعلق بشيء يسمى v-scope ، لذلك يجب أن يكون التأثير ضئيلًا.

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

discussion intend to implement

التعليق الأكثر فائدة

تحطيم المشكلة

عند محاولة التوليف ، يبدو أننا نريد حلاً من شأنه:

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

قد يكون لدي حل يعالج كل هذه! 🤞 مع $event لـ v-on ، لدينا سابقة لتوفير التعبيرات التي تعمل داخل دالة ضمنية مع وسيطة مسماة. يبدو أن الناس يستمتعون بهذه الراحة ولا أرى الكثير من الالتباس الناجم عنها ، لذلك ربما ينبغي أن نتبع هذا المثال للفتحات المحددة النطاق.

الحل المقترح

بدلاً من استخدام v-scope ، يمكننا إتاحة نطاق الفتحة كـ $slot . على سبيل المثال:

<TodoList :todos="todos">
  {{ $slot.todo.text }}

  <template slot="metaBar">
    You have {{ $slot.totals.incomplete }} todos left.
  </template>
</TodoList>

بالنسبة للسياق ، قد يبدو القالب الفرعي بالشكل التالي:

<ul>
  <li v-for="todo in todos">
    <slot :todo="todo" />
  </li>
</ul>

<div>
  <slot name="metaBar" v-bind="itemsStats" />
</div>

عندما تتداخل الفتحات ، فإن الكائن $slot سوف يدمج بيانات الفتحة ، مع إعطاء الأولوية لمعظم الفتحات الداخلية. على سبيل المثال ، في:

<Outer>
  <Middle>
    <Inner>
      {{ $slot }}
    </Inner>
  </Middle>
</Outer>

قد يبدو الدمج كالتالي:

$slot = {
  ...outerSlotData,
  ...middleSlotData,
  ...innerSlotData
}

قد تكون قلقًا / تتساءل عن كيفية التعامل مع تداخل مساحة الاسم وفي 99.9٪ من الحالات ، لا أعتقد حقًا أنها ستكون مشكلة. على سبيل المثال:

<UserData id="chrisvfritz">
  My email is {{ $slot.user.email }}, but you can reach Sarah at
  <UserData id="sdras">
    {{ $slot.user.email }}
  </UserData>.
</UserData>

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

<UserData id="chrisvfritz">
  <template slot-scope="me">
    <UserData id="sdras">
      <template slot-scope="sarah">
        <CollaborationPageLink :user-ids="[me.user.id, sarah.user.id]">
          View your collaboration history
        </CollaborationPageLink>
      </template>
    </UserData>
  </template>
</UserData>

بالنسبة إلى حالات الحافة النادرة هذه ، لا يزال بإمكان المستخدمين استخدام slot-scope بسلوكه الحالي باعتباره فتحة هروب . ولكن سيظل v-scope غير ضروري ، لأنني إذا فهمت بشكل صحيح ، فسيكون الغرض منه هو تبسيط حالات الاستخدام الأكثر شيوعًا ، والتي كان من الممكن أن ينجزها الكائن $slot بالفعل دون التسبب في نفس المشاكل.

مزايا

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

  • لمعرفة البيانات التي يمكنهم الوصول إليها ، يتعين على المستخدمين فقط التفكير في الفتحة (الفتحات) التي سيتم تقديم المحتوى إليها . أعتقد أنه مع v-scope ، سيكون هناك الكثير من الالتباس مع الأشخاص الذين يفترضون أنه يعمل مثل v-for ، لأن هذا هو التوجيه الوحيد الآخر الذي يحدد خصائص السياق المحددة النطاق.

  • لا يتعين على المستخدمين أن يكونوا مألوفين ومرتاحين في التدمير لاستخدام الفتحات المحددة النطاق ، وبالتالي تقليل منحنى التعلم.

  • في بعض الحالات ، خاصة مع العديد من الفتحات المتداخلة التي توفر جميعها حالة ، لن يكون من الواضح _ أي مكون أتت منه بعض الحالات وسيتعين على المستخدمين الوصول إلى slot-scope مرة أخرى. قد يبدو هذا وكأنه عيب ، ولكن عندما أرى فتحات ذات نطاق متداخل ، فإنها دائمًا ما تكون لنمط مزود الحالة ، والذي أشعر بشدة أنه مضاد للنمط. هذا هو المنطق الخاص فإن حقيقة أن العديد من الفتحات ذات النطاق المتداخلة تتطلب المزيد من النماذج المعيارية يمكن أن تقلل في الواقع من استخدام الأنماط المضادة .

  • تم تقليل مساحة سطح واجهة برمجة التطبيقات بشكل كبير ، لأن معظم مطوري Vue سيتعين عليهم فقط تذكر $slot ، وهو مجرد كائن.

  • باستخدام خاصية $ -prefixed ، فإننا نبني على واجهة برمجة تطبيقات فتحة مكون الويب بطريقة توضح أن $slot هو شيء Vue.

  • حاليًا ، غالبًا ما تفعل المكتبات شيئًا مثل <slot v-bind="user" /> حتى يتمكن مستخدموها من حفظ بضعة أحرف باستخدام slot-scope="user" بدلاً من slot-scope="{ user } . يبدو الأمر أنيقًا للوهلة الأولى ، لكنني جئت لتجربة نمط مضاد. تنشأ المشكلة عندما يريد المكوِّن عرض بيانات _ أخرى غير_ للمستخدم. ثم يكون لديهم خياران: إجراء تغيير فاصل لواجهة برمجة التطبيقات الخاصة بهم أو فرض هذه الخاصية الجديدة على الكائن user ، على الرغم من أنه قد لا يكون له علاقة بالمستخدم. لا خيار رائع. لحسن الحظ ، فإن $slot سيزيل الإغراء لجعل المكونات أقل مقاومة للمستقبل ، لأنه في حين أن $slot لا يزال أقصر من $slot.user ، فإنك تفقد السياق المهم عن طريق تسمية المستخدم باسم $slot .

سلبيات

  • في بعض الفتحات المحددة النطاق المتداخلة ، هناك بعض حالات الحافة حيث لن يكون واضحًا _ أي مكون أتت بعض البيانات منه. في هذه الحالات ، لا يزال بإمكان المستخدمين الوصول إلى slot-scope عندما يحتاجون إلى أقصى قدر من الوضوح لذلك لا أعتقد أنها مشكلة كبيرة. بالإضافة إلى ذلك ، كما ذكرت سابقًا ، أعتقد أنه من المحتمل جدًا أن يقوم المستخدم بإطلاق النار على نفسه بمكونات مزود الحالة إذا كان لديه هذه المشكلة في المقام الأول.

ال 36 كومينتر

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

بشكل عام ، سيؤدي ذلك إلى تحسين استخدام واجهة برمجة التطبيقات للمكونات التي لا تصدّر ، والتي يتم استخدامها بشكل أكبر مع مرور الوقت 🙌

إذا فهمت بشكل صحيح ، فإن v-scope يخدم حالة استخدام واحدة فقط. بدلاً من الكتابة:

<foo>
  <template slot-scope="{ item }">{{ item.id }}</template>
</foo>

يمكننا أن نكتب:

<foo v-scope="{ item }">{{ item.id }}</foo>

لقد قللت كثيرًا من الضوضاء في هذه الحالة ولكن يبدو أنها الحالة الوحيدة. يبدو الأمر وكأنه نوع من المبالغة في تقديم توجيه جديد (والذي يعمل بشكل مختلف تمامًا عن التوجيهات الأخرى ، حتى من v-for لأنه يعمل فقط على المكونات ويعتمد على <slot> منطق تحتها). مصدر قلق آخر هو أنه عندما أبحث عن v-scope في هذا الريبو ، يناقش كل من with في JavaScript.

أحبها. يبقى سؤال واحد بالنسبة لي هو كيف نريد التعامل مع الخانات الزمنية المسماة. إذا أردنا السماح للفتحات المسماة باستخدام نفس التوجيه ، فسنحتاج إلى السماح لها على <template> أيضًا:

<comp v-scope="scope">
  {{ scope.msg }} <!-- default slot, inheriting from the scope from above -->
  <template slot="someName" v-scope="otherScope">
    <p>{{ otherScope }}</p>
    <div> whatever</div>
  </template>
</comp>

يمكننا استغلال هذه الفرصة لإهمال sot-scope وإزالته في Vue 3 ، واستبداله بـ v-scope

أعتقد أنه لا ينبغي أن ينتهي بنا المطاف بمفهومين مختلفين (التوجيه v-scope و slot-scope صفة) لنفس الشيء.

Sidenote: الآن ، بالنظر إلى ذلك ، قد يحصل الأشخاص على انطباع من التسلسل الهرمي للعناصر &
التوجيهات ، أنه يمكنهم الوصول إلى otherScope و scope في الخانة المحددة. قد يكون جانب سلبي.

LinusBorg بالنسبة للاسم ، يمكن أن تؤدي الوسيطة v-scope:slotName . أعتقد أن الهدف من السماح بهذا فقط على المكونات هو استبدال ما قاله @ Justineo (https://github.com/vuejs/vue/issues/9180#issuecomment-446168296)

Sidenote: الآن ، بالنظر إلى ذلك ، قد يحصل الأشخاص على انطباع من التسلسل الهرمي للعناصر &
التوجيهات ، يمكنهم الوصول إلى otherScope والنطاق في الفتحة المسماة. قد يكون جانب سلبي.

هذا يعمل ، أليس كذلك؟ 🤔 أنا متأكد من أنني قد قمت بتداخل نطاقات الفتحات

هذا يعمل ، أليس كذلك؟ 🤔 أنا متأكد من أنني قد قمت بتداخل نطاقات الفتحات

أنا لم أعششهم ، على الرغم من أنهم فتحات شقيق. لقد حددت نطاق الفتحة الافتراضية بـ v-scope على المكون ، والفتحة المسماة (وهي فتحة للأخوة ) بـ <template slot="someName">
ارى؟ إنه محير ^ ^

LinusBorg أعتقد أن الاستخدام سيكون مربكًا. أعتقد أنه من الناحية المفاهيمية:

  • عندما يتم استخدام v-scope على المكون مباشرة ، فهذا يعني أنك تستخدم الخانة الافتراضية فقط.
  • إذا كنت تريد استخدام فتحات مسماة ... ما زلت بحاجة إلى استخدام slot + slot-scope .

أوافق على أن الحصول على كل من v-scope و slot-scope قد يكون غير متسق / مربك ، خاصة للمستخدمين الجدد الذين لا يعرفون كيف وصلنا إلى التصميم الحالي.

تحطيم المشكلة

عند محاولة التوليف ، يبدو أننا نريد حلاً من شأنه:

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

قد يكون لدي حل يعالج كل هذه! 🤞 مع $event لـ v-on ، لدينا سابقة لتوفير التعبيرات التي تعمل داخل دالة ضمنية مع وسيطة مسماة. يبدو أن الناس يستمتعون بهذه الراحة ولا أرى الكثير من الالتباس الناجم عنها ، لذلك ربما ينبغي أن نتبع هذا المثال للفتحات المحددة النطاق.

الحل المقترح

بدلاً من استخدام v-scope ، يمكننا إتاحة نطاق الفتحة كـ $slot . على سبيل المثال:

<TodoList :todos="todos">
  {{ $slot.todo.text }}

  <template slot="metaBar">
    You have {{ $slot.totals.incomplete }} todos left.
  </template>
</TodoList>

بالنسبة للسياق ، قد يبدو القالب الفرعي بالشكل التالي:

<ul>
  <li v-for="todo in todos">
    <slot :todo="todo" />
  </li>
</ul>

<div>
  <slot name="metaBar" v-bind="itemsStats" />
</div>

عندما تتداخل الفتحات ، فإن الكائن $slot سوف يدمج بيانات الفتحة ، مع إعطاء الأولوية لمعظم الفتحات الداخلية. على سبيل المثال ، في:

<Outer>
  <Middle>
    <Inner>
      {{ $slot }}
    </Inner>
  </Middle>
</Outer>

قد يبدو الدمج كالتالي:

$slot = {
  ...outerSlotData,
  ...middleSlotData,
  ...innerSlotData
}

قد تكون قلقًا / تتساءل عن كيفية التعامل مع تداخل مساحة الاسم وفي 99.9٪ من الحالات ، لا أعتقد حقًا أنها ستكون مشكلة. على سبيل المثال:

<UserData id="chrisvfritz">
  My email is {{ $slot.user.email }}, but you can reach Sarah at
  <UserData id="sdras">
    {{ $slot.user.email }}
  </UserData>.
</UserData>

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

<UserData id="chrisvfritz">
  <template slot-scope="me">
    <UserData id="sdras">
      <template slot-scope="sarah">
        <CollaborationPageLink :user-ids="[me.user.id, sarah.user.id]">
          View your collaboration history
        </CollaborationPageLink>
      </template>
    </UserData>
  </template>
</UserData>

بالنسبة إلى حالات الحافة النادرة هذه ، لا يزال بإمكان المستخدمين استخدام slot-scope بسلوكه الحالي باعتباره فتحة هروب . ولكن سيظل v-scope غير ضروري ، لأنني إذا فهمت بشكل صحيح ، فسيكون الغرض منه هو تبسيط حالات الاستخدام الأكثر شيوعًا ، والتي كان من الممكن أن ينجزها الكائن $slot بالفعل دون التسبب في نفس المشاكل.

مزايا

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

  • لمعرفة البيانات التي يمكنهم الوصول إليها ، يتعين على المستخدمين فقط التفكير في الفتحة (الفتحات) التي سيتم تقديم المحتوى إليها . أعتقد أنه مع v-scope ، سيكون هناك الكثير من الالتباس مع الأشخاص الذين يفترضون أنه يعمل مثل v-for ، لأن هذا هو التوجيه الوحيد الآخر الذي يحدد خصائص السياق المحددة النطاق.

  • لا يتعين على المستخدمين أن يكونوا مألوفين ومرتاحين في التدمير لاستخدام الفتحات المحددة النطاق ، وبالتالي تقليل منحنى التعلم.

  • في بعض الحالات ، خاصة مع العديد من الفتحات المتداخلة التي توفر جميعها حالة ، لن يكون من الواضح _ أي مكون أتت منه بعض الحالات وسيتعين على المستخدمين الوصول إلى slot-scope مرة أخرى. قد يبدو هذا وكأنه عيب ، ولكن عندما أرى فتحات ذات نطاق متداخل ، فإنها دائمًا ما تكون لنمط مزود الحالة ، والذي أشعر بشدة أنه مضاد للنمط. هذا هو المنطق الخاص فإن حقيقة أن العديد من الفتحات ذات النطاق المتداخلة تتطلب المزيد من النماذج المعيارية يمكن أن تقلل في الواقع من استخدام الأنماط المضادة .

  • تم تقليل مساحة سطح واجهة برمجة التطبيقات بشكل كبير ، لأن معظم مطوري Vue سيتعين عليهم فقط تذكر $slot ، وهو مجرد كائن.

  • باستخدام خاصية $ -prefixed ، فإننا نبني على واجهة برمجة تطبيقات فتحة مكون الويب بطريقة توضح أن $slot هو شيء Vue.

  • حاليًا ، غالبًا ما تفعل المكتبات شيئًا مثل <slot v-bind="user" /> حتى يتمكن مستخدموها من حفظ بضعة أحرف باستخدام slot-scope="user" بدلاً من slot-scope="{ user } . يبدو الأمر أنيقًا للوهلة الأولى ، لكنني جئت لتجربة نمط مضاد. تنشأ المشكلة عندما يريد المكوِّن عرض بيانات _ أخرى غير_ للمستخدم. ثم يكون لديهم خياران: إجراء تغيير فاصل لواجهة برمجة التطبيقات الخاصة بهم أو فرض هذه الخاصية الجديدة على الكائن user ، على الرغم من أنه قد لا يكون له علاقة بالمستخدم. لا خيار رائع. لحسن الحظ ، فإن $slot سيزيل الإغراء لجعل المكونات أقل مقاومة للمستقبل ، لأنه في حين أن $slot لا يزال أقصر من $slot.user ، فإنك تفقد السياق المهم عن طريق تسمية المستخدم باسم $slot .

سلبيات

  • في بعض الفتحات المحددة النطاق المتداخلة ، هناك بعض حالات الحافة حيث لن يكون واضحًا _ أي مكون أتت بعض البيانات منه. في هذه الحالات ، لا يزال بإمكان المستخدمين الوصول إلى slot-scope عندما يحتاجون إلى أقصى قدر من الوضوح لذلك لا أعتقد أنها مشكلة كبيرة. بالإضافة إلى ذلك ، كما ذكرت سابقًا ، أعتقد أنه من المحتمل جدًا أن يقوم المستخدم بإطلاق النار على نفسه بمكونات مزود الحالة إذا كان لديه هذه المشكلة في المقام الأول.

أعتقد أن الخاصية $ -prefixed يجب أن تكون متسقة داخل مثيل Vue بالكامل. لست متأكدًا مما إذا كان سيؤدي إلى حدوث ارتباك عن طريق الحقن الضمني لـ $slot في كل فتحة وهم في الواقع يمثلون وسيطة محلية داخل نطاقات الفتحات هذه. إنه نوع من الاصطلاح (وفقًا لملاحظتي) أن الخصائص $ -prefixed تمثل شيئًا متاحًا لكل مثيل Vue ، لذلك يمكن استخدامها في أي مكان ، بما في ذلك خطافات دورة الحياة والأساليب ووظائف العرض. ستؤدي إضافة هذا $slot إلى كسر هذا الاصطلاح.

chrisvfritz هذا اقتراح مثير للاهتمام ، لكنه يعتمد نوعًا ما على $slots أو $scopedSlots ؟ التلميح الوحيد هو وجود $slot في مكان ما في التعبيرات (يمكن أن يكون في أي مكان أسفل الشجرة) ، وهو ليس واضحًا مثل slot-scope (والذي يمكن أن يكون فقط في جذر الفتحة). على الرغم من أنه يمكننا من الناحية الفنية اكتشاف ذلك في المترجم ، بالنسبة للمستخدم ، ستنتقل الفتحة من $slots إلى $scopedSlots عندما يبدأ المستخدم في استخدام $slot في القالب ...

هل سيكون لهذا تأثير كبير على مستخدمي JSX؟

donnysim لا ، هذا لا يؤثر على JSX بأي شكل من الأشكال.

أعتقد أن الخاصية $ -prefixed يجب أن تكون متسقة داخل مثيل Vue بالكامل. لست متأكدًا مما إذا كان سيؤدي إلى حدوث ارتباك عن طريق الحقن الضمني لـ $slot في كل فتحة وهم في الواقع يمثلون وسيطة محلية داخل نطاقات الفتحات هذه.

Justineo كان لدي نفس الفكرة في البداية ، لكننا نقوم بذلك مقابل $event ولا يبدو أن أحدًا يشكو ، لذلك هناك سابقة _is_ بالفعل ولدينا دليل على أن المستخدمين عادةً لا يشعرون بالارتباك ويستمتعون حقًا بالراحة.

أكبر مشكلة في ذلك هي هل يجب أن يكون محتوى الفتحة متاحًا في المكون الفرعي $slots أو $scopedSlots ؟

@ yyx990803 سؤال عظيم! لدي فكرة قد تنجح. ماذا لو كانت غامضة (فتحات متداخلة تستخدم $slot ) ، نقوم فقط بتجميع جميع الفتحات إلى فتحة محددة النطاق ، ولكن أيضًا جعل كل $scopedSlots متاحًا تحت $slots كأحرف؟ على سبيل المثال ، شيء مثل:

for (const slotName in vm.$scopedSlots) {
  // Don't override existing slots of the same name,
  // since that's _technically_ possible right now.
  if (vm.$slots[slotName]) continue

  Object.defineProperty(vm.$slots, slotName, {
    get: vm.$scopedSlots[slotName],
    enumerable: true,
    configurable: true,
    writable: true
  })
}

بهذه الطريقة في حالة:

<A>
  <B>
    {{ $slot.foo }}
  </B>
</A>

يمكننا التحويل إلى شيء مثل:

// Assume new render helper:
// _r = mergeSlotData

_c('A', {
  scopedSlots: _u([
    {
      key: 'default',
      fn: function(_sd) {
        var $slot = _r($slot, _sd)
        return [
          _c('B', {
            scopedSlots: _u([
              {
                key: 'default',
                fn: function(_sd) {
                  var $slot = _r($slot, _sd)
                  return [_v(_s($slot.foo))]
                },
              },
            ]),
          }),
        ]
      },
    },
  ]),
}) 

ولا يهم ما إذا كان foo يأتي من <A> أو <B> ، لأنه داخل هذه المكونات سيعمل كلا من this.$slots.default و this.$scopedSlots.default(someData) .

زوجان من المحاذير:

  • في الحالات التي تحتوي على فتحات متداخلة وبعضها فقط محدد نطاقًا فعليًا ، فإن وظائف العرض المجمعة من القوالب التي تستخدم $slot ستكون أكبر قليلاً من استخدام slot-scope . ليس مهمًا جدًا ، لذلك أعتقد أنه جيد.

  • لا يمكنني التفكير في مثال حقيقي ، ولكن قد تكون هناك حالات متطرفة حيث يقوم المستخدم بتكرار ما يزيد عن $slots / $scopedSlots وامتلاك خصائص جديدة أو مكررة قد يؤدي إلى سلوك غير متوقع. لقد كررت الاسم بشكل ديناميكي $slots و $scopedSlots قبل لتمكين بعض الأنماط المثيرة للاهتمام ، لكن هذا التغيير لن يؤثر على أي من حالات الاستخدام التي واجهتني.

أفكار؟

نقوم بذلك مقابل $event ولا يبدو أن أحدًا يشكو

chrisvfritz أوه لقد فاتني الشيء $event . على الرغم من أنه من الواضح أنه استثناء من الاصطلاح (اعتدت أن أعتقد أنه 😅) ، إلا أن $event متاح فقط في نطاق محدود للغاية (فقط داخل السمة الحرفية v-bind وليس التداخل).

وبالنسبة إلى الوكلاء $scopedSlots على $slots :

اعتدت أن أعتقد أن الفتحات والفتحات ذات النطاق هي مفاهيم مختلفة وأن Vue لها استخدام منفصل لها ، مما يعني أنه يمكنني الحصول على فتحة وفتحة محددة النطاق تشترك في نفس الاسم ولكن تخدم أغراضًا مختلفة. لكنني اكتشفت لاحقًا أن Vue يتراجع فعليًا إلى الفتحة التي تحمل نفس الاسم عندما تكون الفتحة المحددة النطاق غير متاحة. بالنسبة لي ، هذا يعني أننا يجب أن نعتبرهم نفس الشيء ولكن نظرًا لأن لدينا $slots و $scopedSlots متاحان في كل حالة ، يمكننا دائمًا استخدامها في وظائف العرض بطريقة أكثر مرونة / غير متوقعة ، مثل استخدام فتحة افتراضية لتجاوز جميع عناصر القائمة وفتحة افتراضية محددة النطاق لتجاوز عنصر واحد. لا يتم تشجيع هذا في الواقع (لم نقترح الاستخدام الموصى به على هذا في مستنداتنا ودليل النمط AFAIK) حيث من المحتمل أن ندمجها في مفهوم واحد للفتحة في 3.0 ، ولكن القيام بذلك في 2.x سيؤدي إلى كسر يسمح ببعض الاستخدام منذ وقت طويل جدًا.

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

Justineo قد يكون لدي سوء فهم ، لكنني لا أقترح دمجهم في 2.x بالفعل - فقط تمديد السلوك الاحتياطي الحالي. ستظل حالة الاستخدام التي وصفتها ، حيث يستخدم كل من $slots و $scopedSlots نفس مساحة الاسم ، لأن سلوك slot-scope سيظل دون تغيير. على سبيل المثال:

<div>
  Default slot
  <template slot-scope="foo">
    Default scoped slot {{ foo }}
  </template>
</div>

ستظل تعمل بالضبط نفس الشيء. هل هذا منطقي؟

تضمين التغريدة

لم أكن أتحدث عن دمجهم في 2.x. كنت أقول إذا فعلت هذا:

جعل كل $scopedSlots متاحًا تحت $slots كأحرف

لا يمكنك تمييز $slots.foo من $scopedSlots.foo واستخدامهما بشكل منفصل في وظائف التقديم بعد الآن.

لا يمكنك التمييز بين $ slots.foo و $ scopedSlots.foo واستخدامهما بشكل منفصل في وظائف التصيير بعد الآن.

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

donnysim نعم أتفق معك في وجوب دمجها. لقد أوضحت سبب القلق لدي في https://github.com/vuejs/vue/issues/9180#issuecomment -447185512. يتعلق الأمر بالتوافق مع الإصدارات السابقة.

لا يمكنك التمييز بين $slots.foo $scopedSlots.foo واستخدامهما بشكل منفصل في وظائف العرض بعد الآن.

Justineo يمكنك $slots أولاً ولا نستبدل الفتحات المحددة بالفعل. انظر مثال التنفيذ الذي نشرته أعلاه:

for (const slotName in vm.$scopedSlots) {
  // Don't override existing slots of the same name,
  // since that's _technically_ possible right now.
  if (vm.$slots[slotName]) continue

  Object.defineProperty(vm.$slots, slotName, {
    get: vm.$scopedSlots[slotName],
    enumerable: true,
    configurable: true,
    writable: true
  })
}

تضمين التغريدة

خذ بعين الاعتبار المثال التالي:

render (h) {
  if (!this.$slots.default) {
    return h(
      'div',
      this.items.map(item => this.$scopedSlots.default(item))
    )
  }
  return h('div', this.$slots.default)
}

إذا قام مستخدم المكون بتمرير vm.$scopedSlots.default ، فيجب أن يمر عبر this.items ويعرض item s في الفتحة المحددة. ولكن الآن بعد أن أصبح الآن vm.$scopedSlot.default هو الفتحة المحددة النطاق وهي موجودة ، سيتم استدعاء الفتحة المحددة النطاق بدلاً من الفتحة.

Justineo أعتقد أن حالة الاستخدام هذه ستكون نادرة جدًا ، لكن النمط سيظل ممكنًا بالتغيير:

if (!this.$slots.default) {

إلى:

if (this.$scopedSlots.default) {

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

if (!Object.getOwnPropertyDescriptor(this.$slots, 'default').value) {

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

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

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

تضمين التغريدة

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

posva نشكرك على مشاركة تجربتك!

Justineo برأيك ، هل تشعر أن لتسهيل تحسين واجهة برمجة التطبيقات ذات الفتحة المحددة في Vue 2.x؟

تم نقل هذا إلى "Todo" - هل لدينا إجماع على ما يجب تنفيذه؟

لقد كنت أفكر في هذا الأسبوع الماضي. يعجبني الاقتراح المتغير لـ $slot وكنت أحاول العمل على أدوات حظر التنفيذ. هذا ما أعتقد أنه ممكن:

  • يتم تجميع جميع الفتحات كوظائف (كما هو الحال في 3.0) ، لذلك لا مزيد من الفتحات الثابتة (يؤدي هذا إلى تتبع تحديث أكثر دقة للمكونات)
  • سيعرض $scopedSlots كل فتحة كدالة
  • سيعرض $slots كل فتحة كنتيجة لاستدعاء الوظيفة المقابلة بكائن فارغ.

لذلك فإن ما يلي سيكون مكافئًا:

this.$slots.default
// would be the same as
this.$scopedSlots.default({} /* $slot */)

مع التغييرات الداخلية المذكورة أعلاه ، فإن $slots و $scopedSlots موحدان بشكل أساسي ولكن يجب أن يكونا متوافقين مع الإصدارات السابقة! يجب أن يؤدي أيضًا إلى تسهيل الترحيل إلى الإصدار 3.0.

بعض نماذج الاستخدام باستخدام المتغير $slot المقترح:

<!-- list and item composition -->
<fancy-list>
  <fancy-item :item="$slot.item"/>
</fancy-list>
<!-- hypothetical data fetching component -->
<fetch :url="`/api/posts/${id}`">
  <div slot="success">{{ $slot.data }}</div>
  <div slot="error">{{ $slot.error }}</div>
  <div slot="pending">pending...</div>
</fetch>

لا أعتقد أن دمج النطاقات المتداخلة فكرة جيدة. أعتقد أنه من الأفضل أن تكون واضحًا عند تضمين التداخل (على سبيل المثال ، استخدم دائمًا slot-scope إذا كان لديك فتحات متداخلة النطاق):

<!-- nested usage -->
<foo>
  <bar slot-scope="foo">
    <baz slot-scope="bar">
      {{ foo }} {{ bar }} {{ $slot }}
    </baz>
  </bar>
</foo>

في مثالك الأخير ، ما الخانة التي يشير إليها $slot ؟

Akryum إنه { ...foo, ...bar } .

AkryumJustineo هم أستطيع أن أقول هذا يمكن أن يكون مربكا ... جعلنا slot-scope صالحة للاستعمال على العنصر الجذر من فتحة، أي هنا foo هو نطاق فتحة التي تقدمها <foo/> ، bar مقدم من <bar/> و $slot مقدم من <baz/> ... الآن أعتقد أنه كان من الخطأ السماح بمثل هذا الاستخدام ، لأنه كلما كان الاستخدام الأكثر سهولة في هذه الحالة هو شيء من هذا القبيل:

<!-- nested usage -->
<foo slot-scope="foo">
  <bar slot-scope="bar">
    <baz slot-scope="baz">
      {{ foo }} {{ bar }} {{ baz }}
    </baz>
  </bar>
</foo>

قد يكون هذا محيرا

سيكون الاستخدام الأكثر سهولة في هذه الحالة شيئًا من هذا القبيل

أوافق تماما. أيضًا ، يمكننا الاستمرار في استخدام التدمير.
على سبيل المثال:
'' vue

<FieldValidation field="login" slot-scope="{ value, setValue, error }">
  <LoginInput
    :value="value"
    :error="error"
    @input="setValue"
  />
</FieldValidation>

<FieldValidation field="password" slot-scope="{ value, setValue, error }">
  <PasswordInput
    :value="value"
    :error="error"
    @input="setValue"
  />
</FieldValidation>

مغلق عبر 7988a554 و 5d52262f

ملخص:

  • دعم متغير $slot في جميع الفتحات. (يؤدي وجود $slot إلى تجميع الفتحة على أنها فتحة محددة النطاق).

  • يتم الآن عرض جميع الفتحات ، بما في ذلك الفتحات العادية ، على this.$scopedSlots كوظائف. هذا يعني بافتراض وجود this.$slots.default ، فإن this.$scopedSlots.default() سيعيد قيمته. يسمح هذا لمستخدمي وظيفة التصيير باستخدام this.$scopedSlot دائمًا ولم يعد قلقًا بشأن ما إذا كانت الفتحة التي يتم تمريرها محددة النطاق أم لا. يتوافق هذا أيضًا مع 3.0 حيث يتم عرض جميع الفتحات كوظائف (ولكن على this.$slots بدلاً من ذلك).

  • لا تغيير في استخدام slot-scope ، لا يوجد إدخال لتوجيه جديد. نريد أن نجعل تغييرات بناء الجملة ضئيلة وأن نترك الكسر المحتمل إلى 3.0.

@ yyx990803 هل يقوم $slot بدمج جميع نطاقات الفتحات الخارجية ، أو يشير فقط إلى أقرب فتحة؟

@ Justineo لا دمج ، فقط الأقرب.

بالنظر إلى استخدام فتحة النطاق في التطبيقات التي يمكنني الوصول إليها ، يبدو أن الاختصار $slot لن يكون قابلاً للاستخدام في الواقع في ما يزيد قليلاً عن نصف الحالات بدون سلوك دمج الفتحة المتداخلة. تكمن المشكلة في أن المكونات الأساسية تُستخدم عادةً بدلاً من عناصر HTML الأولية ، وغالبًا ما تحتوي هذه المكونات على فتحات خاصة بها. على سبيل المثال:

<MapMarkers :markers="cities">
  <BaseIcon name="map-marker">
    {{ $slot.marker.name }}
  </BaseIcon>
</MapMarkers>

في المثال أعلاه ، لا توجد سوى فتحة واحدة محددة النطاق ، لمكوِّن <MapMarkers> . يقبل <BaseIcon> فتحة غير محددة النطاق ، ولكن نظرًا لأنه يقبل فتحة على الإطلاق ، فإن $slot سيكون غير محدد أو كائنًا فارغًا بدون سلوك الدمج ، مما يضطر عامل إعادة البناء إلى clunkier:

<MapMarkers :markers="cities">
  <template slot-scope="{ marker }">
    <BaseIcon name="map-marker">
      {{ marker.name }}
    </BaseIcon>
  </template>
</MapMarkers>

إذن هذه هي مخاوفي:

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

  • قد تبدأ مكتبات واجهة المستخدم في الإفراط في استخدام الدعائم بـ v-html حيث تكون الفتحات أكثر ملاءمة ، استجابة لشكاوى المستخدمين حول عدم القدرة على استخدام $slot .

  • قد يبدأ المستخدمون في تجنب المكونات الأساسية في بعض الحالات ، للحفاظ على استخدام فتحة النطاق بشكل أكثر أناقة ، مما يؤدي إلى رمز أقل قابلية للصيانة.

@ yyx990803 هل $slot سيكون من الصعب معرفة أي بيانات فتحة المكون جاءت من؟ إذا كان الأمر كذلك ، فقد يكون من المفيد معرفة أن الحالات الوحيدة التي يمكنني فيها الوصول إلى التطبيقات المستخدمة في فتحات النطاق المتداخلة كانت لمكونات مزود الحالة ، والتي كما ذكرت سابقًا وجدت أنها مضادة للنمط. لذلك ، خاصةً مع خصائص الفتحات ذات الأسماء الجيدة ، أعتقد أن حالات الغموض الفعلي في حالات الاستخدام المشروعة ستكون نادرة جدًا. ومع سلوك الدمج التلقائي ، سيتمكن المستخدمون من تحديد ما إذا كانوا بحاجة إلى مزيد من الوضوح - وعندما يفعلون ذلك ، سيكون هذا هو ما سيوجد من أجله slot-scope .

كحل وسط ، إليك بديل محتمل: ماذا لو بدلاً من دمج فتحات النطاق المتداخلة ، كان لدينا $slot يشير إلى أقرب فتحة _ قدمت نطاقًا_؟ يمكن أن يؤدي ذلك إلى القضاء على الحالات الأكثر غموضًا ، مع الاستمرار في معالجة مخاوفي. أفكار؟

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

يشير $ slot إلى أقرب فتحة توفر نطاقًا

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


على المدى الطويل أعتقد أن أفضل حل هو تغيير دلالات slot-scope بحيث تصبح طريقة للاسم المستعار / التدمير $slot . لذلك سيبدو المثال الخاص بك كما يلي:

<MapMarkers :markers="cities" slot-scope="{ marker }">
  <BaseIcon name="map-marker">
    {{ marker.name }}
  </BaseIcon>
</MapMarkers>

هذا يلغي الحاجة إلى الدمج ، وهو واضح بشأن المتغيرات التي تأتي من أي مزود ، وليس الإسهاب المفرط. (أعتقد أن هذا ما سنفعله على الأرجح في 3.0)

النقطة المحرجة التي نواجهها الآن هي أننا سمحنا باستخدام slot-scope في غير القوالب ، وهذا يمنعنا الآن من استخدامه في المكون نفسه.

قد يؤدي تغيير دلالاتها في 3.0 أيضًا إلى الكثير من الارتباك وألم الهجرة.

ربما يمكننا تجنب مشكلة الوقت من خلال تقديم خاصية جديدة ، slot-alias ، والتي تفعل بالضبط ما تقوله - استعارة $slot لشيء آخر. بخلاف slot-scope ، يمكن استخدامه فقط على المكون نفسه أو حاوية فتحة القالب (هذا يعني أنه سيعمل تمامًا مع slot-scope عند استخدامه على <template> ) :

فتحات افتراضية متداخلة:

<MapMarkers :markers="cities" slot-alias="{ marker }">
  <BaseIcon name="map-marker">
    {{ marker.name }}
  </BaseIcon>
</MapMarkers>
<foo slot-alias="foo">
  <bar slot-alias="bar">
    <baz slot-alias="baz">
      {{ foo }} {{ bar }} {{ baz }}
    </baz>
  </bar>
</foo>

فتحات مسماة بدون تداخل:

الحالة الوحيدة التي تصبح فيها مطولة حتمًا ، تسمى الفتحات ذات التداخل:

<foo>
  <template slot="a" slot-alias="a">
     <bar slot-alias="b">
        {{ a }} {{ b }}
     </bar>
  </template>
</foo>

(يمكن أن يكون هذا في الواقع أقل تفصيلاً باستخدام كل من slot-scope و slot-alias ، لكنني أعتقد أن هذا قد يكون مربكًا إلى حد ما. أنا أؤيد إهمال slot-scope تمامًا)

<foo>
   <bar slot="a" slot-scope="a" slot-scope="b">
      {{ a }} {{ b }}
   </bar>
</foo>

أخيرًا ، يمكننا حتى إعطائها اختصارًا ، () (نظرًا لأن دعائم العرض في JSX عادةً ما تكون وظائف سهم تبدأ بـ () ):

<MapMarkers :markers="cities" ()="{ marker }">
  <BaseIcon name="map-marker">
    {{ marker.name }}
  </BaseIcon>
</MapMarkers>
<foo ()="foo">
  <bar ()="bar">
    <baz ()="baz">
      {{ foo }} {{ bar }} {{ baz }}
    </baz>
  </bar>
</foo>

قارن ما سبق بما يعادل JSX:

<foo>
  {foo => (
    <bar>
      {bar => (
        <baz>
          {baz => `${foo} ${bar} ${baz}`}
        </baz>
      )}
    </bar>
  )}
</foo>

الإغلاق لأن التصميم الآن مختلف تمامًا عما تم اقتراحه في الأصل. فتح موضوع جديد بدلا من ذلك.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات