Vue学习笔记:计算属性

计算属性

  • 入门
  • 进阶
  • 二次进阶
  • 三次进阶
  • 四次进阶
  • 结界
  • 五次进阶
  • 六次进阶
  • 七次进阶
  • 八次进阶
  • 九次进阶
  • 终章
  • 彩蛋

入门

Vue.js中,计算属性示例:

export default {
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe'
    };
  },
  computed: {
    // 计算属性:全名
    fullName() {
      return this.firstName + ' ' + this.lastName;
    }
  }
}

在这个例子中,fullName是一个计算属性,它基于数据属性(data):firstNamelastName,进行计算。每当firstNamelastName发生变化时,Vue会自动重新计算并更新fullName的值。在模板中你只需引用{{fullName}}即可显示结果。

这个例子是最基础的计算属性实现,代码量最小且直观展示了计算属性的基本用法。

进阶

稍微进阶的示例:涉及更复杂的计算逻辑和条件判断

export default {
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe',
      isFormal: false // 是否显示正式称呼
    };
  },
  computed: {
    // 计算属性:全名(根据是否正式展示不同的称呼)
    fullName() {
      if (this.isFormal) {
        return `${this.firstName.charAt(0).toUpperCase()}${this.firstName.slice(1)}. ${this.lastName.toUpperCase()}`;
      } else {
        return `${this.firstName} ${this.lastName}`;
      }
    }
  }
}

在这个例子中,fullName计算属性会根据isFormal数据属性的值来决定返回全名的不同格式。当isFormaltrue时,它将返回姓氏大写且名字仅显示首字母大写的正式称呼;否则,返回正常的全名。在模板中通过{{fullName}}引用该计算属性即可。

二次进阶

一个更进阶的Vue.js计算属性示例:涉及到对复杂对象或数组的处理,以及多个数据依赖项:

export default {
  data() {
    return {
      user: {
        firstName: 'John',
        lastName: 'Doe',
        addresses: [
          { type: 'home', street: '123 Main St' },
          { type: 'work', street: '456 Elm St' }
        ]
      },
      currentAddressType: 'home'
    };
  },
  computed: {
    // 计算属性:当前地址全称
    currentAddress() {
      const selectedAddress = this.user.addresses.find(address => address.type === this.currentAddressType);
      if (selectedAddress) {
        return `${this.user.firstName} ${this.user.lastName}'s ${this.currentAddressType} Address: ${selectedAddress.street}`;
      } else {
        return 'No address found';
      }
    }
  }
}

在这个例子中,currentAddress计算属性基于user对象中的firstNamelastNameaddresses数组,以及currentAddressType数据属性进行计算。它会查找与currentAddressType匹配的地址,并返回包含用户姓名和所选地址类型的完整地址信息。当模板中引用{{currentAddress}}时,会根据这些数据的变化自动更新显示内容。

三次进阶

更进一步的计算属性示例:涉及嵌套对象的深度计算和条件过滤

export default {
  data() {
    return {
      users: [
        { id: 1, name: 'John Doe', age: 30, roles: ['admin', 'user'] },
        { id: 2, name: 'Jane Smith', age: 25, roles: ['user'] },
        // 更多用户...
      ],
      searchName: '',
      roleFilter: ['admin']
    };
  },
  computed: {
    // 计算属性:过滤后的用户列表
    filteredUsers() {
      return this.users.filter(user => 
        user.name.toLowerCase().includes(this.searchName.toLowerCase()) &&
        (this.roleFilter.length === 0 || this.roleFilter.some(role => user.roles.includes(role)))
      );
    }
  }
}

在这个例子中,filteredUsers计算属性基于users数组、searchNameroleFilter数据属性进行计算。它首先通过filter()函数对用户列表进行过滤,满足以下两个条件的用户会被保留:

  1. 用户的名字包含搜索关键字(不区分大小写)。
  2. 如果roleFilter有值,则用户的角色列表需至少包含其中一个角色。

模板中引用{{filteredUsers}}时,会根据userssearchNameroleFilter的变化自动更新显示内容。

四次进阶

高阶的Vue.js计算属性示例:涉及到对异步数据处理,例如使用Promise或async/await

