Vue3 组合式函数,实现minxins

Posted 面条请不要欺负汉堡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue3 组合式函数,实现minxins相关的知识,希望对你有一定的参考价值。

截至目前,组合式函数应该是在VUE 3应用程序中组织业务逻辑最佳的方法。它让我们可以把一些小块的通用逻辑进行抽离、复用,使我们的代码更易于编写、阅读和维护。


一. 什么是“组合式函数”?


根据官方文档说明,在 Vue 应用的概念中,“组合式函数”是一个利用 Vue 组合式 API 来封装和复用有状态逻辑的函数。
这就意味着,任何有状态逻辑,并且使用了响应式处理的逻辑都可以转换成组合式函数。这和我们平时抽离封装的公共方法还是有一些区别的
。我们封装的公共方法往往是无状态的:它在接收一些输入后立刻返回所期望的输出。而组合式函数往往是和状态逻辑关联的。简单的理解就是“**可复用逻辑的集合,专注点分点**”

二. vue3 自定义hooks

1. 为什么Vue3要用自定义Hook?

结论:就是为了让Compoosition Api更好用更丰满,让写Vue3更畅快! 其实这个问题更深意义是为什么Vue3比Vue2更好!无外呼性能大幅度提升,其实编码体验也是Vue3的优点**Composition Api的引入(解决Option Api在代码量大的情况下的强耦合)** 让开发者有更好的开发体验。

2. 怎么理解自定义Hooks

以函数形式抽离一些可复用的方法像钩子一样挂着,随时可以引入和调用,实现高内聚低耦合的目标;

  1. 将可复用功能抽离为外部JS文件
  2. 函数名/文件名以use开头,形如:useXX
  3. 引用时将响应式变量或者方法显式解构暴露出来如:const nameRef,Fn = useXX()

(在setup函数解构出自定义hooks的变量和方法)

3. 实例

简单的加减法计算,将加法和减法抽离为2个自定义Hooks,并且相互传递响应式数据

  • 加法功能-Hook
import  ref, watch  from 'vue';
const useAdd= ( num1, num2 )  =>
    const addNum = ref(0)
    watch([num1, num2], ([num1, num2]) => 
        addFn(num1, num2)
    )
    const addFn = (num1, num2) => 
        addNum.value = num1 + num2
    
    return 
        addNum,
        addFn
    

export default useAdd
  • 减法功能-Hook
import  ref, watch  from 'vue';
export function useSub  ( num1, num2 )
    const subNum = ref(0)
    watch([num1, num2], ([num1, num2]) => 
        subFn(num1, num2)
    )
    const subFn = (num1, num2) => 
        subNum.value = num1 - num2
    
    return 
        subNum,
        subFn
    
  • 加减法计算组件
<template>
    <div>
        num1:<input v-model.number="num1" style="width:100px" />
        <br />
        num2:<input v-model.number="num2" style="width:100px" />
    </div>
    <span>加法等于: addNum </span>
    <br />
    <span>减法等于: subNum </span>
</template>
​
<script setup>

import useAdd from './useAdd.js'     //引入自动hook
import  useSub  from './useSub.js' //引入自动hook
​
const num1 = ref(2)
const num2 = ref(1)
//加法功能-自定义Hook(将响应式变量或者方法形式暴露出来)
const  addNum, addFn  = useAdd( num1, num2 )
addFn(num1.value, num2.value)
//减法功能-自定义Hook (将响应式变量或者方法形式暴露出来)
const  subNum, subFn  = useSub( num1, num2 )
subFn(num1.value, num2.value)
</script>

3. 自定义Hooks 还可以传参

接上面的例子

平均功能-Hook 
import  ref, watch  from "vue";
export function useAverage(addNum) 
  const averageNum = ref(0);
  watch(addNum, (addNum) => 
    averageFn(addNum);
  );
  const averageFn = (addNum) => 
    averageNum.value = addNum / 2;
  ;
  return 
    averageNum,
    averageFn,
  ;



组件内:

//加法功能-自定义Hook(将响应式变量或者方法形式暴露出来)
const  addNum, addFn  = useAdd( num1, num2 )
addFn(num1.value, num2.value)//主动调用,返回最新addNum
//平均功能-自定义Hook- hook传入参数值来其他hook暴露出来的变量
const  averageNum, averageFn = useAverage(addNum)
averageFn(addNum.value)

三 .自定义Hooks 实现minxins

Vue3自定义Hooks和Vue2时代Mixin的关系

Mixin不足

在 Vue 2 中,mixin 是将部分组件逻辑抽象成可重用块的主要工具。但是,他们有几个问题:

1、Mixin 很容易发生冲突:因为每个 mixin 的 property 都被合并到同一个组件中,所以为了避免 property 名冲突,你仍然需要了解其他每个特性。

2、可重用性是有限的:我们不能向 mixin 传递任何参数来改变它的逻辑,这降低了它们在抽象逻辑方面的灵活性。

Vue2 中的mixins混入器写法缺点

//minxins.js 文件
export default
    data()
        return
            message:'混入对象',
            name:'zhangsan000'
        
    ,
    methods: 
        logMessage() 
            console.log('打印message', this.message);
        
    


