Working with Vue.js Composition API in UniApp (Vue 3): Using ‘ref’, ‘reactive’, ‘computed’, ‘watch’.

Vue.js Composition API in UniApp: Ref, Reactive, Computed, and Watch – A Hilarious Deep Dive! 🚀

Alright, buckle up, buttercups! We’re diving headfirst into the wonderful world of Vue.js Composition API within the UniApp ecosystem. Forget the stuffy lectures of yesteryear. This is a party! 🎉 We’re going to explore the core concepts of ref, reactive, computed, and watch with enough humor and clarity to make even your grandma (who probably still uses Internet Explorer) understand.

Imagine the Options API (the old way of doing things) as a messy apartment. Your data is in the data section, your methods are in the methods section, your computed properties are in… well, you get the picture. Everything’s scattered and hard to find. The Composition API, on the other hand, is like a meticulously organized Marie Kondo’d apartment. Everything has its place, and related logic is neatly grouped together. Ah, bliss! 😌

So, grab your favorite beverage (mine’s a suspiciously bright green smoothie – don’t ask), and let’s get started!

I. The Setup: UniApp and the Composition API

First things first, let’s make sure we’re all on the same page. UniApp is a framework for building cross-platform apps using Vue.js. This means you can write one codebase and deploy it to iOS, Android, H5 (web), and even mini-programs (WeChat, Alipay, etc.). It’s like having a superpower! 🦸

The Composition API is a set of functions that allows you to organize your Vue component logic in a more logical and reusable way. It’s the new hotness, and for good reason!

How to use Composition API in UniApp?

Thankfully, UniApp supports the Composition API natively. You’ll primarily be working within the <script setup> tag. This is where the magic happens.

<template>
  <view>
    <h1>Hello, {{ name }}!</h1>
    <button @click="increment">Increment Count</button>
    <p>Count: {{ count }}</p>
    <p>Double Count: {{ doubleCount }}</p>
  </view>
</template>

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

  // Our Composition API logic goes here!
</script>

See that <script setup> tag? That’s your playground. Let’s fill it with fun!

II. ref: Your Basic Reactive Variable (The Tin Can Phone)

Think of ref as a tin can connected to a string. You shout something into one end (update the value), and the other end hears it (the view updates). It’s a simple, yet effective, way to create reactive variables.

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

  const name = ref('World')
  const count = ref(0)

  const increment = () => {
    count.value++ // Access the value through the .value property!
  }
</script>

Key takeaways about ref:

  • Purpose: Creates a reactive reference to a primitive value (number, string, boolean) or an object.
  • Accessing the value: You must use .value to get or set the value. This is the #1 gotcha for beginners, so remember it! 🧠
  • Usage: Ideal for simple data points that need reactivity.

Why .value?

This might seem weird at first, but there’s a good reason for it. Vue needs to track when the value changes to trigger updates in the view. The .value property acts as a "hook" that allows Vue to do its reactivity magic. Think of it as Vue constantly eavesdropping on the tin can phone line! 🕵️

Example Table: ref in Action

Scenario Code Snippet Explanation
Initializing a number const age = ref(30) Creates a reactive number with an initial value of 30.
Updating a string const message = ref('Hello'); message.value = 'Goodbye' Changes the reactive string from "Hello" to "Goodbye".
Using ref in the template <h1>{{ age }}</h1> The template automatically unwraps the ref value, so you don’t need .value here.
Resetting a boolean const isLoggedIn = ref(false); isLoggedIn.value = true Sets the reactive boolean to true.

III. reactive: For Complex Objects (The Intercom System)

If ref is a tin can phone, reactive is a fancy intercom system. It allows you to make entire objects reactive. Any change to a property within the object will trigger updates in the view.

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

  const user = reactive({
    firstName: 'John',
    lastName: 'Doe',
    age: 30
  })

  const updateFirstName = () => {
    user.firstName = 'Jane' // No .value here!
  }
</script>

<template>
  <view>
    <p>First Name: {{ user.firstName }}</p>
    <p>Last Name: {{ user.lastName }}</p>
    <p>Age: {{ user.age }}</p>
    <button @click="updateFirstName">Update First Name</button>
  </view>
</template>

Key takeaways about reactive:

  • Purpose: Creates a reactive object.
  • Accessing properties: You access properties directly, without .value. This is a crucial difference from ref.
  • Usage: Ideal for complex data structures like objects and arrays.

Important Considerations for reactive:

  • It’s deep: reactive makes all properties of the object reactive, even nested objects.
  • Replacement is a no-no: Don’t try to replace the entire reactive object with a new object. This will break reactivity. Instead, update the properties within the existing object.
  • Use toRefs if needed: If you need to destructure a reactive object and maintain reactivity, use the toRefs utility function (more on that later!).

