رندر List (List Rendering)

اصول آموزش vuejs

تاریخ : سه شنبه 16 مهر 1398

نگاشت آرایه به عناصر HTML با v-for

ما می توانیم از دایرکتیو v-for برای تهیه لیست از آیتم های موجود در آرایه استفاده کنیم. دایرکتیو v-for به صورت item in items به یک نحو خاص نیاز دارد ، که در آن items منبع دیتای آرایه و item یک نام مستعار برای عنصر آرایه که در آن تکرار می شود:

<ul id="example-1">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>
var example1 = new Vue({
  el: '#example-1',
  data: {
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})

نتیجه :

  • {{ item.message }}

در داخل بلوکهای v-for ، دسترسی کامل به ویژگیهای دامنه والدین داریم. v-for همچنین از یک آرگومان دوم اختیاری برای index آیتم فعلی پشتیبانی می کند.

<ul id="example-2">
  <li v-for="(item, index) in items">
    {{ parentMessage }} - {{ index }} - {{ item.message }}
  </li>
</ul>
var example2 = new Vue({
  el: '#example-2',
  data: {
    parentMessage: 'Parent',
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})

نتیجه :

  • {{ parentMessage }} - {{ index }} - {{ item.message }}

شما همچنین می توانید از of به جای in استفاده نمایید که به نحو جاوا اسکریپت برای تکرار کنندگان(iterators) نزدیکتر می باشد:

<div v-for="item of items"></div>


استفاده از v-for به همراه object

همچنین می توانید از v-for برای تکرار از طریق خواص یک object استفاده کنید.

<ul id="v-for-object" class="demo">
  <li v-for="value in object">
    {{ value }}
  </li>
</ul>
new Vue({
  el: '#v-for-object',
  data: {
    object: {
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    }
  }
})

نتیجه :

  • {{ value }}

همچنین می توانید یک آرگومان دوم برای نام ویژگی ارائه دهید ( a.k.a. key):

<div v-for="(value, name) in object">
  {{ name }}: {{ value }}
</div>
{{ name }}: {{ value }}

و برای index:

<div v-for="(value, name, index) in object">
  {{ index }}. {{ name }}: {{ value }}
</div>
{{ index }}. {{ name }}: {{ value }}
هنگام تکرار از طریق یک object ، مرتب سازی بر اساس ترتیب ()Object.keys صورت می گیرد ، که تضمینی ندارد مطابق با پیاده سازی موتور جاوا اسکریپت باشد.


حفظ وضعیت (Maintaining State)

هنگامی که Vue در حال به روزرسانی لیستی از عناصر رندر شده با v-for است ، به طور پیش فرض از استراتژی " in-place patch " استفاده می کند. اگر ترتیب داده ها تغییر کند ، به جای حرکت دادن عناصر DOM جهت تطبیق با ترتیب آیتم ها ، Vue هر عنصر را در جای خود قرار داده و اطمینان می دهد آنچه باید در آن index خاص رندر شود انعکاس یابد. این شبیه به رفتار track-by="$index" در Vue 1.x می باشد.

این حالت پیش فرض کارآمد است ، اما تنها در شرایطی مناسب است که خروجی لیست رندر شده شما به وضعیت کامپوننت فرزند یا وضعیت DOM موقتی متکی نباشد (مثلاً مقادیر ورودی فرم).

برای اشاره و راهنمایی به Vue که هویت هر گره را ردیابی کرده تا بتواند از آن ها استفاده مجدد و عناصر موجود را دوباره مرتب سازی نماید ، نیاز دارید یک ویژگی key منحصر به فرد برای هر آیتم در نظر بگیرید:

<div v-for="item in items" v-bind:key="item.id">
  <!-- content -->
</div>

توصیه می شود در هر زمان ممکن یک ویژگی key با v-for ارائه دهید ، مگر اینکه محتوای DOM تکرار شونده ساده باشد ، یا عمداً جهت افزایش عملکرد ، بر رفتار پیش فرض تکیه می کنید.

از آنجا که این یک مکانیسم عمومی جهت شناسایی گره ها توسط Vue است ، این key دارای کاربردهای دیگری نیز می باشد که به طور خاص با V-for گره خورده اند ، همانطور که بعداً در این راهنما خواهیم دید.

از مقادیری همانند اشیاء و آرایه ها به عنوان کلیدهای v-for استفاده نکنید. به جای آن از مقادیر رشته یا عددی استفاده کنید.

جهت استفاده دقیق از ویژگی key، به API documentation مراجعه نمایید.


تشخیص تغییر در آرایه (Array Change Detection)

متدهای جهش (Mutation Methods)

Vue متدهای mutation (جهش) آرایه ارائه داده که باعث بروزرسانی های view نیز می شود. این متدها عبارتند از:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

شما می توانید console مرورگر خود را باز کرده و در مثال قبل با متدها کار کنید.برای مثال : example1.items.push({ message: 'Baz' }).

جایگیزینی یک آرایه (Replacing an Array)

متدهای Mutation ، همانطور که از نام آن مشخص است ، آرایه اصلی مورد نظر را جهش و دگرگون می کنند. در مقایسه ، روشهای غیر جهش نیز وجود دارد ، به عنوان مثال . ()filter(), concat و ()slice، که آرایه اصلی را جهش نمی دهند ، اما همیشه یک آرایه جدید را برمی گردانند. هنگام کار با روش های غیر جهش ، می توانید آرایه قدیمی را با روش جدید جایگزین کنید:

example1.items = example1.items.filter(function (item) {
  return item.message.match(/Foo/)
})

ممکن است فکر کنید این امر باعث خواهد شد که DOM، Vue موجود را دور بیندازد و مجدداً لیست را ارائه دهد - خوشبختانه ، اینگونه نیست. Vue برای به حداکثر رساندن استفاده مجدد از عناصر DOM برخی از اکتشافات هوشمند را پیاده سازی می کند ، بنابراین جایگزینی یک آرایه با آرایه دیگری که حاوی اشیاء همپوشانی است ، عملیاتی بسیار کارآمد است.


هشدار

به دلیل محدودیت های موجود در JavaScript ، Vue نمی تواند تغییرات زیر را در یک آرایه تشخیص دهد:

  • زمانیکه یک آیتم را مستقیما با index جایگزاری می کنید ، به عنوان مثال vm.items [indexOfItem] = newValue
  • زمانیکه طول آرایه را تغییر می دهید ، به عنوان مثال vm.items.length = newLength

برای مثال:

var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items[1] = 'x' // is NOT reactive
vm.items.length = 2 // is NOT reactive

برای غلبه بر بر هشدار 1 ، هر دو کد زیر کار مشابه با vm.items [indexOfItem] = newValue انجام می دهند ، اما باعث بروزرسانی وضعیت در سیستم واکنش پذیری خواهد شد :

// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

همچنین می توانید از متد نمونه vm.$set استفاده کنید که یک نام مستعار برای Vue.set سراسری است:

vm.$set(vm.items, indexOfItem, newValue)

برای مقابله با هشدار 2 ، می توانید از splice استفاده کنید:

vm.items.splice(newLength)


Object Change Detection Caveats

مجددا به دلیل محدودیت های JavaScript مدرن ، Vue نمی تواند اضافه و یا حذف ویژگی (property) را تشخیص دهد. مثلا:

var vm = new Vue({
  data: {
    a: 1
  }
})
// `vm.a` is now reactive

vm.b = 2
// `vm.b` is NOT reactive

Vue اجازه نمی دهد به صورت پویا خصوصیات واکنشی سطح ریشه به یک نمونه ایجاد شده اضافه شود. با این وجود ، می توان با استفاده از روش Vue.set(object, propertyName, value) ویژگیهای واکنشی را به یک شیء تو در تو اضافه کرد. به عنوان مثال ،

var vm = new Vue({
  data: {
    userProfile: {
      name: 'Anika'
    }
  }
})

می توانید یک ویژگی جدید سن age را به شیء userProfile تودرتو اضافه کنید با:

Vue.set(vm.userProfile, 'age', 27)

همچنین می توانید از متد نمونه vm.$set استفاده کنید که یک نام مستعار برای Vue.set سراسری است:

vm.$set(vm.userProfile, 'age', 27)

بعضی اوقات ممکن است بخواهید تعدادی از ویژگی های جدید را به یک شی موجود اختصاص دهید ، به عنوان مثال با استفاده از ()Object.assign یا ()extend._ . در چنین مواردی ، شما باید یک شیء تازه را با خصوصیات هر دو شی ایجاد کنید. بنابراین به جای:

Object.assign(vm.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})

شما می توانید خصوصیات واکنشی جدید را با:

vm.userProfile = Object.assign({}, vm.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})


