vue2患者看病,vue3(vue3重头学-逻辑复用-内置组件)

194 阅读7分钟
本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

组合式函数

定义: 利用vue的组合式api来封装有状态的函数,来达到复用的目的

无状态函数:如格式化日期函数,格式化手机号,以达到保护用户隐私的目的的中间是**** 等都是无状态的函数,无状态函数又称纯函数 纯函数的输入输出有一对一的映射关系,无论它被调用多少次,也同样大概率是无副作用函数

有状态函数: 有状态逻辑负责管理,会随时间或外部变量而变化的状态。


// 这就是一个纯函数,也无副作用,因为它不会改变这两个参数,同时,无论任何时候,他的值都用ab决定
function add(a, b) {
    return a + b
}

// 有状态的函数,有副作用的函数
// 这里的函数,它每次在执行的时候都会使得temp的值发生变化,这就是副作用函数,
// 同时,他也是有状态的,他的状态取决于他的temp
var temp = 3
function add2(a, b) {
    temp = temp + 1;
    return a + b + temp
}

如上面的写法一样,大多数情况下,我们肯定提倡的是无状态函数,这样幂等性是得到保障的,函数也无副作用,降低了bug的触发,但是挺多时候,由于业务的复杂,代码的确需要这样的有状态的函数,所以vue3的写法也是在一定程度上是想规范我们对于有状态函数的写法,尽量进行规范,文档中的例子并不是主要的,主要的是他的约定和最佳实践反而更有借鉴意义

首先是命名:

    组合式函数约定用驼峰命名法命名,并以“use”作为开头。

输入参数

尽管其响应性不依赖 ref,组合式函数仍可接收 ref 参数。如果编写的组合式函数会被其他开发者使用,你最好在处理输入参数时兼容 ref 而不只是原始的值。unref() 工具函数会对此非常有帮助:

import { unref } from 'vue'

function useFeature(maybeRef) {
  // 若 maybeRef 确实是一个 ref,它的 .value 会被返回
  // 否则,maybeRef 会被原样返回
  const value = unref(maybeRef)
}

刚好这里了解下新的API: unref()

这是一个语法糖,如果参数是ref,则返回内部值,否则返回参数本身,看着比较懵,代码奉上,就是一个isRef()语法糖

    val = isRef(val) ? val.value : val

返回值

继续来看组合式函数的返回值,我们在组合式函数中一直使用ref而不是reactive,这是因为返回是一个包含了多个ref的普通非响应式对象,这样是为了后续函数在组件中被调用时,在被解构后,仍然保持响应式

    // x 和 y 是两个 ref
    const { x, y } = useMouse()

副作用

虽然我们前面说了不推进写带有副作用的函数,但是很多时候这又是必须的,比如我们要添加dom的事件监听器,或者在调用时进行网络请求,这个时候需要针对调用和生命周期钩子函数进行注意:

  • 如果使用了SSR(服务端渲染),那必须等到组件挂载后再调用生命周期函数进行dom相关操作,
  • 在调用结束后,必须在onUnmounted()中清理副作用,如事件监听,这个时候应该要移除,

更看重的是和vue2的mixin模式的比较

  • mixin不清晰的数据来源,就是当我们使用了多个mixin的时候,我们不知道一个数据具体来自哪个mixin,组合式函数则通过ref和解构模式来让属性和来源一目了然
  • 命名空间冲突,多个mixin作者如果使用相同的属性名,则会造成命名冲突,但是通过组合式函数,则可以在解构的时候,重新命名,避免冲突,同时给与使用者更大的自由度

正是基于上面这些缺点,在vue3中,文档中也明确说明了不推荐继续使用mixin,保留该API更多的考量是为了项目迁移的需求

文档中最后提到的和React的hook的对比我们就不记录了,免得被说是碰瓷哈哈哈。感兴趣的自行了解,react是非常优秀的框架,不影响我们去学习和了解他,毕竟vue本身也借鉴了很多react的东西

自定义指令

vue允许你自定义一些类似v-modal/ v-show 的指令,自定义指令主要是为了重用涉及普通元素的底层DOM访问的逻辑

下面先看一个例子,vue3的自定义指令和vue2还是不同的,这里详细记录下

<script setup>
// 在模板中启用 v-focus
const vFocus = {
  mounted: (el) => el.focus()
}
</script>

<template>
  <input v-focus />
</template>

这是一个input被vue插入到dom后,自动聚焦的指令,接下来我们了解规则 在** < script setup > ** 中,任何以v开头的驼峰式命名的变量都可以被用作一个自定义指令,如上面的例子中,vFocus使用时就是v-focus

而如果没有在** < script setup > ** 中,自定义指令则需要通过directive选项注册

const app = createApp({})
// 使 v-focus 在所有组件中都可用
app.directive('focus', {
  /* ... */
})

指令钩子

这是我们进行自定义指令编写时最重要的工具:

const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode, prevVnode) {
    // 下面会介绍各个参数的细节
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode, prevVnode) {}
}

这里的参数的定义是:

钩子参数

指令的钩子会传递以下几种参数:

  • el:指令绑定到的元素。这可以用于直接操作 DOM。

  • binding:一个对象,包含以下属性。

    • value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2
    • oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
    • arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"
    • modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }
    • instance:使用该指令的组件实例。
    • dir:指令的定义对象。
  • vnode:代表绑定元素的底层 VNode。

  • prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。

文档中的例子:

<div v-example:foo.bar="baz">
// binding参数会是一个这样的对象
{
  arg: 'foo',
  modifiers: { bar: true },
  value: /* `baz` 的值 */,
  oldValue: /* 上一次更新时 `baz` 的值 */
}

插件

文档第一部分说了插件的使用方式,和vue2基本相同,app.use(myplugin,{}) 文档第二部分是我之前在vue2文档这个位置没有看到过的,编写一个插件,之前更多的做为一个小菜鸡,是找别人的插件来使用,比如lodash这类工具函数,插件常见的功能用途是

  • 注册一个到多个全局组件或自定义指令
  • 提供一个资源provide,使其可被整个应用使用,
  • 添加一些全局实例属性活方法

编写一个插件这里的例子大家自己去文档中看吧,是个很好的例子,但是个人还是更想把一些自定义指令整合后,写成一个插件。