MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Vue 2与Vue 3 Composition API增强代码组织能力的实际应用

2024-02-104.6k 阅读

Vue 2与Vue 3 Composition API增强代码组织能力的实际应用

Vue 2的代码组织方式回顾

在Vue 2中,我们主要通过Options API来组织代码。每个Vue组件都是一个包含各种选项的对象,例如datamethodscomputedwatch等。

data选项

data选项用于定义组件的数据。它是一个函数,返回一个对象,对象中的属性就是组件的响应式数据。

<template>
  <div>
    <p>{{ message }}</p>
    <button @click="updateMessage">Update Message</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello, Vue 2!'
    };
  },
  methods: {
    updateMessage() {
      this.message = 'Message updated!';
    }
  }
};
</script>

在这个例子中,message是定义在data中的响应式数据,updateMessage方法可以修改message的值,Vue会自动更新DOM。

methods选项

methods选项定义了组件的方法。这些方法可以在模板中通过@语法绑定到事件上,也可以在组件的其他方法中调用。

<template>
  <div>
    <input v-model="inputValue">
    <button @click="logInput">Log Input</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      inputValue: ''
    };
  },
  methods: {
    logInput() {
      console.log(this.inputValue);
    }
  }
};
</script>

这里logInput方法打印出inputValue的值,inputValuedata中的数据。

computed选项

computed选项用于定义计算属性。计算属性是基于其他响应式数据的派生数据,并且会缓存计算结果,只有当依赖的数据变化时才会重新计算。

<template>
  <div>
    <p>First Name: <input v-model="firstName"></p>
    <p>Last Name: <input v-model="lastName"></p>
    <p>Full Name: {{ fullName }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      firstName: '',
      lastName: ''
    };
  },
  computed: {
    fullName() {
      return this.firstName + ' ' + this.lastName;
    }
  }
};
</script>

fullName是一个计算属性,它依赖于firstNamelastName,当这两个数据变化时,fullName会重新计算。

watch选项

watch选项用于监听数据的变化,并在数据变化时执行相应的操作。

<template>
  <div>
    <input v-model="count">
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    };
  },
  watch: {
    count(newValue, oldValue) {
      console.log(`Count changed from ${oldValue} to ${newValue}`);
    }
  }
};
</script>

count的值发生变化时,watch中的回调函数会被执行,打印出变化前后的值。

然而,随着组件功能的增加,Options API的代码组织方式可能会出现一些问题。例如,当不同的逻辑关注点(如数据获取、表单验证等)在datamethodscomputed等选项中分散时,代码的维护和理解变得困难。而且,复用逻辑也相对复杂,通常需要通过Mixin来实现,但Mixin也存在命名冲突等问题。

Vue 3 Composition API简介

Vue 3引入了Composition API,它以函数的形式提供了一套更灵活、更强大的代码组织方式。Composition API基于Reactivity API,通过组合函数来组织相关的逻辑。

setup函数

setup函数是Composition API的入口点,它在组件创建之前执行,datamethods等选项还未被解析。setup函数接收两个参数:propscontext

<template>
  <div>
    <p>{{ message }}</p>
    <button @click="updateMessage">Update Message</button>
  </div>
</template>

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

export default {
  setup() {
    const message = ref('Hello, Vue 3!');
    const updateMessage = () => {
      message.value = 'Message updated!';
    };
    return {
      message,
      updateMessage
    };
  }
};
</script>

在这个例子中,ref函数创建了一个响应式变量messageupdateMessage函数可以修改message的值。通过returnmessageupdateMessage暴露给模板使用。

reactive函数

reactive函数用于创建一个响应式对象。与ref不同,ref创建的是单个值的响应式,而reactive用于创建复杂对象的响应式。

<template>
  <div>
    <p>{{ user.name }} is {{ user.age }} years old.</p>
    <button @click="updateUser">Update User</button>
  </div>
</template>

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

export default {
  setup() {
    const user = reactive({
      name: 'John',
      age: 30
    });
    const updateUser = () => {
      user.name = 'Jane';
      user.age = 31;
    };
    return {
      user,
      updateUser
    };
  }
};
</script>

这里user是一个通过reactive创建的响应式对象,updateUser函数可以修改user的属性,Vue会自动更新DOM。

computed函数

computed函数在Composition API中用于创建计算属性,与Vue 2中的computed选项类似,但使用方式有所不同。

