Skip to content

پراپرتی‌های computed

مثال پایه

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

js
export default {
  data() {
    return {
      author: {
        name: 'John Doe',
        books: [
          'Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - The Mystery'
        ]
      }
    }
  }
}
js
const author = reactive({
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
    'Vue 3 - Basic Guide',
    'Vue 4 - The Mystery'
  ]
})

و می‌خواهیم پیام‌های مختلفی را بر اساس اینکه آیا author قبلاً کتابی داشته یا نه نمایش دهیم:

html
<p>Has published books:</p>
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>

در این نقطه، تمپلیت کمی پیچیده شده است. باید وقت بیشتری برای درک کد صرف کنیم که متوجه شویم شرط بر اساس author.books یک محاسبه انجام می‌دهد. مهمتر از این، اگر نیاز باشد که این محاسبه را بیش از یک بار در تمپلیت استفاده کنیم، احتمالاً نمی‌خواهیم یک کد را چندین بار بنویسیم.

به همین دلیل است که وقتی منطق پیچیده و داده‌های پویا داریم، استفاده از ویژگی computed توصیه می‌شود. کلمه computed به معنی محاسبه شده است. در اینجا همان مثال بازسازی شده است:

js
export default {
  data() {
    return {
      author: {
        name: 'John Doe',
        books: [
          'Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - The Mystery'
        ]
      }
    }
  },
  computed: {
    // a computed getter
    publishedBooksMessage() {
      // به نمونه ساخته شده از کامپوننت اشاره می‌کند `this`
      return this.author.books.length > 0 ? 'Yes' : 'No'
    }
  }
}
template
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>

آن را در Playground امتحان کنید

در اینجا ما یک پراپرتی computed به نام publishedBooksMessage داریم.

سعی کنید مقدار آرایه books را تغییر دهید و خواهید دید که publishedBooksMessage متناسب با آن تغییر می‌کند.

پراپرتی‌های computed را می‌توان در تمپلیت‌ها همانند یک ویژگی عادی استفاده کرد. Vue به طور خودکار متوجه می‌شود که پراپرتی computed وابسته به یک یا چند ویژگی دیگر است. بنابراین، هرگاه ویژگی‌های وابسته تغییر کنند، computed نیز به‌روز می‌شود.

همچنین ببینید: Typing Computed

vue
<script setup>
import { reactive, computed } from 'vue'

const author = reactive({
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
    'Vue 3 - Basic Guide',
    'Vue 4 - The Mystery'
  ]
})

// a computed ref
const publishedBooksMessage = computed(() => {
  return author.books.length > 0 ? 'Yes' : 'No'
})
</script>

<template>
  <p>Has published books:</p>
  <span>{{ publishedBooksMessage }}</span>
</template>

آن را در Playground امتحان کنید

در اینجا یک پراپرتی computed به نام publishedBooksMessage تعریف کرده‌ایم. تابع computed()‎ انتظار دارد که یک getter function به عنوان ورودی دریافت کند که مقدار بازگشتی آن از نوع computed ref باشد. مشابه ref های عادی، شما می‌توانید به نتیجه محاسبه شده با عنوان publishedBooksMessage.value دسترسی پیدا کنید. computed ref ها همچنین در تمپلیت‌ها به صورت خودکار باز می‌شوند، بنابراین می‌توانید بدون نیاز به ‎.value به آنها دسترسی پیدا کنید.

یک پراپرتی computed به طور خودکار وابستگی‌های reactive خود را دنبال می‌کند. Vue می‌داند که محاسبه publishedBooksMessage به author.books وابستگی دارد، بنابراین هنگامی که author.books تغییر می‌کند، هر اتصالی که به publishedBooksMessage وابسته باشد، به‌روزرسانی می‌شود.

همچنین ببینید: Typing Computed

تفاوت کشینگ در computedها و متدها

ممکن است متوجه شده باشید که می‌توانیم با فراخوانی یک متد هم به همان نتیجه برسیم.

html
<p>{{ calculateBooksMessage() }}</p>
js
// in component
methods: {
  calculateBooksMessage() {
    return this.author.books.length > 0 ? 'Yes' : 'No'
  }
}
js
// in component
function calculateBooksMessage() {
  return author.books.length > 0 ? 'Yes' : 'No'
}

به جای یک پراپرتی computed، می توانیم همان تابع را به عنوان یک متد تعریف کنیم. نتیجه نهایی این دو رویکرد دقیقاً یکسان است. با این حال، تفاوت این است که پراپرتی‌های computed بر اساس وابستگی‌های reactive، کش می‌شوند. یک پراپرتی computed تنها زمانی دوباره ارزیابی می‌شود که برخی از وابستگی‌های reactive آن تغییر کرده باشند. این بدان معناست که تا زمانی که author.books تغییر نکرده باشد، دسترسی به publishedBooksMessage نتیجه محاسبه قبلی را برمی‌گرداند، بدون نیاز به اجرای مجدد تابع getter .

این به این معنی هم هست که پراپرتی computed زیر هیچ وقت به‌روز نمی‌شود، زیرا Date.now()‎ یک reactive نمی‌باشد.

js
computed: {
  now() {
    return Date.now()
  }
}
js
const now = computed(() => Date.now())

در مقایسه، متد همیشه هر زمان که رندر مجدد اتفاق بیفتد، فراخوانی می‌شود.

