vue3 -- 通过简单示例,聊一聊Composition API

在 vue3 版本之前,我们复用组件(或者提取和重用多个组件之间的逻辑),通常有以下几种方式:

  • Mixin:命名空间冲突 & 渲染上下文中暴露的 property 来源不清晰。例如在阅读一个运用了多个 mixin 的模板时,很难看出某个 property 是从哪一个 mixin 中注入的。
  • Renderless Component:无渲染组件需要额外的有状态的组件实例,从而使得性能有所损耗
  • Vuex:就会变得更加复杂,需要去定义 Mutations 也需要去定义 Actions

上述提到的几种方式,也是我们项目中正在使用的方式。对于提取和重用多个组件之间的逻辑似乎并不简单。我们甚至采用了 extend 来做到最大化利用已有组件逻辑,因此使得代码逻辑依赖严重,难以阅读和理解。

Vue3 中的 Composition API 便是解决这一问题;且完美支持类型推导,不再是依靠一个简单的 this 上下文来暴露 property(比如 methods 选项下的函数的 this 是指向组件实例的,而不是这个 methods 对象)。其是一组低侵入式的、函数式的 API,使得我们能够更灵活地「组合」组件的逻辑。

使用 Vue2 和 Vue3 开发组件有很大的差异性:

  • Vue2 开发组件,我们往往通过选项类型组织代码;props => ui state => 事件
  • Vue3 Composition Api 开发组件,基于逻辑关注点组织代码;(响应式)参数 => 生命周期绑定 => 响应式数据(ref、reactive)

下面,通过一个示例代码,结合 Vue2 和 Vue3 来聊聊 Composition Api 。

<template>
  <div>
    <p>当前系统的主题色为 -- {{color}}</p>
    <p>当前viewport的宽高比为16:9 -- {{is16than9}}</p>
  </div>
</template>

在这里插入图片描述

WEB API说明示例
Window.matchMedia()回一个新的MediaQueryList 对象,表示指定的媒体查询字符串解析后的结果window.matchMedia(mediaQueryString)
prefers-color-scheme检测用户是否有将系统的主题色设置为亮色或者暗色window.matchMedia('(perfers-color-scheme: dark)')
aspect-ratio可以用来测试 viewport 的宽高比

①:主题色相关 ②:宽高比检测相关

vue2 实现

export default {
    name: 'SystemColor',
    data () {
      return {
        color: '',	// ①
        is16than9: false	// ②
      }
    },
    methods: {
      // ①
      perfersColorSchemeUpdate () {
        this.perfersColorSchemeMedia = window.matchMedia('(perfers-color-scheme: dark)')
        this.color = this.perfersColorSchemeMedia.matches ? 'dark': 'light'
      },
      // ②
      aspectRatioUpdate () {
        this.aspectRatioMedia = window.matchMedia('(aspect-ratio: 16/9)')
        this.is16than9 =  this.aspectRatioMedia.matches
      }
    },
    created () {
      // ①
      this.perfersColorSchemeUpdate()
      this.perfersColorSchemeMedia.addEventListener('change', this.perfersColorSchemeUpdate)
			// ②
      this.aspectRatioUpdate()
      this.aspectRatioMedia.addEventListener('change', this.aspectRatioUpdate)
    },
    destroyed () {
      // ①
    	this.perfersColorSchemeMedia.removeEventListener('change', this.perfersColorSchemeUpdate)
      // ② 
      this.aspectRatioMedia.removeEventListener('change', this.aspectRatioUpdate)
    } 
}

现在的诉求,又有一个新组件我们需要复用 「主题色相关」内容,我们需要对其进行抽离。

在 vue2 中,我们优先想到的是采用 mixins 方式