<template>
  <div>
    <!-- ... -->
    {{ formattedUser }}
  </div>
</template>

<script>
export default {
  data() {
    return {
      userId: null,
      user: {}
    };
  },
  computed: {
    // 计算属性:格式化用户信息(假设从API获取)
    async formattedUser() {
      try {
        if (!this.user.id && this.userId) {
          const response = await fetch(`/api/users/${this.userId}`);
          const userData = await response.json();
          this.$set(this.user, 'id', userData.id);
          this.$set(this.user, 'name', `${userData.firstName} ${userData.lastName}`);
          // 其他属性...
        }
        return `${this.user.name} (${this.user.id})`;
      } catch (error) {
        console.error('Error fetching user:', error);
        return '无法获取用户信息';
      }
    }
  },
  watch: {
    userId(newVal) {
      if (newVal) {
        // 当userId变化时,触发formattedUser的重新计算
        this.formattedUser;
      }
    }
  }
};
</script>

在这个例子中,formattedUser是一个异步计算属性,它会根据userId的变化去异步获取并格式化用户信息。这里使用了async/await和fetch API来模拟从服务器获取用户数据的过程。由于计算属性不支持直接返回Promise,所以我们需要在计算属性内部等待异步操作完成。

同时,通过在watch中监听userId的变化,并触发formattedUser的重新计算,确保当用户ID改变时能及时更新显示内容。注意,Vue并不会自动等待异步计算属性的结果,因此需要在模板中结合v-if等条件渲染或者在watch中进行处理以确保正确渲染结果。

结界

来到四阶的你,对于只负责处理前端,应该已经是够用了。接下来,就是精进的道路。如果还有兴趣,想要更加深入,欢迎继续下去。

五次进阶

涉及到更复杂的业务逻辑和数据处理,比如使用Vuex管理状态并根据多个store中的值进行计算:

import { mapState } from 'vuex';

export default {
  computed: {
    ...mapState(['user', 'settings']),
    // 计算属性:根据用户权限和应用设置决定是否显示特定功能
    showAdvancedFeatures() {
      return this.user.isAdmin && this.settings.showAdvancedOptions;
    },
    // 计算属性:处理复杂对象结构,并返回格式化后的数据
    formattedUserData() {
      if (this.user && this.user.data) {
        const { firstName, lastName, posts = [] } = this.user.data;
        const recentPost = posts.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))[0];
        return `${firstName} ${lastName}'s recent post: "${recentPost.title}"`;
      }
      return 'No user data available';
    }
  }
}

在这个例子中:

  1. 使用mapState帮助器从Vuex store获取usersettings状态。
  2. showAdvancedFeatures计算属性基于用户是否为管理员以及应用设置来确定是否显示高级功能。
  3. formattedUserData计算属性则处理嵌套的对象结构,提取用户的名字、最近一篇博客的标题(按创建时间排序),并返回格式化后的字符串。如果用户数据不存在,则返回提示信息。

这些计算属性会自动响应关联的Vuex store状态变化,确保视图内容始终是最新的。

六次进阶

使用计算属性进行性能优化,如利用缓存结果和依赖追踪:

export default {
  data() {
    return {
      largeList: [...], // 假设这是一个非常大的数组
      filterKeyword: ''
    };
  },
  computed: {
    // 使用getter和setter实现带有缓存功能的复杂过滤逻辑
    filteredLargeList: {
      get() {
        if (this._cachedFilteredList && this.filterKeyword === this._lastFilterKeyword) {
          return this._cachedFilteredList;
        }
        
        const keyword = this.filterKeyword.toLowerCase();
        this._cachedFilteredList = this.largeList.filter(item => 
          item.title.toLowerCase().includes(keyword) ||
          item.description.toLowerCase().includes(keyword)
        );
        this._lastFilterKeyword = this.filterKeyword;

        return this._cachedFilteredList;
      },
      set(newList) {
        // 这里假设你允许通过外部直接设置过滤后的列表,但通常不推荐这样做
        this._cachedFilteredList = newList;
      }
    }
  },
  created() {
    // 初始化缓存变量
    this._cachedFilteredList = [];
    this._lastFilterKeyword = '';
  }
}