Example Table: reactive in Action

Scenario Code Snippet Explanation
Initializing a user object const profile = reactive({ name: 'Alice', city: 'Wonderland' }) Creates a reactive user profile object.
Updating a nested property const product = reactive({ details: { price: 10 } }); product.details.price = 20 Updates the price property within the nested details object. Reactivity is maintained even with nested properties.
Using reactive in the template <p>{{ profile.name }} lives in {{ profile.city }}</p> The template automatically accesses the properties of the reactive object.
Adding a new property const myObject = reactive({}); myObject.newProperty = "Hello" You can add new properties to reactive objects after initialization, and they will also be reactive.

IV. computed: The Smart Assistant (The Calculator)

computed properties are like having a smart assistant who automatically calculates values based on other reactive data. They only update when their dependencies change, making them incredibly efficient.

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

  const price = ref(10)
  const quantity = ref(2)

  const total = computed(() => {
    return price.value * quantity.value
  })
</script>

<template>
  <view>
    <p>Price: {{ price }}</p>
    <p>Quantity: {{ quantity }}</p>
    <p>Total: {{ total }}</p>
  </view>
</template>

Key takeaways about computed:

  • Purpose: Creates a read-only reactive value that depends on other reactive values.
  • Automatic updates: computed properties are automatically updated whenever their dependencies change.
  • Caching: computed properties are cached, meaning they only re-evaluate when their dependencies change. This improves performance.
  • Read-only by default: You can define a setter function to make a computed property writable, but it’s generally used for derived data.

Why use computed?

Instead of calculating the total directly in your template ({{ price * quantity }}), using a computed property offers several advantages:

  • Readability: The logic is encapsulated in a dedicated property, making your template cleaner.
  • Reusability: You can reuse the total property in multiple places in your template.
  • Performance: As mentioned before, caching prevents unnecessary calculations.

Example Table: computed in Action

Scenario Code Snippet Explanation
Calculating full name from first and last const firstName = ref('John'); const lastName = ref('Doe'); const fullName = computed(() => firstName.value + ' ' + lastName.value) Creates a fullName property that automatically updates whenever firstName or lastName changes.
Formatting a date const rawDate = ref(new Date()); const formattedDate = computed(() => rawDate.value.toLocaleDateString()) Formats a raw date object into a user-friendly string.
Filtering an array const numbers = ref([1, 2, 3, 4, 5]); const evenNumbers = computed(() => numbers.value.filter(n => n % 2 === 0)) Creates a new array containing only the even numbers from the original numbers array. This array will update automatically if numbers changes.
Writable Computed Property const count = ref(0); const doubleCount = computed({ get: () => count.value * 2, set: (val) => { count.value = val / 2; } }) A computed property that can be read and written to. Setting a new value for doubleCount will update the underlying count ref.

V. watch: The Overzealous Security Guard (The Alarm System)

watch is like a security guard who keeps a close eye on specific reactive values. Whenever those values change, the guard springs into action and executes a predefined function.

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

  const inputValue = ref('')

  watch(inputValue, (newValue, oldValue) => {
    console.log(`Input value changed from ${oldValue} to ${newValue}`)
    // You can perform any action here, like making an API call
  })
</script>

<template>
  <view>
    <input type="text" v-model="inputValue" />
  </view>
</template>

Key takeaways about watch:

  • Purpose: Allows you to react to changes in specific reactive values.
  • Flexibility: You can perform any action within the watch callback function.
  • Explicit: Unlike computed, watch is explicitly defined. You tell Vue exactly what you want to watch.
  • Side effects: watch is typically used for performing side effects, like making API calls, updating local storage, or triggering animations.

Why use watch?

watch is ideal for scenarios where you need to:

  • Make an API call when a specific value changes.
  • Update local storage when a user saves their settings.
  • Trigger an animation when a component becomes visible.
  • Validate form input as the user types.

Example Table: watch in Action

Scenario Code Snippet Explanation
Logging changes to a user’s name const userName = ref('Default'); watch(userName, (newValue, oldValue) => console.log(Name changed from ${oldValue} to ${newValue})) Logs a message to the console whenever the userName ref changes.
Making an API call on search term change const searchTerm = ref(''); watch(searchTerm, (newTerm) => { if (newTerm.length > 2) { fetch(/api/search?q=${newTerm}) } }) Makes an API call to search for results whenever the searchTerm ref changes and is longer than 2 characters.
Updating a component’s style based on a prop const colorProp = ref('red'); watch(colorProp, (newColor) => { document.body.style.backgroundColor = newColor }) Changes the background color of the body element whenever the colorProp ref changes. This demonstrates watching a prop value.
Deep Watching an Object const myObject = reactive({a: 1, b: 2}); watch(() => myObject, (newValue, oldValue) => console.log('object changed'), {deep: true}) Using {deep: true} allows you to watch for changes within the properties of a reactive object, not just a replacement of the entire object.