چرا به کش نیاز داریم؟ تصور کنید ما یک لیست داریم که یک پراپرتی computed دارد که نیاز به انجام محاسبات زیادی دارد. سپس ممکن است پراپرتی‌های computed دیگری داشته باشیم که به نوبه خود به این لیست وابسته باشند. بدون کش، ما تابع دریافت کننده لیست را بیشتر از تعداد مورد نیاز اجرا می‌کنیم! در مواردی که نیاز به کش ندارید، به جای آن از فراخوانی متد استفاده کنید.

computed قابل تغییر

پراپرتی‌های computed به طور پیش‌فرض فقط امکان باز گرداندن داده را دارند. اگر سعی کنید مقدار جدیدی به یک پراپرتی computed اختصاص دهید، یک هشدار در زمان اجرا دریافت خواهید کرد. در موارد نادری که نیاز به "پراپرتی computed قابل تغییر" دارید، می‌توانید با ارائه همزمان یک تابع getter و یک تابع setter برای آن، یکی ایجاد کنید.

js
export default {
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe'
    }
  },
  computed: {
    fullName: {
      // getter
      get() {
        return this.firstName + ' ' + this.lastName
      },
      // setter
      set(newValue) {
        // Note: we are using destructuring assignment syntax here.
        [this.firstName, this.lastName] = newValue.split(' ')
      }
    }
  }
}

اکنون هنگام اجرای ‎this.fullName = 'John Doe'‎، تابع setter فراخوانی خواهد شد و به دنبال آن this.firstName و this.lastName به‌روزرسانی می‌شوند.

vue
<script setup>
import { ref, computed } from 'vue'

const firstName = ref('John')
const lastName = ref('Doe')

const fullName = computed({
  // getter
  get() {
    return firstName.value + ' ' + lastName.value
  },
  // setter
  set(newValue) {
    // Note: we are using destructuring assignment syntax here.
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})
</script>

وقتی شما دستور fullName.value = 'John Doe'‎ را اجرا کنید، تابع setter فراخوانی خواهد شد و firstName و lastName به‌طور متناسب به‌روزرسانی می‌شوند.

دریافت مقدار قبلی

  • فقط در نسخه 3.4+ پشتیبانی می‌شود

در صورتی که نیاز داشته باشید، می‌توانید مقدار قبلی برگردانده شده توسط پراپرتی computed را با دسترسی به آرگومان اول تابع getter دریافت کنید:

js
export default {
  data() {
    return {
      count: 2
    }
  },
  computed: {
    // را زمانی برمی‌گرداند که کمتر یا مساوی 3 باشد count مقدار computed این
    // باشد، آخرین مقداری که شرط ما را برآورده کرده است، برگردانده می شود count >=4 وقتی
    // کمتر یا مساوی 3 شود count تا زمانی که
    alwaysSmall(previous) {
      if (this.count <= 3) {
        return this.count;
      }

      return previous;
    }
  }
}
vue
<script setup>
import { ref, computed } from 'vue'

const count = ref(2)

// را زمانی برمی‌گرداند که کمتر یا مساوی 3 باشد count مقدار computed این
// باشد، آخرین مقداری که شرط ما را برآورده کرده است، برگردانده می شود count >=4 وقتی
// کمتر یا مساوی 3 شود count تا زمانی که
const alwaysSmall = computed((previous) => {
  if (count.value <= 3) {
    return count.value;
  }

  return previous;
})
</script>

در صورتی که از computed قابل تغییر استفاده می کنید:

js
export default {
  data() {
    return {
      count: 2
    }
  },
  computed: {
    alwaysSmall: {
      get(previous) {
        if (this.count <= 3) {
          return this.count;
        }

        return previous;
      },
      set(newValue) {
        this.count = newValue * 2;
      }
    }
  }
}
vue
<script setup>
import { ref, computed } from 'vue'

const count = ref(2)

const alwaysSmall = computed({
  get(previous) {
    if (count.value <= 3) {
      return count.value;
    }

    return previous;
  },
  set(newValue) {
    count.value = newValue * 2;
  }
})
</script>

بهترین شیوه‌ها

توابع getter باید فقط مقدار مورد نظر را برگردانند و تغییر دیگری در برنامه ایجاد نکنند.

مهم است به یاد داشته باشید که توابع getter در computed فقط باید محاسبات خالص را انجام دهند. به عبارت دیگر، درون تابع getter، دیگر stateها را تغییر ندهید، از درخواست‌های async یا تغییر DOM استفاده نکنید! به computed به عنوان یک راه ساده برای محاسبه یک مقدار بر اساس مقادیر دیگر نگاه کنید - مسئولیت اصلی آن تنها محاسبه و بازگرداندن آن مقدار می‌باشد. در ادامه این فصل، به بحث در مورد انجام عملیات‌هایی در پاسخ به تغییرات state با ناظرها (watchers) خواهیم پرداخت.

مقادیر computed را تغییر ندهید

مقدار بازگشتی از پراپرتی computed بر اساس مقادیر دیگر محاسبه می‌شود. به آن مانند یک عکس العمل موقت فکر کنید - هر بار که وضعیت مبدأ تغییر می‌کند، یک عکس العمل جدید ایجاد می‌شود. تغییر دادن یک عکس العمل موقت معنی ندارد، مقدار بازگشتی باید فقط خوانده شود و روی آن عملیاتی انجام نشود. برای تغییر مقدار بازگشتی، باید منبعی که پراپرتی computed به آن وابسته است را به‌روز کنید.

پراپرتی‌های computed has loaded