<template>
  <div>
    <p>First Name: <input v-model="firstName"></p>
    <p>Last Name: <input v-model="lastName"></p>
    <p>Full Name: {{ fullName }}</p>
  </div>
</template>

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

export default {
  setup() {
    const firstName = ref('');
    const lastName = ref('');
    const fullName = computed(() => {
      return firstName.value + ' ' + lastName.value;
    });
    return {
      firstName,
      lastName,
      fullName
    };
  }
};
</script>

computed函数接收一个函数作为参数,返回一个只读的计算属性。只有当firstNamelastName变化时,fullName才会重新计算。

watch函数

watch函数在Composition API中用于监听响应式数据的变化。它有多种用法,例如监听单个数据或多个数据。

<template>
  <div>
    <input v-model="count">
  </div>
</template>

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

export default {
  setup() {
    const count = ref(0);
    watch(count, (newValue, oldValue) => {
      console.log(`Count changed from ${oldValue} to ${newValue}`);
    });
    return {
      count
    };
  }
};
</script>

这里watch函数监听count的变化,当count的值改变时,回调函数会被执行。

Vue 3 Composition API增强代码组织能力的实际应用

逻辑复用

在Vue 2中,复用逻辑通常使用Mixin,但Mixin存在命名冲突等问题。在Vue 3中,Composition API可以通过组合函数轻松实现逻辑复用。

假设我们有一个需求,多个组件都需要获取当前用户的信息,并在用户信息变化时执行一些操作。我们可以创建一个组合函数useUser

import { ref, watch } from 'vue';

export const useUser = () => {
  const user = ref({
    name: '',
    age: 0
  });
  const fetchUser = () => {
    // 模拟异步获取用户信息
    setTimeout(() => {
      user.value = {
        name: 'Alice',
        age: 25
      };
    }, 1000);
  };
  watch(user, (newUser, oldUser) => {
    console.log(`User changed from ${oldUser.name} to ${newUser.name}`);
  });
  return {
    user,
    fetchUser
  };
};

然后在组件中使用这个组合函数。

<template>
  <div>
    <p>{{ user.name }} is {{ user.age }} years old.</p>
    <button @click="fetchUser">Fetch User</button>
  </div>
</template>

<script>
import { useUser } from './useUser';

export default {
  setup() {
    const { user, fetchUser } = useUser();
    return {
      user,
      fetchUser
    };
  }
};
</script>

这样,多个组件都可以复用useUser组合函数中的逻辑,而且不会出现命名冲突等问题。

代码分割与组织

随着组件功能的增加,Options API中的代码可能会变得非常冗长和难以维护。Composition API可以将不同的逻辑关注点分割成不同的组合函数,使代码更加清晰。

例如,一个表单组件可能有数据验证、提交等逻辑。我们可以将这些逻辑分别封装到不同的组合函数中。

import { ref, watch } from 'vue';

// 数据验证组合函数
export const useFormValidation = () => {
  const email = ref('');
  const isValidEmail = computed(() => {
    const re = /\S+@\S+\.\S+/;
    return re.test(email.value);
  });
  watch(email, () => {
    if (!isValidEmail.value) {
      console.log('Invalid email');
    }
  });
  return {
    email,
    isValidEmail
  };
};

// 表单提交组合函数
export const useFormSubmission = () => {
  const isSubmitted = ref(false);
  const submitForm = () => {
    isSubmitted.value = true;
    console.log('Form submitted');
  };
  return {
    isSubmitted,
    submitForm
  };
};

然后在表单组件中使用这些组合函数。

<template>
  <div>
    <input v-model="email" placeholder="Email">
    <p v-if="!isValidEmail">Invalid email</p>
    <button @click="submitForm" :disabled="!isValidEmail">Submit</button>
    <p v-if="isSubmitted">Form has been submitted</p>
  </div>
</template>

<script>
import { useFormValidation, useFormSubmission } from './formComposables';

export default {
  setup() {
    const { email, isValidEmail } = useFormValidation();
    const { isSubmitted, submitForm } = useFormSubmission();
    return {
      email,
      isValidEmail,
      isSubmitted,
      submitForm
    };
  }
};
</script>

通过这种方式,不同的逻辑被清晰地分割开,提高了代码的可维护性和可读性。

与TypeScript结合