组件:
// 使用
import minxins  from "./common/minxins";
import minxins1 from "./common/minxins1"; // 举例
import minxins2 from "./common/minxins2"; // 举例
export default
    mixins: [minxins, minxins1, minxins2], //可混入多个文件
    data()
        return
             message:'混入对象新的',
             bar:'bar',
 
        
    ,
    created()
      this.logMessage(); // 打印 '混入对象新的'
      console.log('created组件钩子被调用')
    , 

 上面的代码 可以看出: mixins 的深度合并非常隐式,让代码逻辑更难理解和调试,具体表现为如下几点:

  • 数据来源不清晰:当使用了多个minxins时, property来自哪个 mixin变得不清晰,这使追溯实现和理解组件行为变得困难。
  • 命名冲突:因为每个特性的属性都被合并到同一个组件中,组件内同名的属性或方法会把mixins里的覆盖掉。
  • 可重用性有限: 多个minxin需要依赖共享的 property键名来进行相互作用,这使得它们隐性地耦合在一起。而一个组合式函数的返回值可以作为另一个组合式函数的参数被传入,像普通函数那样。

1. Mixin难以追溯的方法与属性!Vue3自定义Hooks却可以

Vue3自定义Hooks, 引用时将响应式变量或者方法显式暴露出来如:

const nameRef,Fn = useXX()

2. Mixin同名变量会被覆盖,Vue3自定义Hook可以在引入的时候对同名变量重命名

  • mixin
export default 
  mixins: [ addMixin, subMixin], //组件内混入加法和减法Mixin
  mounted()
      this.add(num1,num2) //调用加法addMixin内部的add方法
      this.sub(num1,num2) //调用减法subMixin内部的sub方法
   

 如果this.add(num1,num2)和 this.sub(num1,num2) 计算的结果返回的同名变量totalNum,由于JS单线程,后面引入的会覆盖前面的,totalNum最终是减法sub的值

  • Vue3自定义Hooks

如上面例子

//加法功能-自定义Hook(将响应式变量或者方法形式暴露出来)
const  addNum, addFn  = useAdd( num1, num2 )
addFn(num1.value, num2.value)
//减法功能-自定义Hook (将响应式变量或者方法形式暴露出来)
const  subNum, subFn  = useSub( num1, num2 )
subFn(num1.value, num2.value)

在Vue3自定义Hooks中,虽然加法和减法Hooks都返回了totalNum,但是利用ES6对象解构很轻松给变量重命名 

所以在vue3中是不推荐使用mixins 这种方式

 Vue3 composition API写 法写 minxins 类功能

//useHello.js
import  ref  from 'vue';

export function useHello() 
  let message = ref('组合函数api');
  const logMessage = (val) => 
    console.log(val + message.value);
  ;
  return  message, logMessage ;



组件使用时
<template>
  <div class="app-container home">
    <p> message </p>
  </div>
</template>

<script setup name="Index">
  import  useHello  from './useHello.js';
  let name = ref('hello');
 // let message = ref('混入对象新'); //异常提示 无法重新声明块范围变量“message”
  const  message, logMessage  = useHello();

  onMounted(() => 
    logMessage(name.value);
  );
</script>

上面代码可以看出:

  • 当使用了多个组合式封装的属性时,追溯来源清晰明了。
  • 命名冲突会直接提示命名重复了的异常。
  • 不存在 Vue2那种 隐式的跨 minxin交流,因为页面引用了重复变量直接会提示。
  • 使用函函数式编程, 函数内部的变量在打包的时候,压缩器会把函数内部的变量压成 var a,b,c,d 之类的。而对象中的属性key 值,主流打包工具压缩器没有简化对象key值名字。所以函数式编程打包包体相对会更小一些。

四 总结

Vue2时代Option Api ,data、methos、watch.....分开写,这种是碎片化的分散的,代码一多就容易高耦合,维护时来回切换代码是繁琐的!

Vue3时代Composition Api,通过利用各种Hooks和自定义Hooks将碎片化的响应式变量和方法按功能分块写,实现高内聚低耦合

形象的讲法:Vue3自定义Hooks是组件下的函数作用域的,而Vue2时代的Mixins是组件下的全局作用域。全局作用域有时候是不可控的,就像var和let这些变量声明关键字一样,const和let是var的修正。Composition Api正是对Vue2时代Option Api 高耦合和随处可见this的黑盒的修正,Vue3自定义Hooks是一种进步。

把Mixin和自定义Hook进行比较,一个是Option Api的体现,一个是Composition Api的体现。如果能理解高内聚低耦合的思想,那么就能理解为什么Vue3是使用Composition Api,并通过各种自定义Hooks使代码更强壮。

五.补充

由Ref组成的对象

以使用可组合的函数式,同时获得ref和reactive的好处

import  ref,reactivefrom 'vue'

function useMouse()
    return 
        x:ref(0),
        y:ref(0),
    

const x = useMouse()
const mouse = reactive(useMouse())

mouse.x === x.value //true

1.可以直接使用es6解构其中的Ref使用
2.根据使用方式,当想要自动解包的功能时,可以使用 reactive将其转换为对象

将异步操作转换为“同步”

使用组合式API,我们甚至可以将异步操作转换为“同步”的

异步
const data = await fetch('https://196.198.1.0/use/').then(r=>r.json())

// use data
 组合式api

const data = await useFetch('https://196.198.1.0/use/').json()
const user_url = computed(()=>data.value?data.value.user_url:'')

先建立数据间的“连结”,然后再等待异步请求返回将数据填充。概念和react 中的SWR(stale-while-revalidate)类似。

状态共享

由于组合式API天然提供的灵活性,状态可以独立于组件被创建并使用

但是不兼容ssr(服务器渲染) 

兼容SSR的状态共享

使用provide和inject 来共享应用层面的状态。

六. 引用

vue3中的自定义hook函数

以上是关于Vue3 组合式函数,实现minxins的主要内容,如果未能解决你的问题,请参考以下文章

Vue3 组合式 API 的基础 —— setup

vue3组合式API之setup()介绍与reactive()函数的使用

vue3修改响应式代理值

vue3中的setup

Vue2和Vue3的区别&&Vue3的组合式API

组合式api