نمایش نتایج Filtered/Sorted

بعضی اوقات می خواهیم نسخه مرتب شده یا فیلتر شده از آرایه را نمایش دهیم بدون اینکه در واقع داده های اصلی را تغییر داده یا مجددا تنظیم کنیم. در این حالت ، می توانید یک ویژگی computed ایجاد کنید که آرایه فیلتر شده یا مرتب شده را برمی گرداند.

برای مثال:

<li v-for="n in evenNumbers">{{ n }}</li>
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
  evenNumbers: function () {
    return this.numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}

در شرایطی که ویژگی های computed امکان پذیر نیست (به عنوان مثال در داخل حلقه های تودردتو v-for ) ، می توانید از یک متد استفاده کنید:

<li v-for="n in even(numbers)">{{ n }}</li>
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
  even: function (numbers) {
    return numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}


v-for با یک رنج

v-for همچنین می تواند یک عدد صحیح را دریافت نماید. در این حالت این قالب را بارها تکرار می کند.

<div>
  <span v-for="n in 10">{{ n }} </span>
</div>

نتیجه:

1 2 3 4 5 6 7 8 9 10


v-for با <template>

شبیه به الگوی v-if ، می توانید از تگ <template> با v-for نیز استفاده کنید تا بتوانید از چندین عنصر از یک بلاک را رندر نمایید. مثلا:

<ul>
  <template v-for="item in items">
    <li>{{ item.msg }}</li>
    <li class="divider" role="presentation"></li>
  </template>
</ul>


v-for با v-if

توجه داشته باشید که توصیه نمی شود از v-if و v-for با هم استفاده کنید. برای جزئیات بیشتر به style guideمراجعه کنید.

هنگامی که آنها در همان گره وجود دارند ، v-for دارای اولویت بالاتری نسبت به v-if است. این بدان معنی است که v-if در هر تکرار حلقه به طور جداگانه اجرا می شود. این می تواند زمانی مفید باشد که می خواهید گره ها را فقط برای برخی موارد ، مانند زیر ارائه دهید:

<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo }}
</li>

موارد فوق فقط todos هایی را که کامل نیستند رندر می شوند.

اگر هدف شما از اجرای شرط حلقه بطور شرطی باشد ، می توانید v-if را روی یک عنصر بسته (یا <template>) قرار دهید. مثلا:

<ul v-if="todos.length">
  <li v-for="todo in todos">
    {{ todo }}
  </li>
</ul>
<p v-else>No todos left!</p>


v-for با Component

شما می توانید به طور مستقیم مانند هر عنصر عادی از v-for در یک کامپوننت سفارشی استفاده کنید:

<my-component v-for="item in items" :key="item.id"></my-component>
در 2.2.0+ ، هنگام استفاده از v-for با یک کامپوننت ، یک key لازم است.

با این وجود ، داده ها به طور خودکار به کامپوننت منتقل نمی شوند ، زیرا کامپوننت دارای محدوده های جدا شده از خود هستند. برای انتقال داده های تکرار شونده در کامپوننت ، باید از props نیز استفاده کنیم:

<my-component
  v-for="(item, index) in items"
  v-bind:item="item"
  v-bind:index="index"
  v-bind:key="item.id"
></my-component>

دلیل عدم تزریق خودکار item به کامپوننت این است که باعث می شود این کامپوننت با نحوه کار v-for پیوستگی محکمی ایجاد کند. صریح بودن اطلاعات مربوط به اینکه از کجا تهیه می شود باعث می شود کامپوننت در موقعیت های دیگر قابل استفاده مجدد باشد.

در اینجا یک مثال کامل از یک لیست ساده TODO آمده است:

<div id="todo-list-example">
  <form v-on:submit.prevent="addNewTodo">
    <label for="new-todo">Add a todo</label>
    <input
      v-model="newTodoText"
      id="new-todo"
      placeholder="E.g. Feed the cat"
    >
    <button>Add</button>
  </form>
  <ul>
    <li
      is="todo-item"
      v-for="(todo, index) in todos"
      v-bind:key="todo.id"
      v-bind:title="todo.title"
      v-on:remove="todos.splice(index, 1)"
    ></li>
  </ul>
</div>

توجه داشته باشید که ویژگی is="todo-item" است. این در الگوهای DOM ضروری است ، زیرا فقط یک عنصر <li> درون یک <ul> معتبر است. این همان کار را با <todo-item> انجام می دهد ، اما در اطراف یک خطای تجزیه مرورگر بالقوه کار می کند. برای کسب اطلاعات بیشتر به الگوی DOM Template Parsing Caveatsمراجعه کنید.

Vue.component('todo-item', {
  template: '\
    <li>\
      {{ title }}\
      <button v-on:click="$emit(\'remove\')">Remove</button>\
    </li>\
  ',
  props: ['title']
})

new Vue({
  el: '#todo-list-example',
  data: {
    newTodoText: '',
    todos: [
      {
        id: 1,
        title: 'Do the dishes',
      },
      {
        id: 2,
        title: 'Take out the trash',
      },
      {
        id: 3,
        title: 'Mow the lawn'
      }
    ],
    nextTodoId: 4
  },
  methods: {
    addNewTodo: function () {
      this.todos.push({
        id: this.nextTodoId++,
        title: this.newTodoText
      })
      this.newTodoText = ''
    }
  }
})

منابع مورد مطالعه جهت جمع آوری این مطلب:
https://vuejs.org/v2/guide/list.html


نظرات