Vue 3 Composition API与TypeScript结合得非常好,能够提供更好的类型检查和代码提示。

例如,我们可以给useUser组合函数添加类型定义。

import { ref, watch, Ref } from 'vue';

interface User {
  name: string;
  age: number;
}

export const useUser = (): { user: Ref<User>, fetchUser: () => void } => {
  const user = ref<User>({
    name: '',
    age: 0
  });
  const fetchUser = () => {
    // 模拟异步获取用户信息
    setTimeout(() => {
      user.value = {
        name: 'Bob',
        age: 35
      };
    }, 1000);
  };
  watch(user, (newUser, oldUser) => {
    console.log(`User changed from ${oldUser.name} to ${newUser.name}`);
  });
  return {
    user,
    fetchUser
  };
};

在组件中使用时,TypeScript会根据定义的类型提供准确的代码提示。

<template>
  <div>
    <p>{{ user.name }} is {{ user.age }} years old.</p>
    <button @click="fetchUser">Fetch User</button>
  </div>
</template>

<script lang="ts">
import { useUser } from './useUser';

export default {
  setup() {
    const { user, fetchUser } = useUser();
    return {
      user,
      fetchUser
    };
  }
};
</script>

这使得代码更加健壮,减少了潜在的错误。

从Vue 2迁移到Vue 3 Composition API的注意事项

数据定义与访问

在Vue 2中,data中的数据直接通过this访问,而在Vue 3中,ref创建的数据需要通过.value访问,reactive创建的对象可以直接访问属性。

<!-- Vue 2 -->
<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello'
    };
  }
};
</script>
<!-- Vue 3 -->
<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

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

export default {
  setup() {
    const message = ref('Hello');
    return {
      message
    };
  }
};
</script>

在模板中虽然看起来一样,但在逻辑代码中,Vue 3需要message.value来访问值。

生命周期钩子

Vue 2中的生命周期钩子在Vue 3中有了新的写法。例如,created钩子在Vue 3中可以通过onMountedonUpdated等函数来实现。

<!-- Vue 2 -->
<template>
  <div>
    <p>Component mounted</p>
  </div>
</template>

<script>
export default {
  created() {
    console.log('Component created');
  }
};
</script>
<!-- Vue 3 -->
<template>
  <div>
    <p>Component mounted</p>
  </div>
</template>

<script>
import { onMounted } from 'vue';

export default {
  setup() {
    onMounted(() => {
      console.log('Component mounted');
    });
  }
};
</script>

需要注意不同生命周期钩子在Vue 3中的对应函数。

模板引用

在Vue 2中,我们通过ref属性在模板中创建引用,然后通过this.$refs访问。在Vue 3中,使用ref函数结合templateRef来实现。

<!-- Vue 2 -->
<template>
  <div>
    <input ref="inputRef">
    <button @click="focusInput">Focus Input</button>
  </div>
</template>

<script>
export default {
  methods: {
    focusInput() {
      this.$refs.inputRef.focus();
    }
  }
};
</script>
<!-- Vue 3 -->
<template>
  <div>
    <input ref="inputRef">
    <button @click="focusInput">Focus Input</button>
  </div>
</template>

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

export default {
  setup() {
    const inputRef = ref(null);
    const focusInput = () => {
      if (inputRef.value) {
        inputRef.value.focus();
      }
    };
    return {
      inputRef,
      focusInput
    };
  }
};
</script>

这种变化需要在迁移时进行调整。

总结

Vue 3 Composition API为前端开发者提供了一种更灵活、更强大的代码组织方式。通过组合函数,我们可以更好地复用逻辑、分割代码,并且与TypeScript结合得更加紧密。从Vue 2迁移到Vue 3 Composition API需要注意一些语法和概念上的变化,但一旦掌握,将大大提升开发效率和代码的可维护性。无论是开发小型项目还是大型应用,Composition API都能为代码组织带来显著的提升。在实际开发中,我们应该根据项目的需求和团队的技术栈,合理地选择和使用Vue 2的Options API或Vue 3的Composition API,以达到最佳的开发效果。

希望通过以上内容,你对Vue 2与Vue 3 Composition API增强代码组织能力的实际应用有了更深入的理解和认识,能够在项目中灵活运用这些知识,打造出高质量的前端应用。在实际开发过程中,不断实践和探索,总结经验,以更好地发挥Vue的强大功能。