/* perfersColorSchemeMixin.js */
export default {
  methods: {
    perfersColorSchemeUpdate () {
      this.perfersColorSchemeMedia = window.matchMedia('(perfers-color-scheme: dark)')
      this.color = this.perfersColorSchemeMedia.matches ? 'dark': 'light'
    }
  }
}
import perfersColorSchemeMixin from './perfersColorSchemeMixin.js'
import aspectRatioMixin from './aspectRatioMixin.js'
export default {
  name: 'SystemColor',
  mixins: [perfersColorSchemeMixin, aspectRatioMixin],
  data () {
    return {
      color: '',
      is16than9: false
    }
  },
  created () {
    this.perfersColorSchemeUpdate()
    this.perfersColorSchemeMedia.addEventListener('change', this.perfersColorSchemeUpdate)

    this.aspectRatioUpdate()
    this.aspectRatioMedia.addEventListener('change', this.aspectRatioUpdate)
  },
  destroyed () {
    this.perfersColorSchemeMedia.removeEventListener('change', this.perfersColorSchemeUpdate)
    this.aspectRatioMedia.removeEventListener('change', this.aspectRatioUpdate)
  } 
}

当然我们也可以把 data 以及 created 中的内容也抽离到 mixins 中。但这样会导致模板中使用的变量不知道来自哪个 mixins,且可能存在命名冲突等问题。此时如果我们想再增加第3个新增功能,需要各个地方增加,阅读性变差、难以维护。

正是这种碎片化使得理解和维护一个复杂的组件变得非常困难。选项的强行分离为展示背后的逻辑关注点设置了障碍。此外,在处理单个逻辑关注点时,我们必须不断地在选项代码块之间“跳转”,以找到与该关注点相关的部分。

vue3 composition api

当我们在组件间提取并复用逻辑时,组合式(@vue/runtime-core) API 是十分灵活的。一个组合函数仅依赖它的参数和 Vue 全局导出的 API,而不是依赖其微妙的 this 上下文。你可以将组件内的任何一段逻辑导出为函数以复用它。

  • 基于响应式
  • 提供 vue 的生命周期钩子
  • 组件销毁时自动销毁依赖监听
  • 可复用的逻辑

组件可以变得如此清爽

import { reactive, toRefs } from 'vue'
import usePrefersColorScheme from './usePrefersColorScheme.js'
import useAspectRatio from './useAspectRatio.js'
export default {
  name: 'SystemColor',
  setup () {
    let color = usePrefersColorScheme()	// ① 仅此一处
    let is16than9 = useAspectRatio()		// ② 仅此一处
    return {
      color,
      is16than9
    }
  }
}

下述可组合式函数,建议使用 use 作为函数名的开头,以表示它是一个组合函数

/* ① usePrefersColorScheme.js */
import { ref, onUnmounted, onMounted } from 'vue'
export default function usePrefersColorScheme () {
  let media 
  let color = ref(null)

  const update = () => {
    media = window.matchMedia('(perfers-color-scheme: dark)')
    color.value = media.matches ? 'dark': 'light'
  }
  
  update()
  media.addEventListener('change', update)
  onUnmounted(() => {
    media.removeEventListener('change', update)
  })

  return color
}
/* ② useAspectRatio.js */
import { ref, onUnmounted, onMounted } from 'vue'
export default function usePrefersColorScheme () {
  let media
  let is16than9 = ref(null)

  const update = () => {
    media = window.matchMedia('(aspect-ratio: 16/9)')
    is16than9.value = media.matches
  }
  
  update()
  media.addEventListener('change', update)
  onUnmounted(() => {
    media.removeEventListener('change', update)
  })

  return is16than9
}

这些组合函数里面他都可以使用生命周期的钩子,可以自动更新和自动注销。在使用的时候也就没有了负担,只需要去留意每一个 ref 对应什么样的功能,其更好的提高代码的复用度、可读性、可维护性。

相比而言,组合式 API:

  • 暴露给模板的 property 来源十分清晰,因为它们都是被组合逻辑函数返回的值
  • 不存在命名空间冲突,可以通过解构任意命名
  • 不再需要仅为逻辑复用而创建的组件实例

其他

Vue 3 已经可以使用的有两个主要的逻辑的组件库,vueusevue-composable

  • vueuse:提供了更加细粒度的 Web API 以及工具分装
  • vue-composable:提供了更多常用的逻辑封装。
奋飛 CSDN认证博客专家 技术管理 前端工程化
乐观、勇气、专注、果断、好奇、公正、慎思、真诚、追求极致追求完美、诚信!独立撰写了多个前端专题模块,访问量达百万级。多次负责组织大数据可视化前端架构平台开发工作。对前端新技术、新潮流具有很强的敏锐力和洞察力!
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页