VI. toRefs: The Destructuring Savior (The Decryption Key)

Remember how I said you shouldn’t destructure a reactive object directly? Well, that’s because destructuring breaks reactivity. But fear not! toRefs is here to save the day!

toRefs converts a reactive object into a plain object where each property is a ref pointing to the corresponding property in the original reactive object. This allows you to destructure the object while maintaining reactivity.

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

  const user = reactive({
    firstName: 'John',
    lastName: 'Doe'
  })

  const { firstName, lastName } = toRefs(user)

  const updateFirstName = () => {
    firstName.value = 'Jane' // Need .value here!
  }
</script>

<template>
  <view>
    <p>First Name: {{ firstName }}</p>
    <p>Last Name: {{ lastName }}</p>
    <button @click="updateFirstName">Update First Name</button>
  </view>
</template>

Key takeaways about toRefs:

  • Purpose: Allows you to destructure a reactive object while maintaining reactivity.
  • Conversion to ref: Each property in the returned object is a ref.
  • Usage: Ideal for passing reactive data to child components as props.

Why use toRefs?

Imagine you have a complex reactive object with many properties, and you only need to pass a few of those properties to a child component. Using toRefs allows you to selectively expose those properties without sacrificing reactivity.

VII. Putting it All Together: A Grand Finale! 🎶

Let’s create a simple counter component that utilizes all four core concepts: ref, reactive, computed, and watch.

<template>
  <view>
    <h1>Counter Component</h1>
    <p>Counter: {{ count }}</p>
    <button @click="increment">Increment</button>
    <button @click="decrement">Decrement</button>
    <p>Is Even: {{ isEven }}</p>
    <input type="text" v-model="message" />
    <p>Message Length: {{ messageLength }}</p>
  </view>
</template>

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

  // Ref for the counter value
  const count = ref(0)

  // Reactive object for settings
  const settings = reactive({
    incrementAmount: 1,
    decrementAmount: 1
  })

  // Computed property to check if the counter is even
  const isEven = computed(() => count.value % 2 === 0)

  // Method to increment the counter
  const increment = () => {
    count.value += settings.incrementAmount
  }

  // Method to decrement the counter
  const decrement = () => {
    count.value -= settings.decrementAmount
  }

  // Ref for the message
  const message = ref('')

  // Computed property for message length
  const messageLength = computed(() => message.value.length)

  // Watcher to log the counter value when it changes
  watch(count, (newCount, oldCount) => {
    console.log(`Counter changed from ${oldCount} to ${newCount}`)
  })
</script>

Explanation:

  • We use ref for the count and message variables, as they are simple primitive values.
  • We use reactive for the settings object, as it contains multiple properties that might change.
  • We use computed for isEven and messageLength, as they are derived values based on other reactive data.
  • We use watch to log the counter value whenever it changes.

VIII. Beyond the Basics: Advanced Techniques and Tips

  • shallowRef and shallowReactive: These variants create shallowly reactive objects. Only the top-level properties are reactive, not nested objects. This can improve performance if you have large, complex objects where you don’t need deep reactivity.
  • readonly and shallowReadonly: These functions create read-only versions of reactive objects. This prevents accidental modifications.
  • triggerRef: Manually trigger a ref update. Use with caution! (Usually not needed)
  • customRef: Create your own reactive primitive with fine-grained control! For advanced use cases.

IX. Common Mistakes to Avoid

  • Forgetting .value with ref: This is the most common mistake! Remember to use .value to access and modify the value of a ref.
  • Replacing reactive objects: Don’t replace a reactive object with a new object. Instead, update the properties within the existing object.
  • Mutating reactive arrays directly: Use array methods like push, pop, splice, etc., to modify reactive arrays. Avoid directly assigning a new array to the reactive array variable.
  • Overusing watch: watch is powerful, but it can also lead to performance issues if used excessively. Consider using computed for derived values whenever possible.

X. Conclusion: Go Forth and Compose! 🚀

Congratulations! You’ve made it through this whirlwind tour of the Vue.js Composition API in UniApp. You’ve learned about ref, reactive, computed, and watch, and you’re now equipped to build more organized, reusable, and maintainable Vue components.

Remember, practice makes perfect! Experiment with these concepts in your own UniApp projects, and don’t be afraid to make mistakes. That’s how you learn!

Now go forth and compose! And remember, keep it fun! 😄

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *