Browse Source

Merge branch 'master' of github.com:alpinejs/alpine into no-listeners-on-clone

Austen Cameron 4 years ago
parent
commit
ca229fb7bb
10 changed files with 923 additions and 128 deletions
  1. 60 73
      README.ar.md
  2. 1 1
      README.de.md
  3. 3 3
      README.ko.md
  4. 13 8
      README.md
  5. 783 0
      README.zh-CN.md
  6. 16 15
      dist/alpine-ie11.js
  7. 15 14
      dist/alpine.js
  8. 1 1
      src/directives/for.js
  9. 14 13
      src/directives/on.js
  10. 17 0
      test/for.spec.js

+ 60 - 73
README.ar.md

@@ -6,13 +6,13 @@
 ![npm version](https://img.shields.io/npm/v/alpinejs)
 [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg?sanitize=true)](https://alpinejs.codewithhugo.com/chat/)
 
-توفر لك Alpine.js بنية تفاعلية (تصريحية) مثل أُطر العمل الشهيرة كـ Vue و React بكُلفة وجهد أقل بكثير.
+توفر لك Alpine.js بنية تفاعلية (تصريحية) مثل أُطر العمل الشهيرة Vue و React بكُلفة وجهد أقل بكثير.
 
 يمكنك الإحتفاظ بـ DOM والاستمرار في استخدامه، وإضافة الدّوال والوظائف له عند الحاجة.
 
 يشبه إلى حد ما [Tailwind](https://tailwindcss.com/) ولكن في الجافاسكربت.
 
-> ملاحظة: نشير إلى أن بُنية Alpine.js شبية جداً إلى [Vue](https://vuejs.org/) (أو [Angular](https://angularjs.org/)). أنا ممتن لهذه الأُطر بما قدموه في تطوير الويب.
+> ملاحظة: نشير إلى أن بُنية Alpine.js شبية جداً بـ [Vue](https://vuejs.org/) (أو [Angular](https://angularjs.org/)). أنا ممتن لهذه الأُطر بما قدموه في تطوير الويب.
 
 #### ملاحظة:
 
@@ -48,7 +48,7 @@
 
 سيقوم Alpine.js بتهيئة نفسه، سهلة أليس كذلك!
 
-في بيئات التطوير، نوصي باستعمال إصدار محدّد كما في الرابط، لتجنب حدوث مشاكل غير متوقعة أو تصادمها مع الإصدارات الحديثة. على سبيل المثال، لإستخدام الإصدار الأخير (2.8.0) يمكنك كتابة:
+في بيئات التطوير، نوصي باستعمال إصدار محدّد كما في الرابط، لتجنب حدوث مشاكل غير متوقعة أو تصادمها مع الإصدارات الحديثة. على سبيل المثال، لاستخدام الإصدار الأخير (2.8.0) يمكنك كتابة:
 
 <div dir="ltr">
 
@@ -92,7 +92,7 @@ import 'alpinejs'
     
 </div>
 
-لو تُلاحظ في السطور أعلاه كتبنا [module/nomodule pattern](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/) ، تسمح هذه الأنماط للمتصفحات الحديث تحميل مجموعة الوحدات بشكل تلقائي، بينما ستقوم IE11 والمتصفحات القديمة الأخرى تحميل وحدات IE11 تلقائياً. 
+لو تُلاحظ في السطور أعلاه كتبنا [module/nomodule pattern](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/) ، تسمح هذه الأنماط للمتصفحات الحديثة بتحميل مجموعة الوحدات بشكل تلقائي، بينما ستقوم IE11 والمتصفحات القديمة الأخرى تبحميل وحدات IE11 تلقائياً. 
 
 ## طريقة الاستخدام
 
@@ -170,13 +170,13 @@ import 'alpinejs'
 | [`x-show`](#x-show) | إظهار أو إزالة العناصر بناء على التعبيرات المنطقية <span dir="ltr">`display: none;`</span> صحيحة أو خاطئة. |
 | [`x-bind`](#x-bind) | يضبط قيمة السّمة (attribute) على حسب نتائج تعليمات الجافاسكربت. |
 | [`x-on`](#x-on) | يربط المُنصِت للأحداث (event listener) بالعنصر، يتم تنفيذ تعليمات الجافاسكربت عند التفاعل معه. |
-| [`x-model`](#x-model) | يضيف اتصال للبيانات ثنائي الإتجاه "two-way data binding" حيث أن القيم الذي يُدخلها المستخدم تتم مزامنتها مع قيمة بيانات العنصر للمكوّن. |
+| [`x-model`](#x-model) | يضيف اتصالا للبيانات ثنائي الإتجاه "two-way data binding" حيث أن القيم الذي يُدخلها المستخدم تتم مزامنتها مع قيمة بيانات العنصر للمكوّن. |
 | [`x-text`](#x-text) | يعمل بشكل مشابه لـ `x-bind`، ولكنه يقوم بتحديث النص المضمن `innerText` داخل العنصر. |
 | [`x-html`](#x-html) | يعمل بشكل مشابه لـ `x-bind`، ولكنه يقوم بتحديث النص المضمن `innerText` داخل العنصر. |
-| [`x-ref`](#x-ref) | طريقة بسيطة لإسترجاع عنصر داخل DOM موجود خارج المكوّن الخاص بك. |
-| [`x-if`](#x-if) | إزالة عنصر تماماً من DOM. يجب استخدامه داخل وَسم `<template>` |
+| [`x-ref`](#x-ref) | طريقة سهلة لاسترجاع عنصر داخل DOM موجود خارج المكوّن الخاص بك. |
+| [`x-if`](#x-if) | إزالة عنصر تماماً من الـDOM. يجب استخدامه داخل وَسم `<template>` |
 | [`x-for`](#x-for) | ينشئ فروع DOM جديدة لكل عنصر من عناصر المصفوفة. يجب استخدامه داخل وَسم `<template>` |
-| [`x-transition`](#x-transition) | توجيه لعمل تأثيرات إنتقالية في مراحل مختلفة على الأصناف (Classes) |
+| [`x-transition`](#x-transition) | توجيه لعمل تأثيرات انتقالية في مراحل مختلفة على الأصناف (Classes) |
 | [`x-spread`](#x-spread) | يسمح لك بربط الكائنات التي تحتوي على توجيهات Alpine بالعناصر، لتحسين إمكانية إعادة استخدامه بشكل أفضل. |
 | [`x-cloak`](#x-cloak) | تتم إزالة هذه السمة عند تهيئة Alpine. مفيد لإخفاء DOM المُهيأ مسبقًا. |
 
@@ -186,12 +186,11 @@ import 'alpinejs'
 | --: | --: |
 | <span dir="ltr">[`$el`](#el)</span> | ترجع فرع DOM الخاص بالمكوّن الأساسي (root component). |
 | <span dir="ltr">[`$refs`](#refs)</span> | يسترجع عناصر DOM المميّزة بـ `x-ref` داخل المكوّن. |
-| <span dir="ltr">[`$event`](#event)</span> | يرجع كائن الحدث "Event"  الأصلي داخل المُتنصت لأحداث المستخدم. |
-| <span dir="ltr">[`$dispatch`](#dispatch)</span> | ينشئ حدثاً مخصصا `CustomEvent` ويرسله داخلياً باستخدام <span dir="ltr">`.dispatchEvent()`</span>. |
+| <span dir="ltr">[`$event`](#event)</span> | يرجع كائن الحدث "Event"  الأصلي داخل المُنصت لأحداث المستخدم. |
+| <span dir="ltr">[`$dispatch`](#dispatch)</span> | ينشئ حدثاً مخصصا `CustomEvent` ويرسله داخلياً باستخدام `.dispatchEvent()`. |
 | <span dir="ltr">[`$nextTick`](#nexttick)</span> | بعد معالجة Alpine لتحديث DOM يتم تنفيذ تعليمات برمجية. |
 | <span dir="ltr">[`$watch`](#watch)</span> | يقوم باستدعاء callback تم تحديده مسبقاً عندما يتم تعديل خاصية المُراقب "watched" |
 
-
 ## الرعاة
 
 <img width="33%" src="https://refactoringui.nyc3.cdn.digitaloceanspaces.com/tailwind-logo.svg" alt="Tailwind CSS">
@@ -216,9 +215,9 @@ import 'alpinejs'
 
 **البُنية:** <span dir="ltr">`<div x-data="[object literal]">...</div>`</span>
 
-تعرّف `x-data` حقل/نطاق جديد للمكوّن، يخبر Alpine بتهيئة المكوّن الجديد  بكائن البيانات المعرّف والمحدّد مسبقاً.
+تعرّف `x-data` حقل/نطاق جديد للمكوّن، يخبر Alpine بتهيئة المكوّن الجديد بكائن البيانات المعرّف والمحدّد مسبقاً.
 
-مشابه لخاصية `data` بالمكونّات في إطار Vue.
+مشابه لخاصية `data` في المكونّات في إطار Vue.
 
 **استخراج التعابير المنطقية للمكوّن**
 
@@ -247,11 +246,7 @@ import 'alpinejs'
 </script>
 ```
 
-</div>
-
-
-> لمستخدمي مجمّع الوحدات (bundler): يرجى ملاحظة أن الدوال التي يصل إليها Alpine.js في النطاق العام (window). فلاستخدام x-data يجب أن تصرّحها إلى `window`. على سبيل المثال <span dir="ltr">`window.dropdown = function () {}`</span> (لأنه في Webpack ،Rollup ،Parcel وما إلى ذلك، الدّوال التي تكتبتها تكون بشكل إفتراضي ضِمن نطاق الوحدة "module" وليس في نطاق الصفحة `window`).
-
+> لمستخدمي مجمّع الوحدات (bundler): يرجى ملاحظة أن الدوال التي يصل إليها Alpine.js في النطاق العام (window). فلاستخدام x-data يجب أن تصرّحها إلى `window`. على سبيل المثال <span dir="ltr">`window.dropdown = function () {}`</span> (لأنه في Webpack ،Rollup ،Parcel وما إلى ذلك، الدّوال التي تكتبتها تكون بشكل افتراضي ضِمن نطاق الوحدة "module" وليس في نطاق الصفحة `window`).
 
 يمكنك أيضاً دمج عدة كائنات متعددة معاً باستخدام محلّل الكائنات (object destructuring).
 
@@ -273,7 +268,7 @@ import 'alpinejs'
 
 ينفّذ `x-init` تعليمات برمجية عند تشغيل وتهيئة إحدى المكوّنات.
 
-إذا أردت تنفيذ التعليمات البرمجية بعد أن يجري Alpine تحديثه على DOM (مُماثل لـ <span dir="ltr">`mounted()`</span> في Vue.js) يمكنك إرجاع callback من `x-init` وسيتم تشغيله بعدها:
+إذا أردت تنفيذ التعليمات البرمجية بعد أن يجري Alpine تحديثه على الـDOM (مُماثل لـ <span dir="ltr">`mounted()`</span> في Vue.js) يمكنك إرجاع callback من `x-init` وسيتم تشغيله بعدها:
 
 <span dir="ltr"> `x-init="() => { // يمكننا الوصول إلى DOM بعد تهيئته // }"` </span>
 
@@ -294,7 +289,7 @@ import 'alpinejs'
 
 ```html
 <div x-show.transition="open">
-    يتم عمل تأثير بصري بالظهور "in" و الإختفاء "out"
+    يتم عمل تأثير بصري بالظهور "in" و الاختفاء "out"
 </div>
 ```
 
@@ -314,7 +309,7 @@ import 'alpinejs'
 | `x-show.transition.in.duration.200ms.out.duration.50ms` | تأثيرات على فترات مختلفة "in" و "out". |
 
 
-> ملاحظة: يمكنك دمج جميع التاثيرات مع بعضها البعض (على الرغم من أنها غريبة ربما): `x-show.transition.in.duration.100ms.origin.top.right.opacity.scale.85.out.duration.200ms.origin.bottom.left.opacity.scale.95`
+> ملاحظة: يمكنك دمج جميع التأثيرات مع بعضها البعض (على الرغم من أنها غريبة ربما): `x-show.transition.in.duration.100ms.origin.top.right.opacity.scale.85.out.duration.200ms.origin.bottom.left.opacity.scale.95`
 
 > ملاحظة: سينتظر `x-show` إلى حين أن تنتهني جميع التأثيرات، إذا كنت تريد تجاهلها، أضف المعدّل <span dir="ltr">`.immediate`</span>
 
@@ -332,13 +327,13 @@ import 'alpinejs'
 
 ### `x-bind`
 
-> ملاحظة: يمكنك اختصار الكتابة باستعمال النقطتين ":" كـ <span dir="ltr">`:type="..."`</span>
+> ملاحظة: يمكنك اختصار الكتابة باستعمال النقطتين ":" مثل <span dir="ltr">`:type="..."`</span>
 
 **المثال:** <span dir="ltr">`<input x-bind:type="inputType">`</span>
 
 **البُنية:** <span dir="ltr">`<input x-bind:[attribute]="[expression]">`</span>
 
-يضبط قيمة السّمة (attribute) على حسب نتائج تعليمات الجافاسكربت. ويمكن لهذه التعليمات أن تصل الى جميع بيانات المكوّن. ويتم تحديثه في كل مرة يتم فيها تحديث بياناته.
+يضبط قيمة السّمة (attribute) على حسب نتائج تعليمات الجافاسكربت. ويمكن لهذه التعليمات أن تصل إلى جميع بيانات المكوّن. ويتم تحديثه في كل مرة يتم فيها تحديث بياناته.
 
 > ملاحظة: يتم تحديث ارتباطات السمات (binding) فقط إذا تم تحديث قيّمها. يكتشف Alpine تلقائيًا هذه القيم والتحديثات ثم يحسّنها.
 
@@ -346,9 +341,9 @@ import 'alpinejs'
 
 يتصرف `x-bind` بشكل مختلف قليلاً عند تحديد الصنف (class attribute).
 
-بالنسبة للأصناف (classes) قم بتمرير كائن يكون مفتاحه هو إسم الفئة، وقيم هذه الأزواج عبارة عن تعبيرات منطقية تحدّد ما إذا كان يتم تطبيق الصنف على العنصر أم لا.
+بالنسبة للأصناف (classes) قم بتمرير كائن يكون مفتاحه هو اسم الفئة، وقيَمُ هذه الأزواج عبارة عن تعبيرات منطقية تحدّد ما إذا كان يتم تطبيق الصنف على العنصر أم لا.
 
-كمثال:
+مثال:
 <span dir="ltr">`<div x-bind:class="{ 'hidden': foo }"></div>`</span>
 
 في هذا المثال يتم تطبيق الصنف "hidden" فقط عندما تكون قيمة foo صحيحة `true`.
@@ -357,37 +352,34 @@ import 'alpinejs'
 
 يدعم `x-bind` المتغيرات بالإضافة إلى تعبيرات الجافاسكربت في حالة إذا كانت تُرجع قيمة منطقية صحيحة أو خاطئة (`true` أو `false`).
 
-كمثال:
-
+مثال:
 <div dir="ltr">
-
 ```html
 <!-- العبارة: -->
 <button x-bind:disabled="myVar">إضغطني</button>
 
 <!-- إذا myVar == true: -->
-<button disabled="disabled">إضغطني</button>
+<button disabled="disabled">اضغطني</button>
 
 <!-- في حال myVar == false:  -->
-<button>Click me</button>
+<button>اضغطني</button>
 ```
-
 </div>
 
 هنا تتم إضافة السمة `disabled` أو إزالتها بناءً على قيمة المتغيّر `myVar`.
 
-تدعم كذلك Alpine سمات منطقية مختلفة  HTML specification كـ:  `disabled`,`readonly`,`required`,`checked`,`hidden`,`selected`,`open` وغيرها.
+تدعم كذلك Alpine سمات منطقية مختلفة  HTML specification مثل:  `disabled`,`readonly`,`required`,`checked`,`hidden`,`selected`,`open` وغيرها.
 
 المُعدّل .camel
 مثال: <svg x-bind:view-box.camel="viewBox">
 
-يقوم المعدّل بضبط وربط حالة الأحرف بصيغة camel case لإسم السمة. في المثال أعلاه، تم ربط قيمة viewBox بِسِمة viewBox (بدلاً من view-box).
+يقوم المعدّل بضبط وربط حالة الأحرف بصيغة camel case لاسم السمة. في المثال أعلاه، تم ربط قيمة viewBox بِسِمة viewBox (بدلاً من view-box).
 
 ---
 
 ### `x-on`
 
-> ملاحظة: يمكنك اختصار الكتابة باستعمال النقطتين ":" كـ <span dir="ltr">`@click="..."`</span>
+> ملاحظة: يمكنك اختصار الكتابة باستعمال النقطتين ":" مثل <span dir="ltr">`@click="..."`</span>
 
 **المثال:** <span dir="ltr">`<button x-on:click="foo = 'bar'"></button>`</span>
 
@@ -397,7 +389,7 @@ import 'alpinejs'
 
 يتم تحديث السِمات الأخرى للعنصر المرتبطة بمصدر البيانات هذا بمجرد تعديل البيانات الموجودة في التعليمات البرمجية.
 
-> ملاحظة: اختياريًا، يمكن أيضًا تحديد اسم دالة JavaScript.
+> ملاحظة: اختياريًا، يمكن أيضًا تحديد اسم دالة الجافاسكربت.
 
 **المثال:** <span dir="ltr">`<button x-on:click="myFunction"></button>`</span>
 
@@ -407,11 +399,11 @@ import 'alpinejs'
 
 **المثال:** <span dir="ltr">`<input type="text" x-on:keydown.escape="open = false">`</span>
 
-يمكنك الإستجابة لأحداث معينة ينقر عليها المستخدم في لوحة المفاتيح باستخدام المعدّلات `x-on:keydown`. يرجى ملاحظة أن هذا المُعدّل يستخدم صيغة kebab-case لتسمية قيم `Event.key`.
+يمكنك الإستجابة لأحداث معينة عند ضغط المستخدم في لوحة المفاتيح باستخدام المعدّلات `x-on:keydown`. يرجى ملاحظة أن هذا المُعدّل يستخدم صيغة kebab-case لتسمية قيم `Event.key`.
 
 أمثلة: `enter`, `escape`, `arrow-up`, `arrow-down`
 
-> يمكننا كذلك الإستجابة أزرار لوحة المفاتيح الأساسية كـ <span dir="ltr">`x-on:keydown.cmd.enter="foo"`</span>
+> يمكننا كذلك الإستجابة لأزرار لوحة المفاتيح الأساسية مثل <span dir="ltr">`x-on:keydown.cmd.enter="foo"`</span>
 
 **المُعدّل <span dir="ltr">`.away`</span>**
 
@@ -419,39 +411,39 @@ import 'alpinejs'
 
 لا يتم تنفيذ تعبير Event Handler إلا إذا لم يتم تشغيل الحدث بواسطة العنصر نفسه (أو مكوناته الفرعية).
 
-هذا مفيد لإخفاء القوائم المنسدلة والنوافذ عندما ينقر المستخدم في مكان آخر.
+هذا مفيد لإخفاء القوائم المنسدلة والنوافذ عندما يضغط المستخدم في مكان آخر.
 
 **المُعدّل <span dir="ltr">`.prevent`</span>**
 **المثال:** <span dir="ltr">`<input type="checkbox" x-on:click.prevent>`</span>
 
-تؤدي إضافة <span dir="ltr">`.prevent`</spam> إلى مستمع الحدث إلى استدعاء منع `preventDefault` في الحدث الذي سيتم تنفيذه. في المثال أعلاه، هذا يعني أن مربع الاختيار لن يتم تحديده بالفعل عندما ينقر المستخدم عليه.
+تؤدي إضافة <span dir="ltr">`.prevent`</span> داخل مستمع الحدث إلى استدعاء منع `preventDefault` في الحدث الذي سيتم تنفيذه. في المثال أعلاه، هذا يعني أن مربع الاختيار لن يتم تحديده بالفعل عندما ينقر المستخدم عليه.
 
 **المُعدّل <span dir="ltr">`.stop`</span>**
 **المثال::** <span dir="ltr">`<div x-on:click="foo = 'bar'"><button x-on:click.stop></button></div>`</span>
 
-إضافة <span dir="ltr">`.stop`</span> إلى المتصنّت للأحداث يستدعي `stopPropagation.` في المثال أعلاه،  يعني أن الحدث "click" لن ينتقل إلى  `<div>` الخارجي. بمعنى آخر، عندما ينقر المستخدم على الزر، لا يتم تعريف `foo` على أنه `bar`.
+إضافة <span dir="ltr">`.stop`</span> إلى المنصت للأحداث يستدعي `stopPropagation.` في المثال أعلاه، يعني أن الحدث "click" لن ينتقل إلى  `<div>` الخارجي. بمعنى آخر، عندما ينقر المستخدم على الزر، لا يتم تعريف `foo` على أنه `bar`.
 
 **المُعدّل <span dir="ltr">`.self`</span>**
 **المثال:** <span dir="ltr">`<div x-on:click.self="foo = 'bar'"><button></button></div>`</span>
 
-إضافة .self إلى المتصنّت للأحداث إلى تشغيل الحدث فقط إذا كان <span dir="ltr">`$event.target`</span> و نفسه العنصر. في المثال أعلاه، يعني أنه عند النقر على الزر "button" لن يتم تشغيل الحدث
+إضافة .self إلى المنصت للأحداث يأدي إلى تشغيل الحدث فقط إذا كان <span dir="ltr">`$event.target`</span> هو نفس العنصر. في المثال أعلاه، يعني أنه عند النقر على الزر "button" لن يتم تشغيل الحدث.
 
 **المُعدّل <span dir="ltr">`.window`</span>**
 **المثال:** <span dir="ltr">`<div x-on:resize.window="isOpen = window.outerWidth > 768 ? false : open"></div>`</span>
 
-إضافة <span dir="ltr">`.window`</span> إلى مستمع الأحداث، سيقوم بتثبيت المتصنّت للأحداث على نافذة المتصفّح كله `window` بدلاً من DOM الذي قمت بتصريحه أو تحديده. هذا مفيد لك في حال أردت ضبط أحد المكوّنات وتغييرها على حسب حجم (أبعاد) المتصفّح. في المثال أعلاه سيتم إغلاق النافذة أو القائمة المنسدلة إذا تجاوزت أبعاد المتصفح 768 بكسل، خلاف ذلك نُبقيه على حالته.
+إضافة <span dir="ltr">`.window`</span> إلى منصت الأحداث، سيقوم بتثبيت منصت للأحداث على نافذة المتصفّح كله `window` بدلاً من DOM الذي قمت بتصريحه أو تحديده. هذا مفيد لك في حال أردت ضبط أحد المكوّنات وتغييرها على حسب حجم (أبعاد) المتصفّح. في المثال أعلاه سيتم إغلاق النافذة أو القائمة المنسدلة إذا تجاوزت أبعاد المتصفح 768 بكسل، خلاف ذلك نُبقيه على حالته.
 
->ملاحظة: يمكنك أيضًا استخدام معدّل <span dir="ltr">`.document`</span> لإرفاق المتصنّت بدلا من `window`.
+>ملاحظة: يمكنك أيضًا استخدام معدّل `.document` لإرفاق المنصت بدلا من `window`.
 
 **المُعدّل <span dir="ltr">`.once`</span>**
 **المثال:** <span dir="ltr">`<button x-on:mouseenter.once="fetchSomething()"></button>`</span>
 
-تعني إضافة المعدّل <span dir="ltr">`.once`</span> إلى المتصنّت للحدث أن المنصت (listener) يعمل مرة واحدة فقط. هذا مفيد للمهام التي تريد القيام بها مرة واحدة فقط، مثل الجلب الجزئي لشفرات HTML أو ما شابه.
+تعني إضافة المعدّل <span dir="ltr">`.once`</span> إلى المنصت للحدث أن المنصت (listener) يعمل مرة واحدة فقط. هذا مفيد للمهام التي تريد القيام بها مرة واحدة فقط، مثل الجلب الجزئي لشفرات HTML أو ما شابه.
 
 **المُعدّل <span dir="ltr">`.passive`</span>**
 **المثال:** <span dir="ltr">`<button x-on:mousedown.passive="interactive = true"></button>`</span>
 
-إذا أضفنا <span dir="ltr">`.passive`</span> إلى المتصنّت للحدث، فإن هذا الرمز المميز سيعطل وظيفة <span dir="ltr">`preventDefault()`</span> ولن تعمل على أي حدث يتم تنفيذه. يمكن أن يساعدك أحياناً في تحسين أداء التمرير (scroll) على الأجهزة التي تعمل باللمس.
+إذا أضفنا <span dir="ltr">`.passive`</span> إلى المنصت للحدث، فإن هذا الرمز المميز سيعطل وظيفة `preventDefault()` ولن تعمل على أي حدث يتم تنفيذه. يمكن أن يساعدك أحياناً في تحسين أداء التمرير (scroll) على الأجهزة التي تعمل باللمس.
 
 **المُعدّل <span dir="ltr">`.debounce`</span>**
 **المثال:** <span dir="ltr">`<input x-on:input.debounce="fetchSomething()">`</span>
@@ -475,7 +467,7 @@ import 'alpinejs'
 **المُعدّل <span dir="ltr">`.camel`</span>**
 **المثال:** <span dir="ltr">`<input x-on:event-name.camel="doSomething()">`</span>
 
-يقوم المتصنّت للأحداث بالإنصات إلى الأحداث التي تحمل حالة أحرف بصيغة camelCase. في هذا المثال سيتم تنفيذها للعناصر التي تحمل اسم `eventName`.
+يقوم المنصت للأحداث بالإنصات إلى الأحداث التي تحمل حالة أحرف بصيغة camelCase. في هذا المثال سيتم تنفيذها للعناصر التي تحمل اسم `eventName`.
 
 ---
 
@@ -484,15 +476,15 @@ import 'alpinejs'
 
 **البُنية:** <span dir="ltr">`<input type="text" x-model="[data item]">`</span>
 
-يضيف `x-model` ربط بيانات ثنائي الإتجاه "two-way data binding" (أي أن ربط البيانات يكون في كلا الطرفين). بمعنى آخر، أن قيمة العنصر تتم مزامنتها مع قيمة بيانات عنصر للمكوّن.
+يضيف `x-model` ربط بيانات ثنائي الإتجاه "two-way data binding" (أي أن ربط البيانات يكون في كلا الطرفين). بمعنى آخر، أن قيمة العنصر تتم مزامنتها مع قيمة بيانات عنصر المكوّن.
 
 > يكتشف `x-model` تلقائياً التغييرات التي تطرأ على العناصر التالية:  text inputs ،checkboxes ،radio buttons ،textareas ،selects ،multiple selects.
-> يمكنك فهم كيف يعمل ذلك في الخفاء في "سيناريوهات" إطار عمل [how Vue would](https://vuejs.org/v2/guide/forms.html).
+> يمكنك فهم كيف يعمل ذلك في الخفاء في "سيناريوهات" إطار عمل [Vue](https://vuejs.org/v2/guide/forms.html).
 
 **المُعدّل <span dir="ltr">`.number`</span>**
 **المثال:** <span dir="ltr">`<input x-model.number="age">`</span>
 
-يقوم <span dir="ltr">`.number`</span> بتحويل القيمة المدخلة عبر `<input>` إلى رقم. في حال تعذّر تحليل القيمة المدخلة أنه رقم فعلاً سيُرجع القيمة الأصلية المدخلة.
+يقوم <span dir="ltr">`.number`</span> بتحويل القيمة المدخلة عبر `<input>` إلى رقم. في حال تعذّر تحليل القيمة المدخلة لرقم فعلاً سيُرجع القيمة الأصلية المدخلة.
 
 **المُعدّل <span dir="ltr">`.debounce`</span>**
 **المثال:** <span dir="ltr">`<input x-model.debounce="search">`</span>
@@ -528,7 +520,7 @@ import 'alpinejs'
 
 **البُنية:** <span dir="ltr">`<span x-html="[expression]"`</span>
 
-يعمل بشكل مشابه لـ `x-bind`، ولكنه يقوم بتحديث شفرة HTML المضمنة  `innerText` داخل العنصر. 
+يعمل بشكل مشابه لـ `x-bind`، ولكنه يقوم بتحديث شفرة HTML المضمنة `innerText` داخل العنصر. 
 
 > :warning: **في هذه الحالة نوصي بكتابة محتوى (شفرات نظيفة) ولا تسمح بمُدخلات المُستخدم (user-provided)** :warning:
 >
@@ -543,9 +535,9 @@ import 'alpinejs'
 
 توفر `x-ref` طريقة مفيدة لجلب عناصر DOM خارج المكون الخاص بك، عندما تقوم بتعيين `x-ref` للعنصر، سيكون متاحاً لجميع معالجات الأحداث داخل الكائن عن طريق استدعاء `$refs`. 
 
-بديل مفيد  في حال كان يجب استخدام الأمر `document.querySelector` في كثير من الأحيان للإشارة إلى العناصر.
+يوفر ذلك بديلا مفيدا في حال كان يجب استخدام الأمر `document.querySelector` في كثير من الأحيان للإشارة إلى العناصر.
 
-> يمكنك أيضا عمل ربط القيم المتغيّرة (الديناميكية) بـ <span dir="ltr">`<span :x-ref="item.id"></span>`</span>.
+> يمكنك أيضا ربط القيم المتغيّرة (الديناميكية) بـ <span dir="ltr">`<span :x-ref="item.id"></span>`</span>.
 
 ---
 
@@ -556,7 +548,7 @@ import 'alpinejs'
 
 إذا كانت وظيفة `x-show` (كما شرحناها سابقاً) غير كافية، فيمكن استخدام `x-if` بدلاً من ذلك لإزالة عنصر بالكامل من DOM.
 
-نظرًا لأن Alpine لا يحتوي على DOM افتراضي، يجب استخدام `x-if` مع الوسم `<template></template>`. بحيث يسمح لـ Alpine  البقاء مستقرًا والوصول إلى DOM الحقيقي.
+نظرًا لأن Alpine لا يحتوي على DOM افتراضي، يجب استخدام `x-if` مع الوسم `<template></template>`. بحيث يسمح لـ Alpine  بالبقاء مستقرًا والوصول إلى DOM الحقيقي.
 
 > ملاحظة: عند استخدام `x-if`، يجب أن يكون هناك عنصر جذر واحد (element root) على الأقل داخل `<template></template>`
 
@@ -575,12 +567,13 @@ import 'alpinejs'
 </template>
 ```
 
+> ملاحظة: الـ`:key` مفتاح اختياري، ومع ذلك، نوصى به.
 </div>
 
 
 > ملاحظة: هذا `:key` مفتاح اختياري ، ومع ذلك، نوصى به وبشدة.
 
-يُعدّ `x-for` مناسباً للحالات التي يلزم فيها انشاء عقدة DOM جديدة لكل عنصر داخل المصفوفة. مشابه لـ `v-for` في Vue، ولكن الإختلاف الوحيد هو أنه يجب وضعه في وسم `template` بدلاً من عنصر DOM عادي.
+يُعدّ `x-for` مناسباً للحالات التي يلزم فيها إنشاء عقدة DOM جديدة لكل عنصر داخل المصفوفة. مشابه لـ `v-for` في Vue، ولكن الاختلاف الوحيد هو أنه يجب وضعه في وسم `template` بدلاً من عنصر DOM عادي.
 
 إذا كنت ترغب في الوصول إلى الفهرس الحالي (current index) للتكرار، فاستخدم الصيغة التالية:
 
@@ -602,7 +595,7 @@ import 'alpinejs'
     <div x-text="item"></div>
     <!--نفس العنصر المذكور أعلاه. -->
     <div x-text="collection[index]"></div>
-    <!-- لعنصر السابق. -->
+    <!-- العنصر السابق. -->
     <div x-text="collection[index - 1]"></div>
 </template>
 ```
@@ -614,7 +607,7 @@ import 'alpinejs'
 > ملاحظة: عند استخدام template في svg، ستحتاج إلى إضافة [polyfill](https://github.com/alpinejs/alpine/issues/637#issuecomment-654856538) الذي يجب تنفيذه قبل تهيئة Alpine.js. 
 
 #### تداخل `x-for`
-يمكنك عمل حلقات تكرار داخل حلقات x-for ولكن يجب أن نلّف (wrap) كل دورة في عنصر. مثلا:
+يمكنك عمل حلقات تكرار داخل حلقات x-for ولكن يجب أن نلّف كل حلقة في عنصر. مثلا:
 
 <div dir="ltr">
 
@@ -684,7 +677,7 @@ import 'alpinejs'
 
 > المثال أعلاه يستخدم تنسيقات [Tailwind CSS](https://tailwindcss.com/)
 
-يوفر Alpine ستة تأثيرات إنتقالية مختلفة لتطبيق الفئات (Classes) على مراحل مختلفة من انتقال العنصر، بين الحالات "hidden" و "shown". تعمل هذه التوجيهات مع كل من `x-show` و `x-if`.
+يوفر Alpine ستة تأثيرات انتقالية مختلفة لتطبيق الفئات (Classes) على مراحل مختلفة من انتقال العنصر، بين الحالات "hidden" و "shown". تعمل هذه التوجيهات مع كل من x-show و x-if.
 
 تعمل هذه تمامًا مثل توجيهات التأثير بـ VueJS، باستثناء أن لها أسماء مختلفة وأكثر منطقية:
 
@@ -738,11 +731,11 @@ import 'alpinejs'
 
 يتيح `x-spread` نقل ربط الكائنات التي تحتوي على توجيهات Alpine لعنصر ما، إلى كائن يمكن إعادة استخدامه.
 
-مفاتيح الكائن (object keys) عبارة عن توجيهات (يمكن أن يكون أي توجيهات بما في ذلك المُعدّلات) والقيم عبرة عن عمليات callback يتم تنفيذ قيمها بواسطة Alpine.
+مفاتيح الكائن (object keys) عبارة عن توجيهات (يمكن أن يكون أي توجيهات بما في ذلك المُعدّلات) والقيم عبارة عن عمليات callback يتم تنفيذ قيمها بواسطة Alpine.
 
 > ملاحظة: هناك بعض الأشياء التي يجب مراعاتها في `x-spread`:
-> - الحالة الخاصة الوحيدة لـ spread هي استخدامه مع `x-for`. عند تطبيقه على التوجيه `x-for`، يجب إرجاع تعليمات نصيّة (string) عادية بواسطة callback.  كمثال <span dir="ltr">`['x-for']() { return 'item in items' }`</span>
-> - `x-data` و  `x-init` لا يمكن إستخدامهما داخل كائن "spread".
+> - الحالة الخاصة الوحيدة لـ spread هي استخدامه مع `x-for`. عند تطبيقه على التوجيه `x-for`، يجب إرجاع تعليمات نصيّة عادية بواسطة callback.  مثال span dir="ltr">`['x-for']() { return 'item in items' }`</span>
+> - `x-data` و  `x-init` لا يمكن استخدامهما داخل كائن "spread".
 
 ---
 
@@ -764,7 +757,7 @@ import 'alpinejs'
 
 ### الخصائص السحرية
 
-> باستثناء <span dir="ltr">`$el`</span>، **لا يمكن استخدام الخصائص السحرية ضمن x-data** في حال المكوّن لم تتم تهيئته بعد.
+> باستثناء <span dir="ltr">$el</span>، **لا يمكن استخدام الخصائص السحرية ضمن x-data** في حال لم تتم تهيئة المكوّن بعد.
 
 ---
 
@@ -811,10 +804,9 @@ import 'alpinejs'
 <input x-on:input="alert($event.target.value)">
 ```
 
+يرجع كائن الحدث "Event"  الأصلي داخل المُنصت لأحداث المستخدم.
 </div>
 
-يرجع كائن الحدث "Event"  الأصلي داخل المُتنصت لأحداث المستخدم.
-
 > ملاحظة: خاصية $event متاحة فقط في تعليمات DOM.
 
 إذا أردت تمرير $event داخل دوال الجافاسكربت فيمكنك تمريرها مباشرة.
@@ -869,11 +861,9 @@ import 'alpinejs'
 <div>
 ```
 
+> ملاحظة: المثال أعلاه لن يعمل بشكل جيّد. لأنه إذا تم تشغيل الحدث المخصص، فسيتم انتشار "تشغيله" في العناصر الرئيسة المشتركة بـ div.
 </div>
 
-
-> ملاحظة: المثال أعلاه لن يعمل بشكل جيّد. لأنه إذا تم تشغيل الحدث المخصص، فسيتم انتشاره "تشغيله" في العناصر الرئيسية المشتركة بـ div.
-
 **Dispatching to Components**
 
 يمكن أيضًا استخدام الطريقة الموضحة أعلاه لتمكين الاتصال بين المكونات:
@@ -950,19 +940,16 @@ import 'alpinejs'
 </div>
 ```
 
-</div>
-
-
-تمكّنك <span dir="ltr">`$watch`</span> في المثال أعلاه أنه عند الضغط فوق الزر "button" ويتم تفعليه `open`، يقوم callback بتنفيذ `console.log` وطباعة القيمة الجديدة.
+تمكّنك <span dir="ltr">`$watch`</span> في المثال أعلاه من تفعيل `open` عند الضغط على الزر "button"، يقوم callback بتنفيذ `console.log` وطباعة القيمة الجديدة.
 
 ## الحماية
 إذا وجدت ثغرة أمنية، يُرجى إرسال بريد إلكتروني إلى: [calebporzio@gmail.com]().
 
-يعتمد Alpine على `function` لتنفيذ خصائصه. على الرغم من كونها أنها أكثرَ أماناً من <span dir="ltr">`eval()`</span> إلا أن هذه الممارسة مازالت محظورة في بعض البيئات المقيّدة مثل Google Chrome App باستخدام سياسة أمان المحتوى المقيد ([CSP](https://csp.withgoogle.com/docs/strict-csp.html)).
+يعتمد Alpine على `function` لتنفيذ خصائصه. على الرغم من كونها أكثرَ أماناً من <span dir="ltr">`eval()`</span> إلا أن هذه الممارسة مازالت محظورة في بعض البيئات المقيّدة مثل Google Chrome App باستخدام سياسة أمان المحتوى المقيد ([CSP](https://csp.withgoogle.com/docs/strict-csp.html)).
 
 إذا كنت تستخدم Alpine في بيئة بها بيانات حساسة وتحتاج إلى [CSP](https://csp.withgoogle.com/docs/strict-csp.html)، فأنت بحاجة إلى تضمين التقييم غير الآمن `unsafe-eval` في سياستك. يساعد في وضع سياسات قوية على حماية المستخدمين عند استخدام المعلومات الشخصية والمالية.
 
-نظرًا لأن السياسة تنطبق على جميع البرامج النصية في صفحتك، فمن المهم أن تتم مراجعة المكتبات الخارجية الأخرى المضمنة في موقعك بعناية للتأكد من أنها جديرة لإستعمالها وآمنة ولن تقدم أي ثغرة أمنية XSS أو (Cross Site Scripting vulnerability)  عبر الموقع سواء باستخدام وظيفة <span dir="ltr">`eval()`</span> أو التلاعب بـ DOM لإدخال تعليمات برمجية ضارة في صفحتك.
+نظرًا لأن السياسة تنطبق على جميع البرامج النصية في صفحتك، فمن المهم أن تتم مراجعة المكتبات الخارجية الأخرى المضمنة في موقعك بعناية للتأكد من أنها جديرة لاستعمالها وآمنة ولن تقدم أي ثغرة أمنية XSS أو (Cross Site Scripting vulnerability)  عبر الموقع سواء باستخدام وظيفة <span dir="ltr">`eval()`</span> أو التلاعب بـ DOM لإدخال تعليمات برمجية ضارة في صفحتك.
 
 ## الرخصة
 
@@ -970,4 +957,4 @@ import 'alpinejs'
 
 مرخص بموجب ترخيص MIT، راجع [LICENSE.md](LICENSE.md) للحصول على التفاصيل.
 
-</div>
+</div>

+ 1 - 1
README.de.md

@@ -161,7 +161,7 @@ Und 6 magische Eigenschaften (englisch *magic properties*):
 
 **Struktur:** `<div x-data="[JSON data object]">...</div>`
 
-`x-data` deklariert einen neuene Komponenten-Geltungsbereich. Jede neu erstellte Komponente wird nun mit der angegebenen Datenquelle initialisiert.
+`x-data` deklariert einen neuen Komponenten-Geltungsbereich. Jede neu erstellte Komponente wird nun mit der angegebenen Datenquelle initialisiert.
 
 Dies verhält sich ähnlich wie die `data`-Eigenschaft einer Vue-Komponente.
 

+ 3 - 3
README.ko.md

@@ -98,16 +98,16 @@ import 'alpinejs'
 
 | 지침 | 설명 |
 | --- | --- |
-| [`x-data`](#x-data) | 새로운 구성요소의 위를 선언합니다. |
+| [`x-data`](#x-data) | 새로운 구성요소의 위를 선언합니다. |
 | [`x-init`](#x-init) | 구성요소가 초기화될 때 제공된 식을 실행합니다. |
-| [`x-show`](#x-show) | `display: none;` 부울 표현식에 따라 요소를 토글니다. (true 또는 false). |
+| [`x-show`](#x-show) | `display: none;` 부울 표현식에 따라 요소를 토글니다. (true 또는 false). |
 | [`x-bind`](#x-bind) | 전달 된 JS 표현식의 결과와 동일한 속성 값을 설정합니다. |
 | [`x-on`](#x-on) | 요소에 이벤트 리스너를 설치합니다. 이벤트가 발생하면 제공된 JS 표현식을 실행합니다. |
 | [`x-model`](#x-model) | 지시문은 입력 요소와의 데이터 바인딩을 보장합니다. 이를 통해 양방향으로 데이터 바인딩이 가능합니다. |
 | [`x-text`](#x-text) | 유사한 방식으로 작동 `x-bind`의 `innerText` 요소가 업데이트됩니다. |
 | [`x-html`](#x-html) | 유사한 방식으로 작동 `x-bind`의 `innerHTML` 요소가 업데이트됩니다. |
 | [`x-ref`](#x-ref) | 구성 요소의 DOM 요소를 가져오는 편리한 방법입니다. |
-| [`x-if`](#x-if) | 전달된 조건이 충족되지 않으면 DOM에서 요소를 완전히 제거합니다. `<template>` 태그에 사용되어야 합니다
+| [`x-if`](#x-if) | 전달된 조건이 충족되지 않으면 DOM에서 요소를 완전히 제거합니다. `<template>` 태그에 사용되어야 합니다.
 | [`x-for`](#x-for) | 배열의 각 항목에 대해 새 DOM 노드를 만듭니다. `<template>`태그에 사용해야합니다. |
 | [`x-transition`](#x-transition) | 요소 전환의 다양한 단계에 클래스를 추가하기 위한 지시. |
 | [`x-spread`](#x-spread) | Alpine 지시문이 있는 개체를 요소에 바인딩하여 재사용성을 높일 수 있습니다. |

+ 13 - 8
README.md

@@ -17,6 +17,7 @@ Think of it like [Tailwind](https://tailwindcss.com/) for JavaScript.
 | Language | Link for documentation |
 | --- | --- |
 | Arabic | [**التوثيق باللغة العربية**](./README.ar.md) |
+| Chinese Simplified | [**简体中文文档**](./README.zh-CN.md) |
 | Chinese Traditional | [**繁體中文說明文件**](./README.zh-TW.md) |
 | German | [**Dokumentation in Deutsch**](./README.de.md) |
 | Indonesian | [**Dokumentasi Bahasa Indonesia**](./README.id.md) |
@@ -502,13 +503,15 @@ If you want to access the array object (collection) of the iteration, use the fo
 
 ```html
 <template x-for="(item, index, collection) in items" :key="index">
-    <!-- You can also reference "collection" inside the iteration if you need. -->
-    <!-- Current item. -->
-    <div x-text="item"></div>
-    <!-- Same as above. -->
-    <div x-text="collection[index]"></div>
-    <!-- Previous item. -->
-    <div x-text="collection[index - 1]"></div>
+    <div>
+        <!-- You can also reference "collection" inside the iteration if you need. -->
+        <!-- Current item. -->
+        <div x-text="item"></div>
+        <!-- Same as above. -->
+        <div x-text="collection[index]"></div>
+        <!-- Previous item. -->
+        <div x-text="collection[index - 1]"></div>
+    </div>
 </template>
 ```
 
@@ -633,7 +636,9 @@ The object keys are the directives (Can be any directive including modifiers), a
 
 ```html
 <style>
-    [x-cloak] { display: none; }
+    [x-cloak] {
+        display: none !important;
+    }
 </style>
 ```
 

+ 783 - 0
README.zh-CN.md

@@ -0,0 +1,783 @@
+# Alpine.js
+
+![npm bundle size](https://img.shields.io/bundlephobia/minzip/alpinejs)
+![npm version](https://img.shields.io/npm/v/alpinejs)
+[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg?sanitize=true)](https://alpinejs.codewithhugo.com/chat/)
+
+Alpine.js 通过很低的成本提供了于 Vue 或 React 这类大型框架相近的响应式和声明式特性。
+
+你可以继续操作 DOM,并在需要的时候使用 Alpine.js。
+
+可以理解为 JavaScript 版本的 [Tailwind](https://tailwindcss.com/)。
+
+> 备注:Alpine.js 的语法几乎完全借用自 [Vue](https://vuejs.org/) (并用 [Angular](https://angularjs.org/) 的语法做了些扩展)。在此由衷感谢他们对 Web 世界的贡献。
+
+## 安装 Install
+
+**使用 CDN:** 把以下脚本加入到你的 `<head>` 尾部.
+```html
+<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>
+```
+
+就是这样,Alpine.js 会自行初始化。
+
+生产环境中,建议在链接中锁定特定版本号,以此避免新版本中的变更造成问题。
+例如,锁定版本为 `2.8.0` (最新版本):
+```html
+<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.8.0/dist/alpine.min.js" defer></script>
+```
+
+**使用 npm:** 从 npm 安装依赖包。
+```js
+npm i alpinejs
+```
+
+并在你的脚本中引入它。
+```js
+import 'alpinejs'
+```
+
+**需要 IE11 支持的场景** 改用这段脚本。
+```html
+<script type="module" src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js"></script>
+<script nomodule src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine-ie11.min.js" defer></script>
+```
+
+这一写法使用了 [module/nomodule 模式(英文)](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/) ,这样的写法可以让现代浏览器自动加载模块版本依赖,而在 IE11 或其他早期浏览器中自动加载专属兼容版本。
+
+## 使用 Use
+
+*下拉菜单(Dropdown)/模态弹窗(Modal)*
+```html
+<div x-data="{ open: false }">
+    <button @click="open = true">Open Dropdown</button>
+
+    <ul
+        x-show="open"
+        @click.away="open = false"
+    >
+        Dropdown Body
+    </ul>
+</div>
+```
+
+*标签页 Tabs*
+```html
+<div x-data="{ tab: 'foo' }">
+    <button :class="{ 'active': tab === 'foo' }" @click="tab = 'foo'">Foo</button>
+    <button :class="{ 'active': tab === 'bar' }" @click="tab = 'bar'">Bar</button>
+
+    <div x-show="tab === 'foo'">Tab Foo</div>
+    <div x-show="tab === 'bar'">Tab Bar</div>
+</div>
+```
+
+这样的写法也可以用在其他地方:
+*鼠标悬停时从服务器预加载下拉菜单中的 HTML 内容。*
+```html
+<div x-data="{ open: false }">
+    <button
+        @mouseenter.once="
+            fetch('/dropdown-partial.html')
+                .then(response => response.text())
+                .then(html => { $refs.dropdown.innerHTML = html })
+        "
+        @click="open = true"
+    >Show Dropdown</button>
+
+    <div x-ref="dropdown" x-show="open" @click.away="open = false">
+        Loading Spinner...
+    </div>
+</div>
+```
+
+## 学习 Learn
+
+当前共有 14 个指令可用,如下所示:
+
+| 指令 | 描述 |
+| --- | --- |
+| [`x-data`](#x-data) | 定义一个新的组件作用域。 |
+| [`x-init`](#x-init) | 组件初始化时运行其中的表达式。 |
+| [`x-show`](#x-show) | 根据表达式结果(true 或 false)控制元素的 `display: none;`(译者注:控制模块显示/隐藏) |
+| [`x-bind`](#x-bind) | 将当前属性的值设定为指令中表达式的结果。 |
+| [`x-on`](#x-on) | 向元素上挂载事件监听器。当事件触发时执行其中的表达式。 |
+| [`x-model`](#x-model) | 向当前元素新增「双向数据绑定」。保持输入元素与组件数据同步。 |
+| [`x-text`](#x-text) | 和 `x-bind` 类似,但更新的是元素的 `innerText`。 |
+| [`x-html`](#x-html) | 和 `x-bind` 类似,但更新的是元素的 `innerHTML`。 |
+| [`x-ref`](#x-ref) | 在组件外获取原始 DOM 元素的简便方法。 |
+| [`x-if`](#x-if) | 值为 false 时将从 DOM 中完全移除元素。需要在 `<template>` 标签中使用。 |
+| [`x-for`](#x-for) | 为数组中的每一项创建一个新的 DOM 节点。需要在 `<template>` 标签中使用。 |
+| [`x-transition`](#x-transition) | 用于在过渡(CSS Translation)的各个阶段中为元素添加 class 的指令。 |
+| [`x-spread`](#x-spread) | 为了更好的复用,可以绑定一个带有 Alpine 指令(作为 key)的对象到元素上。 |
+| [`x-cloak`](#x-cloak) | 这一属性会在 Alpine 初始化完成后被移除,可以用来隐藏未初始化的 DOM。 |
+
+以及 6 个魔法属性:
+
+| 魔法属性 | 描述 |
+| --- | --- |
+| [`$el`](#el) | 获取根元素的 DOM 节点。 |
+| [`$refs`](#refs) | 获取组件中标记有 `x-ref` 的 DOM 元素。 |
+| [`$event`](#event) | 在事件监听器中获取浏览器原生的「Event」对象。 |
+| [`$dispatch`](#dispatch) | 创建一个「CustomEvent」并使用其内部的 `.dispatchEvent()` 方法进行分发。 |
+| [`$nextTick`](#nexttick) | 在 Alpine 做出响应式 DOM 更新后,执行一个给出的表达式。 |
+| [`$watch`](#watch) | 当监听的组件属性发生变化时,触发给定的回调函数。 |
+
+
+## 赞助商 Sponsors
+
+<img width="33%" src="https://refactoringui.nyc3.cdn.digitaloceanspaces.com/tailwind-logo.svg" alt="Tailwind CSS">
+
+**想让你的 Logo 也出现在这? [到 Twitter 发送 DM](https://twitter.com/calebporzio)**
+
+## 社区相关项目 Community Projects
+
+* [AlpineJS 周报(英文)](https://alpinejs.codewithhugo.com/newsletter/)
+* [Spruce (状态管理框架/英文)](https://github.com/ryangjchandler/spruce)
+* [Turbolinks Adapter(英文)](https://github.com/SimoTod/alpine-turbolinks-adapter)
+* [Alpine Magic Helpers(英文)](https://github.com/KevinBatdorf/alpine-magic-helpers)
+* [Awesome Alpine(英文)](https://github.com/ryangjchandler/awesome-alpine)
+
+### 指令 Directives
+
+---
+
+### `x-data`
+
+**例如:** `<div x-data="{ foo: 'bar' }">...</div>`
+
+**结构:** `<div x-data="[可迭代对象]">...</div>`
+
+`x-data` 将定义一个新的组件作用域。它将通知框架初始化带有传入数据的一个新组件。
+
+类似 Vue 组件中的 data 属性。
+
+**抽离组件逻辑**
+
+你可以把数据(以及行为)通过一个可复用的函数抽离出来:
+
+```html
+<div x-data="dropdown()">
+    <button x-on:click="open">Open</button>
+
+    <div x-show="isOpen()" x-on:click.away="close">
+        // Dropdown
+    </div>
+</div>
+
+<script>
+    function dropdown() {
+        return {
+            show: false,
+            open() { this.show = true },
+            close() { this.show = false },
+            isOpen() { return this.show === true },
+        }
+    }
+</script>
+```
+
+> **各位 Bundler 使用者**,注意 Alpine.js 操作的函数都在全局作用域(`window`),你需要把 `x-data` 使用的函数声明到 `window` 上,例如 `window.dropdown = function () {}` (因为在 Webpack, Rollup, Parcel 等工具中,`function` 默认会被挂载模块作用域,而非 `window`).
+
+你也可以通过对象解构语法,把多个数据对象混合起来:
+
+```html
+<div x-data="{...dropdown(), ...tabs()}">
+```
+
+---
+
+### `x-init`
+**例如:** `<div x-data="{ foo: 'bar' }" x-init="foo = 'baz'"></div>`
+
+**结构:** `<div x-data="..." x-init="[表达式]"></div>`
+
+`x-init` 将在组件初始化时运行给定的表达式。
+
+如果你希望代码在 Alpine 对 DOM 进行初始化更新后再调用(类似 VueJS 中的 `mounted()` 钩子),你可以传入一个回调函数,它将在 DOM 操作完成后被调用,例如:
+
+`x-init="() => { // we have access to the post-dom-initialization state here // }"`
+
+---
+
+### `x-show`
+**例如:** `<div x-show="open"></div>`
+
+**结构:** `<div x-show="[表达式]"></div>`
+
+`x-show` 将根据表达式结果(true 或 false)控制元素的 `display: none;`(译者注:控制模块显示/隐藏)
+
+**x-show.transition**
+
+`x-show.transition` 是一个可以让你在使用 `x-show` 操作 CSS 过渡(Transition)时更爽的简便 API。
+
+```html
+<div x-show.transition="open">
+    这段内容会在过渡中滑入/滑出。
+</div>
+```
+
+| 指令 | 描述 |
+| --- | --- |
+| `x-show.transition` | 同时滑入/滑出并缩放(默认值:opacity, scale: 0.95, timing-function: cubic-bezier(0.4, 0.0, 0.2, 1), duration-in: 150ms, duration-out: 75ms)
+| `x-show.transition.in` | 仅滑入 |
+| `x-show.transition.out` | 仅滑出 |
+| `x-show.transition.opacity` | 仅使用淡入淡出 |
+| `x-show.transition.scale` | 仅使用缩放 |
+| `x-show.transition.scale.75` | 自定义 CSS 缩放转换为 `transform: scale(.75)`. |
+| `x-show.transition.duration.200ms` | 设定 "in" 的的过渡时间为 200ms。出动作("out")将是它的一半 (100ms). |
+| `x-show.transition.origin.top.right` | 自定义 CSS 转换起始位置为 `transform-origin: top right`. |
+| `x-show.transition.in.duration.200ms.out.duration.50ms` | 为 "in" 和 "out" 设定不同的过渡时间。 |
+
+> 备注: 所有的过渡修饰符都可组合使用。也就是说可以这么用(虽然真这么用过于粗暴23333): `x-show.transition.in.duration.100ms.origin.top.right.opacity.scale.85.out.duration.200ms.origin.bottom.left.opacity.scale.95`
+
+> 备注: `x-show` 将等待子元素完成 out。如果你希望跳过这一行为,可以添加 `.immediate` 修饰符:
+```html
+<div x-show.immediate="open">
+    <div x-show.transition="open">
+</div>
+```
+---
+
+### `x-bind`
+
+> 备注:可以用 ":" 简写语法替代,例如: `:type="..."`.
+
+**例如:** `<input x-bind:type="inputType">`
+
+**结构:** `<input x-bind:[HTML属性]="[表达式]">`
+
+`x-bind` 将当前属性的值设定为指令中表达式的结果。 这一表达式可以访问组件数据对象中的所有 key,并在每次数据更新时重算结果。
+
+> 备注: 属性数据绑定只会在依赖的值更新时被重算。框架足够智能,可以观测数据变化并检测绑定是否关心这些变化。
+
+**`x-bind` 到 class 属性**
+
+`x-bind` 在完成到 `class` 属性的绑定时,行为略有不同。
+
+对于 class,你可以传入一个对象,key 为 class 名,值为布尔量,用来表示对应 class 是否应该生效。
+
+例如:
+`<div x-bind:class="{ 'hidden': foo }"></div>`
+
+在这一例子中,"hidden" 这一 class 将只会在 foo 数据属性为 `true` 时有效。
+
+**`x-bind` 到布尔值属性**
+
+`x-bind` 支持布尔值属性,与其他 value 属性一样,使用变量或者其他结果为 `true` 或 `false` 的表达式作为其条件。
+
+例如:
+```html
+<!-- 给定值: -->
+<button x-bind:disabled="myVar">Click me</button>
+
+<!-- 当 myVar == true: -->
+<button disabled="disabled">Click me</button>
+
+<!-- 当 myVar == false: -->
+<button>Click me</button>
+```
+
+这将在 `myVar` 为 true 或 false 时分别新增或删除 `disabled` 属性。
+
+[HTML specification 文档(英文)](https://html.spec.whatwg.org/multipage/indices.html#attributes-3:boolean-attribute)中列出的布尔值属性都被支持,例如: `disabled`, `readonly`, `required`, `checked`, `hidden`, `selected`, `open` 等等。
+
+> 备注:如果你需要一个 false 状态来展示你的属性,如:`aria-*`,要用对绑定值使用链式的 `.toString()`。例如: `:aria-expanded="isOpen.toString()"` 将在 `isOpen` 为 `true` 或 `false` 时都能保持属性存在。
+
+**`.camel` 修饰符**
+**例如:** `<svg x-bind:view-box.camel="viewBox">`
+
+`camel` 修饰符用来绑定等效驼峰写法的属性名。如上面的例子,`viewBox` 的值将被绑定到 `viewBox` 属性,他被表示为 `view-box`。
+
+---
+
+### `x-on`
+
+> 备注:你可以直接使用简化的 "@" 语法,例如:`@click="..."`。
+
+**例如:** `<button x-on:click="foo = 'bar'"></button>`
+
+**结构:** `<button x-on:[事件]="[表达式]"></button>`
+
+`x-on` 挂载一个事件监听器到声明的元素昂,当事件触发后,传入的表达式将被执行。你可以在指令所在元素可用的任何事件上使用 `x-on`,完整的事件列表,可以参考 [MDN 上的事件参考文档](https://developer.mozilla.org/en-US/docs/Web/Events)。
+
+如果表达式中的进行了任何数据变动,其他绑定到这些数据的元素属性将被更新。
+
+> 备注:你也可以直接使用 JavaScript 函数名。
+
+**例如:** `<button x-on:click="myFunction"></button>`
+
+这等效于:`<button x-on:click="myFunction($event)"></button>`
+
+**`keydown` 修饰符**
+
+**例如:** `<input type="text" x-on:keydown.escape="open = false">`
+
+你可以通过向 `x-on:keydown` 指令增加 keydown 修饰符来指定特定需要监听的按钮。注意修饰符是 `Event.key` 值的短横线隔开写法(kebab-cased)版本。
+
+例如:`enter`, `escape`, `arrow-up`, `arrow-down`
+
+> 备注:你也可以这样监听混合的系统修饰符: `x-on:keydown.cmd.enter="foo"`
+
+**`.away` 修饰符**
+
+**例如:** `<div x-on:click.away="showModal = false"></div>`
+
+当 `.away` 修饰符存在时,事件处理器将在源头不是他们自己或他们的子元素时被触发。
+
+这对下拉菜单或模态框中处理用户点击其外部的位置关闭的交互动作非常有用。
+
+**`.prevent` 修饰符**
+**例如:** `<input type="checkbox" x-on:click.prevent>`
+
+增加 `.prevent` 到事件监听器上,将会调用触发事件的 `preventDefault` 方法。在上面的例子中,这意味着这个 checkbox 在用户点击时不会真的被选中。
+
+**`.stop` 修饰符**
+**例如:** `<div x-on:click="foo = 'bar'"><button x-on:click.stop></button></div>`
+
+增加 `.stop` 到事件监听器上,将会调用触发事件的 `stopPropagation` 方法。在上面的例子中,这意味着点击事件不会从这个按钮冒泡到其他 div 上。换句话说,用户点击按钮时 `foo` 并不会被置为 `bar`。
+
+**`.self` 修饰符**
+**例如:** `<div x-on:click.self="foo = 'bar'"><button></button></div>`
+
+增加 `.self` 到事件监听器上,将会使得处理器只在 `$event.target` 为元素自己的时候被调用。在上面的例子中,这意味着按钮的点击事件会冒泡的上层的 `<div>` 时,将 **不会** 调用处理器函数。
+
+**`.window` 修饰符**
+**例如:** `<div x-on:resize.window="isOpen = window.outerWidth > 768 ? false : open"></div>`
+
+增加 `.window` 到事件监听器上,将会安装监听器到全局的 window 对象而不是实际声明的 DOM 节点。当你需要在 window 上的属性变化(如 resize 事件)时修改自己组件的状态,这会非常有用。在例子中,当窗口宽度扩大到 768 像素以上时我们就会关闭模态框/下拉菜单,否则保持已有状态。
+
+>备注:你也可以使用 `.document` 修饰符来挂载监听器到`document` 而不是 `window`
+
+**`.once` 修饰符**
+**例如:** `<button x-on:mouseenter.once="fetchSomething()"></button>`
+
+增加 `.once` 修饰符到事件监听器上,将会确保监听器只被调用一次。这对于那些你只想调一次的事件来说超有用,比如请求 HTML 片段之类的。
+
+**`.passive` 修饰符**
+**例如:** `<button x-on:mousedown.passive="interactive = true"></button>`
+
+增加 `.passive` 修饰符到事件监听器上,将会使监听器为 passive 状态,这意味着 `preventDefault()`不能工作。这可以帮助处理触摸设备上的滑动性能问题。
+
+**`.debounce` 修饰符**
+**例如:** `<input x-on:input.debounce="fetchSomething()">`
+
+`debounce` 修饰符允许你对事件处理器进行「防抖」。换句话说,事件处理器将会在上次触发完成后等待一段时间才会再次触发。当处理器准备好后再处理上一个调用。
+
+默认的防抖「等待」时间是 250 毫秒。
+
+如果你打算自定义它,可以这样指定:
+
+```
+<input x-on:input.debounce.750="fetchSomething()">
+<input x-on:input.debounce.750ms="fetchSomething()">
+```
+
+**`.camel` 修饰符**
+**例如:** `<input x-on:event-name.camel="doSomething()">`
+
+`camel` 修饰符用来挂载等效驼峰写法的事件监听器。如上面的例子,表达式将在 `eventName` 事件触发时被执行。
+
+---
+
+### `x-model`
+**例如:** `<input type="text" x-model="foo">`
+
+**结构:** `<input type="text" x-model="[data item]">`
+
+`x-model` 新增「双向数据绑定」到一个元素上。换句话说,输入元素的 value 值将于组件中数据项的值维持同步。
+
+> 备注:`x-model` 可以智能地检测文字输入框(input)、多选框(checkbox)、单选框(radio button)、文本输入区(textarea)、下拉选择(select)、下拉多选(multi selects)。他的行为及使用场景和 [Vue](https://vuejs.org/v2/guide/forms.html) 别无二致。
+
+**`.number` 修饰符**
+**例如:** `<input x-model.number="age">`
+
+`number` 修饰符将转换输入的值为数据为数字类型,如果不能被成功转换,将返回原始值。
+
+**`.debounce` 修饰符**
+**例如:** `<input x-model.debounce="search">`
+
+`debounce` 修饰符允许你对值的更新进行「防抖」。换句话说,事件处理器将会在上次触发完成后等待一段时间才会再次触发。当处理器准备好后再处理上一个调用。
+
+默认的防抖「等待」时间是 250 毫秒。
+
+如果你打算自定义它,可以这样指定:
+
+```
+<input x-model.debounce.750="search">
+<input x-model.debounce.750ms="search">
+```
+
+---
+
+### `x-text`
+**例如:** `<span x-text="foo"></span>`
+
+**结构:** `<span x-text="[表达式]"`
+
+`x-text` 与 `x-bind` 十分相似,只不过当属性中的值更新时,他会更新元素的 `innerText`。
+
+---
+
+### `x-html`
+**例如:** `<span x-html="foo"></span>`
+
+**结构:** `<span x-html="[表达式]"`
+
+`x-html` 与 `x-bind` 十分相似,只不过当属性中的值更新时,他会更新元素的 `innerHTML`。
+
+> :warning: **只能用在可信内容上,绝不要直接展示用户的内容** :warning:
+>
+> 动态渲染第三方来源的 HTML 会非常容易造成 [XSS 漏洞(英文)](https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting)。
+
+---
+
+### `x-ref`
+**例如:** `<div x-ref="foo"></div><button x-on:click="$refs.foo.innerText = 'bar'"></button>`
+
+**结构:** `<div x-ref="[ref 名称]"></div><button x-on:click="$refs.[ref 名称].innerText = 'bar'"></button>`
+
+`x-ref` 提供了一种在组件外获取原始 DOM 元素的简便方法。通过设置元素上的 `x-ref` 属性,你可在任何事件处理中通过内置对象(魔法属性) `$refs` 和标记的 refName 拿到 DOM。
+
+它可以帮助你在任何地方替代设置id再通过 `document.querySelector` 取值的做法。
+
+> 备注:如果你需要,也可以绑个动态值到 x-ref `<span :x-ref="item.id"></span>`。
+
+---
+
+### `x-if`
+**例如:** `<template x-if="true"><div>Some Element</div></template>`
+
+**结构:** `<template x-if="[表达式]"><div>Some Element</div></template>`
+
+有的时候,`x-show` 可能会不太好用(它会在值为 false 时为元素增加 `display: none` 样式),`x-if` 可以真实的完整的把元素从 DOM 中删除。
+
+但要注意,`x-if` 只能在 `<template></template>` 标签中使用,因为 Alpine 没有使用虚拟 DOM。这一实现允许 Alpine 保持简洁,直接使用真实的 DOM 来完成动态部分(magic)。
+
+> 备注:`x-if` 要求在 `<template></template>` 标签中有单一的根元素。
+
+> 备注:当在 `svg` 标签中使用 `template` 的时候,你需要增加 [polyfill](https://github.com/alpinejs/alpine/issues/637#issuecomment-654856538) 在初始化 Alpine 之前运行它。
+
+---
+
+### `x-for`
+**例如:**
+```html
+<template x-for="item in items" :key="item">
+    <div x-text="item"></div>
+</template>
+```
+
+> 备注:`:key` 绑定时可选的,但是强烈推荐,使用它在列表渲染中是必要的。
+
+`x-for` 可以用于为数组中的每一项创建一个新的 DOM 节点。这一特性与 Vue 中的 `v-for` 非常类似,他也需要在 `template` 标签上使用,不能是普通的 DOM 元素。
+
+如果需要使用当前迭代中的索引,可以这样写:
+
+```html
+<template x-for="(item, index) in items" :key="index">
+    <!-- 你可以在需要时直接使用索引值 index。 -->
+    <div x-text="index"></div>
+</template>
+```
+
+若果你需要在迭代中访问原始数组对象,可以这样写:
+
+```html
+<template x-for="(item, index, collection) in items" :key="index">
+    <div>
+        <!-- 你可以在需要时直接使用数组原始值 collection。 -->
+        <!-- 当前项 -->
+        <div x-text="item"></div>
+        <!-- 和上一行效果一致 -->
+        <div x-text="collection[index]"></div>
+        <!-- 上一项 -->
+        <div x-text="collection[index - 1]"></div>
+    </div>
+</template>
+```
+
+> 备注:`x-for` 要求在 `<template></template>` 标签中有单一的根元素。
+
+> 备注:当在 `svg` 标签中使用 `template` 的时候,你需要增加 [polyfill](https://github.com/alpinejs/alpine/issues/637#issuecomment-654856538) 在初始化 Alpine 之前运行它。
+
+#### 嵌套 `x-for`
+可以嵌套使用 `x-for` 循环,但是你必须为每个循环外面都包裹一个元素,如下:
+
+```html
+<template x-for="item in items">
+    <div>
+        <template x-for="subItem in item.subItems">
+            <div x-text="subItem"></div>
+        </template>
+    </div>
+</template>
+```
+
+#### 范围内迭代
+
+Alpine 支持 `i in n` 语法,当 n 是整数时,就可以完成在一个固定范围内迭代的目标了。
+
+```html
+<template x-for="i in 10">
+    <span x-text="i"></span>
+</template>
+```
+
+---
+
+### `x-transition`
+**例如:**
+```html
+<div
+    x-show="open"
+    x-transition:enter="transition ease-out duration-300"
+    x-transition:enter-start="opacity-0 transform scale-90"
+    x-transition:enter-end="opacity-100 transform scale-100"
+    x-transition:leave="transition ease-in duration-300"
+    x-transition:leave-start="opacity-100 transform scale-100"
+    x-transition:leave-end="opacity-0 transform scale-90"
+>...</div>
+```
+
+```html
+<template x-if="open">
+    <div
+        x-transition:enter="transition ease-out duration-300"
+        x-transition:enter-start="opacity-0 transform scale-90"
+        x-transition:enter-end="opacity-100 transform scale-100"
+        x-transition:leave="transition ease-in duration-300"
+        x-transition:leave-start="opacity-100 transform scale-100"
+        x-transition:leave-end="opacity-0 transform scale-90"
+    >...</div>
+</template>
+```
+
+> 这个例子使用了 [Tailwind CSS](https://tailwindcss.com) 中提供的 class。
+
+Alpine 提供了 6 个不同的过渡指令用来在元素过渡(Transition)的显示/隐藏状态下不同阶段生效不同的 class。这些指令都能和 `x-show` 还有 `x-if` 一起使用。
+
+这些行为与 VueJS 的过渡指令完全一致。只是用了更清晰的命名。
+
+| 指令 | 描述 |
+| --- | --- |
+| `:enter` | 在整个 Enter 阶段都生效 |
+| `:enter-start` | 在元素被插入前生效,在元素被插入的后1帧被删除 |
+| `:enter-end` | 在元素被插入后1帧(`enter-start` 被移除的同时)生效,在过渡/动画结束后被删除 |
+| `:leave` | 在整个 Leave 阶段都生效 |
+| `:leave-start` | 在 Leave 过渡触发后立即生效,1帧即被删除 |
+| `:leave-end` | 在 Leave 过渡触发后(`leave-start` 删除的同时)1 帧新增,并在过渡/动画结束时删除。|
+
+---
+
+### `x-spread`
+**例如:**
+```html
+<div x-data="dropdown()">
+    <button x-spread="trigger">Open Dropdown</button>
+
+    <span x-spread="dialogue">Dropdown Contents</span>
+</div>
+
+<script>
+    function dropdown() {
+        return {
+            open: false,
+            trigger: {
+                ['@click']() {
+                    this.open = true
+                },
+            },
+            dialogue: {
+                ['x-show']() {
+                    return this.open
+                },
+                ['@click.away']() {
+                    this.open = false
+                },
+            }
+        }
+    }
+</script>
+```
+
+`x-spread` 允许你展开一个可以复用的对象,其中包括元素的 Alpine 绑定。
+
+这一对象的 key 是指令(当然也可以包括修饰符),value 是可被 Alpine 执行的回调。
+
+> 备注: x-spread 有以下两点需要注意
+> - 当被展开的指令是 `x-for` 时,你需要在回调中返回一个表达式字符串,如:`['x-for']() { return 'item in items' }`.
+> - `x-data` 和 `x-init` 不能在「spread」对象中使用。
+
+---
+
+### `x-cloak`
+**例如:** `<div x-data="{}" x-cloak></div>`
+
+`x-cloak` 属性将在 Alpine 初始化后被移除。这对于隐藏未初始化完毕的 DOM 非常有效,一种典型的通过全局样式的工作方式如下:
+
+```html
+<style>
+    [x-cloak] {
+        display: none !important;
+    }
+</style>
+```
+
+### 魔法属性 Magic Properties
+
+> 除 `$el` 外,魔法属性 **在 `x-data` 中的无法使用**,因为那时组件还没初始化。
+
+---
+
+### `$el`
+**例如:**
+```html
+<div x-data>
+    <button @click="$el.innerHTML = 'foo'">Replace me with "foo"</button>
+</div>
+```
+
+`$el` 是一个用来获取根组件 DOM 节点的魔法属性。
+
+### `$refs`
+**例如:**
+```html
+<span x-ref="foo"></span>
+
+<button x-on:click="$refs.foo.innerText = 'bar'"></button>
+```
+
+`$refs` 是一个用来在组件中获取原生 DOM 元素的魔法属性。这可以帮助你在需要时手动拿到原始的 DOM 元素。
+
+---
+
+### `$event`
+**例如:**
+```html
+<input x-on:input="alert($event.target.value)">
+```
+
+`$event` 是一个用来在事件监听器中接受浏览器原生「Event」对象的魔法属性。
+
+> 备注: $event 属性只在 DOM 表达式中有效。
+
+如果你需要在一个 JavaScript 函数中使用 $event,可以直接这么干:
+
+`<button x-on:click="myFunction($event)"></button>`
+
+---
+
+### `$dispatch`
+**例如:**
+```html
+<div @custom-event="console.log($event.detail.foo)">
+    <button @click="$dispatch('custom-event', { foo: 'bar' })">
+    <!-- 点击时,将触发 console.log -->
+</div>
+```
+
+**有关事件传播 (Event Propagation)**
+
+请注意,因为 [事件冒泡机制](https://en.wikipedia.org/wiki/Event_bubbling) 的存在,当你需要从相同层级节点上拦截已触发的事件时,需要增加 [`.window`](https://github.com/alpinejs/alpine#x-on) 修饰符:
+
+**例如:**
+
+```html
+<div x-data>
+    <span @custom-event="console.log($event.detail.foo)"></span>
+    <button @click="$dispatch('custom-event', { foo: 'bar' })">
+<div>
+```
+
+> 上面的例子是错误示范,不能正常工作,因为 `custom-event` 被触发时,他只会传播到共同的祖先,上级的 `div` 元素。
+
+**分发到组件**
+
+也可以刚才这一技巧在组件之间通信使用:
+
+**例如:**
+
+```html
+<div x-data @custom-event.window="console.log($event.detail)"></div>
+
+<button x-data @click="$dispatch('custom-event', 'Hello World!')">
+<!-- 当点击发生时,将会 console.log "Hello World!". -->
+```
+
+`$dispatch` 是一个创建 `CustomEvent` 并使用其内部的 `.dispatchEvent()` 进行分发的快捷方式。有很多使用自定义事件在组件间传递数据的优秀用例。[参考这篇文档](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events) 来获取更多关于浏览器中 `CustomEvent` 的信息。
+
+可以注意到,放在 `$dispatch('some-event', { some: 'data' })` 第二个参数的数据,在新事件上可通过「detail」属性来拿到:`$event.detail.some`。将自定义事件的数据附加到 .detail 属性是在浏览器中 `CustomEvent` 的标准实践。更多信息参阅 [这篇文档(英文)](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail)。
+
+你也可以使用 `$dispatch()` 来触发 `x-model` 绑定的数据更新。例如:
+
+```html
+<div x-data="{ foo: 'bar' }">
+    <span x-model="foo">
+        <button @click="$dispatch('input', 'baz')">
+        <!-- 按钮被点击后,`x-model` 将获得冒泡的 「input」事件,并更新 foo 为 「baz」。 -->
+    </span>
+</div>
+```
+
+> 备注:$dispatch 属性只在 DOM 表达式中有效。
+
+如果你需要在一个 JavaScript 函数中使用 $dispatch,可以直接这么干:
+
+`<button x-on:click="myFunction($dispatch)"></button>`
+
+---
+
+### `$nextTick`
+**例如:**
+```html
+<div x-data="{ fruit: 'apple' }">
+    <button
+        x-on:click="
+            fruit = 'pear';
+            $nextTick(() => { console.log($event.target.innerText) });
+        "
+        x-text="fruit"
+    ></button>
+</div>
+```
+
+`$nextTick` 是一个运行你在 Alpine 做出响应式 DOM 更新后,执行一个给定表达式的指令。这在你需要在完成数据更新后用 DOM 状态做交互时非常有用。
+
+---
+
+### `$watch`
+**例如:**
+```html
+<div x-data="{ open: false }" x-init="$watch('open', value => console.log(value))">
+    <button @click="open = ! open">Toggle Open</button>
+</div>
+```
+
+你可以通过 `$watch` 魔法方法「监听」一个组件属性。在上面的例子中,当按钮被点击且 open 被改变时,你提供的回调函数会被调用,`console.log` 新的 value。
+
+## 安全相关 Security
+如果你发现了安全漏洞,请发邮件到 [calebporzio@gmail.com]().
+
+Alpine 依赖对 `Function` 对象的自定义实现来运行指令。虽然比 `eval()` 安全一,但在某些环境下这一方法也不可用,例如 Google Chrome App 使用的限制性内容安全策略(Content Security Policy/CSP).
+
+如果你在使用 Alpin 的网站上处理敏感数据,就需要设置 [CSP](https://csp.withgoogle.com/docs/strict-csp.html) 使之包括 `unsafe-eval`。正确设置安全的策略有助于保护用户的个人隐私数据。
+
+由于策略设置会适用于页面中所有脚本,务必要检查页面引入的其他外部依赖,避免它们引发 XSS 漏洞、通过 `eval()` 修改 DOM 注入恶意代码。
+
+## V3 路线图 Roadmap
+* 从`x-ref` 迁移到 `ref` 以与 Vue 保持一致?
+* 新增 `Alpine.directive()`
+* 新增 `Alpine.component('foo', {...})` (通过 `__init()` 魔法方法)
+* 分发 Alpine 的 "loaded", "transition-start" 等多个事件 ([#299](https://github.com/alpinejs/alpine/pull/299)) ?
+* 移除 `x-bind:class="{ 'foo': true }"` 中的使用对象(以及数组)的语法 ([#236](https://github.com/alpinejs/alpine/pull/236) 并增加在 `style` 中使用对象的支持
+* 优化 `x-for` 的响应式效果 ([#165](https://github.com/alpinejs/alpine/pull/165))
+* 增加 "deep watching" 支持 ([#294](https://github.com/alpinejs/alpine/pull/294))
+* 增加 `$el` 快捷方式
+* 修改 `@click.away` 为 `@click.outside`?
+
+## 许可证 License
+
+Copyright © 2019-2020 Caleb Porzio 与仓库所有贡献者 版权所有
+
+基于 MIT 许可证开源,查看 [LICENSE.md](LICENSE.md) 获取详情。

+ 16 - 15
dist/alpine-ie11.js

@@ -6929,8 +6929,12 @@
       event = camelCase(event);
     }
 
+    var _handler2, listenerTarget;
+
     if (modifiers.includes('away')) {
-      var _handler = function handler(e) {
+      listenerTarget = document;
+
+      _handler2 = function handler(e) {
         _newArrowCheck(this, _this);
 
         // Don't do anything if the click came from the element or within it.
@@ -6942,16 +6946,13 @@
         runListenerHandler(component, expression, e, extraVars);
 
         if (modifiers.includes('once')) {
-          document.removeEventListener(event, _handler, options);
+          document.removeEventListener(event, _handler2, options);
         }
-      }.bind(this); // Listen for this event at the root level.
-
-
-      document.addEventListener(event, _handler, options);
+      }.bind(this);
     } else {
-      var listenerTarget = modifiers.includes('window') ? window : modifiers.includes('document') ? document : el;
+      listenerTarget = modifiers.includes('window') ? window : modifiers.includes('document') ? document : el;
 
-      var _handler2 = function handler(e) {
+      _handler2 = function _handler(e) {
         var _this2 = this;
 
         _newArrowCheck(this, _this);
@@ -6991,15 +6992,15 @@
           }.bind(this));
         }
       }.bind(this);
+    }
 
-      if (modifiers.includes('debounce')) {
-        var nextModifier = modifiers[modifiers.indexOf('debounce') + 1] || 'invalid-wait';
-        var wait = isNumeric(nextModifier.split('ms')[0]) ? Number(nextModifier.split('ms')[0]) : 250;
-        _handler2 = debounce(_handler2, wait);
-      }
-
-      listenerTarget.addEventListener(event, _handler2, options);
+    if (modifiers.includes('debounce')) {
+      var nextModifier = modifiers[modifiers.indexOf('debounce') + 1] || 'invalid-wait';
+      var wait = isNumeric(nextModifier.split('ms')[0]) ? Number(nextModifier.split('ms')[0]) : 250;
+      _handler2 = debounce(_handler2, wait);
     }
+
+    listenerTarget.addEventListener(event, _handler2, options);
   }
 
   function runListenerHandler(component, expression, e, extraVars) {

+ 15 - 14
dist/alpine.js

@@ -863,8 +863,12 @@
       event = camelCase(event);
     }
 
+    let handler, listenerTarget;
+
     if (modifiers.includes('away')) {
-      let handler = e => {
+      listenerTarget = document;
+
+      handler = e => {
         // Don't do anything if the click came from the element or within it.
         if (el.contains(e.target)) return; // Don't do anything if this element isn't currently visible.
 
@@ -876,14 +880,11 @@
         if (modifiers.includes('once')) {
           document.removeEventListener(event, handler, options);
         }
-      }; // Listen for this event at the root level.
-
-
-      document.addEventListener(event, handler, options);
+      };
     } else {
-      let listenerTarget = modifiers.includes('window') ? window : modifiers.includes('document') ? document : el;
+      listenerTarget = modifiers.includes('window') ? window : modifiers.includes('document') ? document : el;
 
-      let handler = e => {
+      handler = e => {
         // Remove this global event handler if the element that declared it
         // has been removed. It's now stale.
         if (listenerTarget === window || listenerTarget === document) {
@@ -917,15 +918,15 @@
           });
         }
       };
+    }
 
-      if (modifiers.includes('debounce')) {
-        let nextModifier = modifiers[modifiers.indexOf('debounce') + 1] || 'invalid-wait';
-        let wait = isNumeric(nextModifier.split('ms')[0]) ? Number(nextModifier.split('ms')[0]) : 250;
-        handler = debounce(handler, wait);
-      }
-
-      listenerTarget.addEventListener(event, handler, options);
+    if (modifiers.includes('debounce')) {
+      let nextModifier = modifiers[modifiers.indexOf('debounce') + 1] || 'invalid-wait';
+      let wait = isNumeric(nextModifier.split('ms')[0]) ? Number(nextModifier.split('ms')[0]) : 250;
+      handler = debounce(handler, wait);
     }
+
+    listenerTarget.addEventListener(event, handler, options);
   }
 
   function runListenerHandler(component, expression, e, extraVars) {

+ 1 - 1
src/directives/for.js

@@ -95,7 +95,7 @@ function evaluateItemsAndReturnEmptyIfXIfIsPresentAndFalseOnElement(component, e
     let items = component.evaluateReturnExpression(el, iteratorNames.items, extraVars)
 
     // This adds support for the `i in n` syntax.
-    if (isNumeric(items) && items > 0) {
+    if (isNumeric(items) && items >= 0) {
         items = Array.from(Array(items).keys(), i => i + 1)
     }
 

+ 14 - 13
src/directives/on.js

@@ -9,8 +9,12 @@ export function registerListener(component, el, event, modifiers, expression, ex
         event = camelCase(event);
     }
 
+    let handler, listenerTarget
+
     if (modifiers.includes('away')) {
-        let handler = e => {
+        listenerTarget = document
+
+        handler = e => {
             // Don't do anything if the click came from the element or within it.
             if (el.contains(e.target)) return
 
@@ -25,14 +29,11 @@ export function registerListener(component, el, event, modifiers, expression, ex
                 document.removeEventListener(event, handler, options)
             }
         }
-
-        // Listen for this event at the root level.
-        document.addEventListener(event, handler, options)
     } else {
-        let listenerTarget = modifiers.includes('window')
+        listenerTarget = modifiers.includes('window')
             ? window : (modifiers.includes('document') ? document : el)
 
-        let handler = e => {
+        handler = e => {
             // Remove this global event handler if the element that declared it
             // has been removed. It's now stale.
             if (listenerTarget === window || listenerTarget === document) {
@@ -68,15 +69,15 @@ export function registerListener(component, el, event, modifiers, expression, ex
                 })
             }
         }
+    }
 
-        if (modifiers.includes('debounce')) {
-            let nextModifier = modifiers[modifiers.indexOf('debounce')+1] || 'invalid-wait'
-            let wait = isNumeric(nextModifier.split('ms')[0]) ? Number(nextModifier.split('ms')[0]) : 250
-            handler = debounce(handler, wait, this)
-        }
-
-        listenerTarget.addEventListener(event, handler, options)
+    if (modifiers.includes('debounce')) {
+        let nextModifier = modifiers[modifiers.indexOf('debounce')+1] || 'invalid-wait'
+        let wait = isNumeric(nextModifier.split('ms')[0]) ? Number(nextModifier.split('ms')[0]) : 250
+        handler = debounce(handler, wait, this)
     }
+
+    listenerTarget.addEventListener(event, handler, options)
 }
 
 function runListenerHandler(component, expression, e, extraVars) {

+ 17 - 0
test/for.spec.js

@@ -556,3 +556,20 @@ test('x-for with an array of numbers', async () => {
         expect(document.querySelectorAll('span')[1].textContent).toEqual('3')
     })
 })
+
+test('x-for over range using i in x syntax with i <= 0', async () => {
+    document.body.innerHTML = `
+        <div x-data="{ count: 1 }">
+            <template x-for="i in count">
+                <span x-text="i"></span>
+            </template>
+            <button @click="count--"></button>
+        </div>
+    `
+
+    Alpine.start()
+
+    document.querySelector('button').click()
+
+    await wait(() => { expect(document.querySelectorAll('span').length).toEqual(0) })
+})