在这个例子中:

  1. filteredLargeList是一个具有getter和setter方法的计算属性。
  2. getter方法首先检查是否存在缓存(_cachedFilteredList)并且当前的过滤关键词是否与上次相同(_lastFilterKeyword)。如果满足条件,则直接返回缓存的结果,避免了对大型数据集的重复过滤操作,从而提高性能。
  3. 如果过滤关键词发生变化,计算属性会重新过滤数据,并更新缓存和关键词记录。
  4. setter方法用于在特殊情况下允许外部直接设置过滤后的列表,但这通常不是计算属性的标准用法。在大多数场景下,我们只需关注getter部分即可。

这个计算属性结合了响应式依赖追踪、业务逻辑处理以及性能优化策略,展示了Vue.js计算属性在实际项目中的高级应用。

七次进阶

涉及利用计算属性进行深度监听和数据转换,同时结合使用Vuex:

<template>
  <div>
    <!-- ... -->
    {{ formattedAndFilteredItems }}
  </div>
</template>

<script>
import { mapState, mapGetters } from 'vuex';

export default {
  computed: {
    // 使用mapState获取store中的原始items
    ...mapState(['items']),
    
    // 使用mapGetters获取全局的filterKeyword和formattingOptions
    ...mapGetters(['filterKeyword', 'formattingOptions']),

    // 计算属性:深度监听并过滤、格式化items
    formattedAndFilteredItems() {
      const filteredItems = this.items.filter(item => 
        item.title.toLowerCase().includes(this.filterKeyword.toLowerCase())
      );

      return filteredItems.map(item => ({
        ...item,
        title: this.applyFormatting(item.title),
        description: this.applyFormatting(item.description)
      }));
    },

    // 计算属性方法:根据formattingOptions对文本进行格式化
    applyFormatting(text) {
      if (!text || !this.formattingOptions) return text;
      
      let formattedText = text;
      
      // 根据不同的formattingOptions对文本进行处理
      if (this.formattingOptions.uppercase) formattedText = formattedText.toUpperCase();
      if (this.formattingOptions.lowercase) formattedText = formattedText.toLowerCase();
      if (this.formattingOptions.capitalize) formattedText = formattedText.replace(/\b\w/g, l => l.toUpperCase());
      
      return formattedText;
    }
  }
};
</script>

在这个例子中:

  1. 使用mapState从Vuex store获取items数组。
  2. 使用mapGetters从Vuex store获取全局的过滤关键词(filterKeyword)和格式化选项(formattingOptions)。
  3. formattedAndFilteredItems计算属性首先基于filterKeyword过滤items数组,并对过滤后的结果应用applyFormatting函数来格式化每个项目的标题和描述字段。
  4. applyFormatting是一个计算属性方法,它根据formattingOptions动态地将文本转换为大写、小写或首字母大写。

这个计算属性不仅实现了深度监听、过滤以及复杂的数据转换,还展示了如何在组件内部与Vuex状态管理器紧密协作。

八次进阶

涉及动态计算CSS样式、响应式布局以及与第三方库的交互:

<template>
  <div :style="dynamicStyles">
    
  div>
template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      windowWidth: window.innerWidth,
      theme: null,
      customFontSize: 16,
    };
  },
  computed: {
    // 计算属性:根据窗口宽度和主题动态生成CSS样式
    dynamicStyles() {
      return {
        color: this.theme ? `var(--${this.theme}-text)` : '#333',
        fontSize: `${this.customFontSize}px`,
        width: `${this.windowWidth}px`,
        transform: `scale(${this.calculateScaleFactor()})`,
      };
    },

    // 计算属性:根据用户设置和窗口大小计算缩放因子
    calculateScaleFactor() {
      if (this.windowWidth > 1200) {
        return 1;
      } else if (this.windowWidth <= 600) {
        return 0.8;
      } else {
        return 0.95 + (this.windowWidth - 600) * 0.01 / 600;
      }
    },
    
    // 计算属性:从API获取并解析主题信息
    async currentTheme() {
      if (!this.theme) {
        try {
          const { data } = await axios.get('/api/current-theme');
          this.$set(this, 'theme', data.name);
        } catch (error) {
          console.error('Error fetching theme:', error);
        }
      }
      return this.theme;
    },
  },
  mounted() {
    // 监听窗口大小变化,实时更新windowWidth
    window.addEventListener('resize', () => {
      this.windowWidth = window.innerWidth;
    });
  },
};
script>

在这个例子中:

  1. dynamicStyles计算属性根据窗口宽度、当前主题和用户自定义字体大小生成CSS样式对象。
  2. calculateScaleFactor计算属性基于窗口宽度返回一个动态缩放因子,用于响应式布局调整。
  3. currentTheme是一个异步计算属性,它会从API获取当前主题,并在获取成功后将其设置到组件的本地状态。由于计算属性不支持直接返回Promise,因此这里使用了 $set 方法来更新数据。

此外,还监听了窗口大小变化事件以确保windowWidth始终是最新的值,进而影响到依赖它的计算属性结果。这个示例展示了计算属性如何在实际项目中进行复杂的数据处理、样式计算和API交互。

九次进阶

涉及到高级组件设计、Vuex状态管理、自定义指令、高级路由和过渡动画等。以下是一个包含这些概念的示例:

<template>
  <div id="app">
    
    <div :class="themeClass" @click="toggleTheme">
      主题切换({{ theme }})
    <div>

    
    <transition :name="transitionName">
      <router-view :key="$route.fullPath">router-view>
    transition>

    
    <p v-fadeInOut>这个文本会淡入淡出p>
  div>
template>

<script>
import { mapState } from 'vuex';
import axios from 'axios';
import FadeInOut from '@/directives/FadeInOut';

export default {
  directives: {
    // 注册自定义指令
    fadeInOut: FadeInOut,
  },
  computed: {
    ...mapState(['currentTheme']), // 从Vuex中映射当前主题状态

    // 根据主题计算CSS类名
    themeClass() {
      return this.currentTheme === 'light' ? 'theme-light' : 'theme-dark';
    },

    // 根据路由变化设置过渡动画名称
    transitionName() {
      return this.$route.meta.transition || 'fade';
    },
  },
  methods: {
    async toggleTheme() {
      const newTheme = this.currentTheme === 'light' ? 'dark' : 'light';
      try {
        await axios.post('/api/theme', { theme: newTheme });
        this.$store.commit('UPDATE_THEME', newTheme);
      } catch (error) {
        console.error('Error toggling theme:', error);
      }
    },
  },
};
script>

<style scoped>
.theme-light { background-color: #fff; color: #333; }
.theme-dark { background-color: #333; color: #fff; }

/* 过渡动画样式 */
.fade-enter-active, .fade-leave-active {
  transition: opacity .5s;
}
.fade-enter, .fade-leave-to {
  opacity: 0;
}
style>

在这个例子中:

  1. 我们引入了Vuex并使用mapState帮助器来获取存储在全局状态管理中的当前主题。
  2. 定义了一个计算属性themeClass,根据当前主题动态地为元素添加相应的CSS类名。
  3. transitionName计算属性根据路由元信息设置页面切换时的过渡动画效果。
  4. 自定义指令v-fadeInOut用于控制文本内容的淡入淡出效果。
  5. 在方法toggleTheme中,我们通过调用API更新服务器端的主题,并且同步修改 Vuex 中的状态。

此外,通过观察$route对象的变化来实现路由过渡动画,并在点击事件中处理主题切换逻辑,展示了如何将多个Vue.js进阶特性整合到一起以构建一个更复杂的应用程序结构。

终章

如果你能掌握到这里,就算是踏入了新世界的大门。

进一步进阶Vue.js开发,我们可以探讨更复杂的场景和最佳实践,包括:

  1. 模块化和项目架构优化

    • 使用Vue CLI 3或4创建并配置多页面应用(MPA)或单页面应用(SPA)。
    • 按功能拆分组件,并组织成合理的目录结构,如原子组件、分子组件和容器组件等。
    • 引入模块化工具如Webpack或Vite进行资源管理和打包。
  2. 异步数据处理与API调用

    • 利用Vue Router的导航守卫(Navigation Guards)在路由切换时处理异步数据加载。
    • 结合axios或其他HTTP库实现API请求封装,支持拦截器、错误处理和响应式数据绑定。
    • 使用async/await或Promise配合Vue生命周期钩子进行数据预取和懒加载。
  3. 高级状态管理

    • 在大型应用中深度使用Vuex,设计模块化的store结构,利用getters、mutations和actions来管理全局状态。
    • 集成中间件(middleware)来监控action调用过程,实现日志记录、权限校验等功能。
  4. 性能优化

    • 使用Vue的v-ifv-show控制条件渲染,避免不必要的DOM操作。
    • 对于大量数据列表,采用虚拟滚动技术提高性能。
    • 利用Vue的keep-alive缓存组件实例以避免重复渲染。
    • 开启代码分割和动态导入减少初始加载时间。
  5. 测试驱动开发

    • 使用Jest、Mocha等单元测试框架结合Vue Test Utils编写组件单元测试。
    • 配合Selenium等工具进行端到端(E2E)测试,确保应用整体流程的正确性。
  6. 国际化和本地化

    • 应用Vue I18n插件实现多语言支持,根据用户偏好动态更改界面文字内容。
  7. SSR(服务器端渲染)和静态生成

    • 针对SEO友好和首屏加载速度要求高的场景,可以采用Nuxt.js搭建Vue SSR应用或者Next.js实现静态站点生成。
  8. Web Workers和Service Worker

    • 使用Web Workers将CPU密集型任务迁移到后台线程以改善用户体验。
    • 实现PWA特性,利用Service Worker缓存关键资源,使应用具备离线访问能力。
  9. 可复用的设计模式和最佳实践

    • 使用Composition API(Vue 3引入)重构逻辑,提升代码可读性和复用性。
    • 探索组合式API中的依赖注入、自定义渲染函数以及Suspense用于异步组件加载。

这些进阶实践能够构建出更为复杂、高性能且易于维护的Vue.js应用程序。

彩蛋

进阶实践不仅限于上述内容,还可以深入到更专业的领域和技术栈整合中。以下是进一步的进阶主题:

  1. Vue 3特性深入

    • 熟悉并掌握Composition API(组合式API)以替代Options API进行逻辑组织和复用。
    • 使用Teleport实现跨组件定位DOM元素。
    • 利用Suspense实现异步组件的高级加载策略。
    • 学习如何使用自定义渲染器拓展Vue应用至WebGL、Canvas或其他非DOM环境。
  2. TypeScript集成

    • 将TypeScript与Vue.js结合,利用类型系统提高代码质量及开发效率,编写强类型组件。
    • 配置Vue项目的TS配置文件,如shims-vue.d.tstsconfig.json
  3. 状态管理库升级

    • 探索Pinia(Vue 3推荐的状态管理库),它基于Composition API设计,提供更简洁直观的API来管理复杂状态。
  4. UI框架深度定制

    • 对流行的UI库如Element UI(Vue 2)或Vuetify、Quasar、Ant Design Vue(针对Vue 3)进行深度定制和二次开发。
  5. 微前端架构

    • 在大型企业级项目中采用微前端技术,比如通过Single-Spa或Qiankun将多个Vue应用集成在一个主壳应用内。
  6. Serverless SSR

    • 结合云服务提供的Serverless函数服务,构建无服务器端渲染解决方案,降低运维成本和提升资源利用率。
  7. DevOps工具链整合

    • CI/CD流程自动化,包括Git Hooks、GitHub Actions或Jenkins等持续集成/部署工具,实现自动化测试、打包和发布。
  8. 错误监控与性能分析

    • 集成Sentry、Bugsnag等错误追踪工具,实时收集线上错误信息并优化问题修复流程。
    • 利用Lighthouse、Webpack Bundle Analyzer等工具分析和优化应用性能。
  9. 移动端开发与响应式设计

    • 掌握Vue在移动端项目中的最佳实践,结合Vue Touch、Hammer.js处理手势交互。
    • 设计和实施自适应布局,确保应用在不同屏幕尺寸和设备上具有良好的用户体验。
  10. GraphQL集成

    • 将Vue应用与GraphQL后端服务对接,使用Apollo Client等库处理GraphQL查询、订阅以及缓存机制。

这些更为复杂的进阶实践能够应对更多挑战,构建出更加现代化、可扩展且高效率的Vue.js应用程序。

你可能感兴趣的:(vue.js,学习,笔记)