Vue3 学习笔记 —— Hooks、全局函数和变量、Vue3 插件

news2024/12/30 2:53:33

目录

1. Hooks

1.1 Vue2 中的 mixins

1.1.1 mixins 是什么?

1.1.2 mixins 缺点?

1.2 Vue3 中的 Hooks

1.2.1 Vue3 Hooks 是什么?

1.2.2 Vue3 内置 hooks 举例

1.2.3 自定义 Hooks

2. 全局函数和全局变量

2.1 app.config.globalProperties

2.2 过滤器还在吗?如何使用全局变量?

2.3 createApp()、proxy 源码分析

3. Vue3 插件

3.1 什么是插件?怎么使用插件?

3.2 编写 Vue3 插件(实现 Loading 效果)

3.2.1 添加 Loading 插件页面(loading.vue)

3.2.2 编写 Loading 插件 ts 逻辑(loading.ts)

3.2.3 在 main.ts 中全局注册 Loading  插件

3.2.4 在 .vue 中使用 Loading 插件

3.3 Vue3 中的 app.use() 方法

3.3.1 手写一个 app.use()

3.3.2 app.use() 源码分析


1. Hooks

小满Vue3(第二十八章 自定义Hooks)_哔哩哔哩_bilibili小满Vue3(第二十八章 自定义Hooks)是Vue3 + vite + Ts + pinia + 实战 + 源码 +electron的第39集视频,该合集共计110集,视频收藏或关注UP主,及时了解更多相关视频内容。https://www.bilibili.com/video/BV1dS4y1y7vd?p=39&vd_source=8bc01635b95dbe8ecd349b2c23b03a10

1.1 Vue2 中的 mixins

1.1.1 mixins 是什么?

将多个组件 相同逻辑 抽离出来,各个组件引入 mixins 进行复用

mixins 的生命周期调用,先于组件内部的生命周期调用

1.1.2 mixins 缺点?

变量来源不明确:隐式传入,不利于阅读

存在覆盖问题:组件的 data、methods、filters 会覆盖 mixins 里同名的 data、methods、filters

1.2 Vue3 中的 Hooks

1.2.1 Vue3 Hooks 是什么?

Vue3 的 Hook 函数 相当于 Vue2 的 mixins,区别在于 Hooks 是函数

Vue3 Hooks 库:

Get Started | VueUseCollection of essential Vue Composition Utilitieshttps://vueuse.org/guide/

1.2.2 Vue3 内置 hooks 举例

先来看下官方提供的一些例子,比如 useAttrs、useSlots...

// 父组件中使用子组件,并传入一些属性
<Test :name="'6666'" :value="8888"></Test>

// 子组件中,使用 useAttrs 获取 attrs
import { useAttrs } from 'vue';

const attrs = useAttrs();

console.log(attrs); // name:.....

 

 

1.2.3 自定义 Hooks

定义 hooks.ts:

// hooks 中直接使用 vue 中的 APIs 即可
import { onMounted } from 'vue'

// 接收的选项
type Options = {
    el: string
}

// 返回的选项
type Return = {
    Baseurl: string | null
}

export default function (option: Options): Promise<Return> {
  return new Promise((resolve) => {
    onMounted(() => {
      // 获取图片 DOM
      const file: HTMLImageElement = document.querySelector(option.el) as HTMLImageElement;
      // 在图片加载完成后,再进行  base64 转换
      file.onload = ():void => {
        resolve({
            Baseurl: toBase64(file)
        })
      }
    })

    const toBase64 = (el: HTMLImageElement): string => {
      // 创建 canvas
      const canvas: HTMLCanvasElement = document.createElement('canvas')
      const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
      // 设置 canvas 宽高
      canvas.width = el.width
      canvas.height = el.height
      // 绘制图片
      ctx.drawImage(el, 0, 0, canvas.width, canvas.height)   
      // 通过这个函数,可以导出一个 base64 格式的图片
      return canvas.toDataURL('image/png')
    }
  })
}

 

在 .vue 中,使用 hooks.ts:

<template>
  <div class="my-demo">
    <img id="img" src="../images/test.png" />
  </div>
</template>

<script lang="ts">
import { reactive, toRefs, defineComponent } from 'vue';
import useBase64 from './hoo';

export default defineComponent({
  setup(props, { emit }) {
    useBase64({
      el: '#img',
    }).then((res) => {
      console.log('Base64 格式的图片 ----', res.Baseurl);
    });

    const state = reactive({});

    return {
      ...toRefs(state),
    };
  },
});
</script>

2. 全局函数和全局变量

小满Vue3(第二十九章 全局函数和变量)_哔哩哔哩_bilibili小满Vue3(第二十九章 全局函数和变量)是Vue3 + vite + Ts + pinia + 实战 + 源码 +electron的第40集视频,该合集共计110集,视频收藏或关注UP主,及时了解更多相关视频内容。https://www.bilibili.com/video/BV1dS4y1y7vd?p=40&spm_id_from=pageDriver&vd_source=8bc01635b95dbe8ecd349b2c23b03a10

2.1 app.config.globalProperties

Vue2 中,使用 Prototype 定义全局函数和全局变量

Vue.prototype.$http = () => {}

Vue3 中,没有 Prototype,使用 app.config.globalProperties 定义全局函数和全局变量

const app = createApp({})
app.config.globalProperties.$http = () => {}

2.2 过滤器还在吗?如何使用全局变量?

Vue3 中,移除了过滤器,使用 全局函数 替代过滤器

下面在 main.ts 中,声明了一个全局函数:

app.config.globalProperties.$filters = {
  format<T extends any>(str: T): string {
    return `格式化输出文字 --- {str}`
  }

 

main.ts 中,补充声明文件,否则 TypeScript 无法正确推导数据类型,导致页面爆红

type Filter = {
    format<T>(str: T): string
}
 
// 声明要扩充 @vue/runtime-core 包的声明
// 这里扩充 "ComponentCustomProperties" 接口, 因为他是 Vue3 中实例的属性的类型
declare module 'vue' {
    export interface ComponentCustomProperties {
        $filters: Filter
    }
}
 
 

 

setup() 中,通过 getCurrentInstance().proxy.$filters.xxx 使用全局函数

import { getCurrentInstance } from 'vue'
// 获取 实例
const app = getCurrentInstance()
// 通过 实例.proxy 调用全局方法
console.log(app?.proxy?.$filters.format('test'))

2.3 createApp()、proxy 源码分析

关于 createApp():packages\runtime-core\src\apiCreateApp.ts

// 调用初始化函数,返回 createApp 函数
export function createAppAPI<HostElement>(
  render: RootRenderFunction<HostElement>,
  hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
  /**
   * @param rootComponent 根组件
   */
  return function createApp(rootComponent, rootProps = null) {
    if (!isFunction(rootComponent)) {
      rootComponent = { ...rootComponent }
    }

    if (rootProps != null && !isObject(rootProps)) {
      __DEV__ && warn(`root props passed to app.mount() must be an object.`)
      rootProps = null
    }

    // 初始化,就是返回了 app、config.globalProperties、mixins 等等之类的对象
    const context = createAppContext()
    const installedPlugins = new Set()

    let isMounted = false

    // 将初始化数据,赋值给 app 对象,继续填充属性、版本、方法等
    const app: App = (context.app = {
      _uid: uid++,
      _component: rootComponent as ConcreteComponent,
      _props: rootProps,
      _container: null,
      _context: context,
      _instance: null,

      version,

      get config() {
        return context.config
      },

      set config(v) {
        ...
      },

      // 注册插件
      use(plugin: Plugin, ...options: any[]) {
        ...
      },

      mixin(mixin: ComponentOptions) {
        ...
      },

      component(name: string, component?: Component): any {
        ...
      },

      directive(name: string, directive?: Directive) {
        ...
      },

      mount(
        rootContainer: HostElement,
        isHydrate?: boolean,
        isSVG?: boolean
      ): any {
        ...
      },

      unmount() {
        ...
      },

      provide(key, value) {
        ...
      }
    })

    ...

    // 返回 app
    return app
  }
}

 

关于 createAppContext():packages\runtime-core\src\apiCreateApp.ts

export function createAppContext(): AppContext {
  return {
    app: null as any,
    config: {
      isNativeTag: NO,
      performance: false,
      globalProperties: {},
      optionMergeStrategies: {},
      errorHandler: undefined,
      warnHandler: undefined,
      compilerOptions: {}
    },
    mixins: [],
    components: {},
    directives: {},
    provides: Object.create(null),
    optionsCache: new WeakMap(),
    propsCache: new WeakMap(),
    emitsCache: new WeakMap()
  }
}

关于 proxy:packages\runtime-core\src\component.ts

  instance.accessCache = Object.create(null)

  // markRaw 会添加 __skip__ 属性,进而跳过 reactive,防止重复代理
  instance.proxy = markRaw(new Proxy(instance.ctx, PublicInstanceProxyHandlers))

ctx 就是 $、$attrs、$data、$el、$emit、$props、$parent、$refs、$root、$slots、$watch...等等

打印 ctx 能够看到 __v_skip: true 表示跳过 reactive,防止对 ctx 进行重复代理

3. Vue3 插件

小满Vue3(第三十章 自定义Vue插件 & 源码讲解)_哔哩哔哩_bilibili小满Vue3(第三十章 自定义Vue插件 & 源码讲解)是Vue3 + vite + Ts + pinia + 实战 + 源码 +electron的第41集视频,该合集共计110集,视频收藏或关注UP主,及时了解更多相关视频内容。https://www.bilibili.com/video/BV1dS4y1y7vd?p=41&vd_source=8bc01635b95dbe8ecd349b2c23b03a10

3.1 什么是插件?怎么使用插件?

举个栗子~

ElementPlus 中的 ElMessage 就可以是一个插件,因为全局皆可使用

使用 createApp() 初始化 Vue 项目后,通过 use() 方法,将插件添加到项目中

3.2 编写 Vue3 插件(实现 Loading 效果)

Vue3 插件支持两种形式:

  • 对象形式:要求必须有 install() 方法,Vue3 会将 app 实例 注入 install() 方法
  • 函数形式:就直接当 install() 方法去使用

3.2.1 添加 Loading 插件页面(loading.vue)

此文件中需要注意:将外部需要使用的 组件内部方法,进行暴露(defineExpose)

<template>
    <div v-if="isShow" class="loading">
        <div class="loading-content">Loading...</div>
    </div>
</template>
    
<script setup lang='ts'>
import { ref } from 'vue';

// 是否展示 loading
const isShow = ref(false)
 
const show = () => {
    isShow.value = true
}

const hide = () => {
    isShow.value = false
}

// 对外暴露 当前组件的 属性和方法,不暴露就不能被使用,会报错
defineExpose({
    isShow,
    show,
    hide
})
</script>
 
 
    
<style scoped lang="less">
.loading {
    position: fixed;
    display: flex;
    justify-content: center;
    align-items: center;
    inset: 0;
    background: rgba(0, 0, 0, 0.8);
    &-content {
        font-size: 30px;
        color: #fff;
    }
}
</style>

3.2.2 编写 Loading 插件 ts 逻辑(loading.ts)

Vue3 插件的 对象 形式,必须有 install 函数

Vue3 自动给 install() 内传入 app 实例

createVNode —— Vue3 提供的底层方法,它会给组件创建一个虚拟 DOM,也就是 Vnode

render 把 Vnode 生成真实 DOM,并且挂载到指定节点

不通过 vnode.component.setupState 获取 loading.vue 的方法,因为 loading.vue 没有主动暴露方法的话,Vue3 不推荐在插件中使用 没被暴露的方法

虽然在打印出来的 vnode 中找到了 component.setupState,但是使用会报错

使用 exposed 替代 vnode.component.setupState 方法(.vue 里要使用 defineExpose 导出哦)

import { createVNode, render, VNode, App } from 'vue';
import Loading from './loading.vue'

// Vue3 插件的 对象 形式,必须有 install 函数
export default {
    // Vue3 自动给 install() 内传入 app 实例
    install(app: App) {
        // createVNode —— Vue3 提供的底层方法
        // 它会给组件创建一个虚拟 DOM,也就是 Vnode
        // 此时打印 vnode,会发现 component 没有值
        const vnode: VNode = createVNode(Loading)

        // render 把 Vnode 生成真实 DOM,并且挂载到指定节点,此处挂载点是全局 body
        // 此时打印 vnode,会发现 component 已经有值了
        render(vnode, document.body)

        // Vue 提供的全局配置,可以自定义(此处注意命名啊,别跟 elementplus 重复了)
        app.config.globalProperties.$loading = {

            // 为啥不通过 vnode.component.setupState 获取 loading.vue 的方法?
            // 因为 loading.vue 没有主动暴露方法的话,Vue3 不推荐在插件中使用
            // 虽然在打印出来的 vnode 中找到了 component.setupState,但是使用会报错
            // 使用 exposed 替代上述方法

            show: () => vnode.component?.exposed?.show(),
            hide: () => vnode.component?.exposed?.hide(),
        }
 
    }
}

3.2.3 在 main.ts 中全局注册 Loading  插件

// 引入 Vue3 插件(ts 文件)
import Loading from './components/loading' 

const app = createApp(App)

// 注册插件
app.use(Loading)
 
 
type Lod = {
    show: () => void,
    hide: () => void
}

// 编写 ts loading 声明文件
// 目的是为了 防止编写插件时报错,还能顺便增加智能提示
// @vue/runtime-core 可以替换成 vue,但是可能会出现报错
declare module '@vue/runtime-core' {
    export interface ComponentCustomProperties {
        $loading: Lod
    }
}

app.mount('#app')

 

3.2.4 在 .vue 中使用 Loading 插件

<template>
  <div></div>
</template>
 
<script setup lang='ts'>
  import { ref, reactive, getCurrentInstance } from 'vue'

  const instance = getCurrentInstance()

  // 展示 loading
  instance?.proxy?.$Loading.show()

  // 5s 后关闭 loading
  setTimeout(()=>{
    instance?.proxy?.$Loading.hide()
  }, 5000)
</script>

3.3 Vue3 中的 app.use() 方法

3.3.1 手写一个 app.use()

添加 my-use.ts

import type { App } from 'vue'
import { app } from './main'

// 定义泛型,要求插件中,必须有 install 函数
interface Use {
    install: (app: App, ...options: any[]) => void
}
 
const installedList = new Set()
 
export function myuse<T extends Use>(plugin: T, ...options: any[]) {
    if (installedList.has(plugin)) {
      return console.warn('插件重复添加了 --- ', plugin)
    } else {
        plugin.install(app, ...options)
        installedList.add(plugin)
    }
}

在 main.ts 中使用 my-use.ts

import { myuse } from './my-use'
myuse(Loading);

3.3.2 app.use() 源码分析

packages\runtime-core\src\apiCreateApp.ts

// 注册插件
use(plugin: Plugin, ...options: any[]) {
  // 如果当前组件注册过,就进行报错
  if (installedPlugins.has(plugin)) {
    __DEV__ && warn(`Plugin has already been applied to target app.`)
  // 没有注册过,就判断下 plugin 有没有值,里面有没有 install 方法(对象格式)
  } else if (plugin && isFunction(plugin.install)) {
    // 如果是函数,就将插件添加到缓存中
    installedPlugins.add(plugin)
    // 调用 install 方法,将 app、用户自定义参数 传进去
    plugin.install(app, ...options)
    // 函数格式
  } else if (isFunction(plugin)) {
    installedPlugins.add(plugin)
    plugin(app, ...options)
  } else if (__DEV__) {
    warn(
      `A plugin must either be a function or an object with an "install" ` +
        `function.`
    )
  }
  return app
},

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/61077.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【深入设计模式】适配器模式—一切皆有可能

文章目录1. 适配器模式1.1 适配器模式简介1.2 适配器模式结构1.3 适配器模式示例2. 适配器模式在源码中的应用3. 总结适配器这个词来源于硬件领域&#xff0c;是一个独立的硬件设备接口&#xff0c;允许硬件或电子接口与其它硬件或电子接口相连&#xff0c;比如常见的电源适配器…

(附源码)SSM芜湖公共书房服务平台 毕业设计 250859

SSM芜湖公共书房服务平台 摘 要 一座有底蕴的城市&#xff0c;应该是一个阅读的城市&#xff1b;一个有魅力的城市&#xff0c;应该是一个散发着书香的城市&#xff0c;而全民阅读量逐年增加&#xff0c;是社会进步、文明程度提高的重要标志。各大城市启动“全民阅读”工作以来…

Redis缓存过期和和内存淘汰策略

目录 1、MaxMemory 2、Expire数据结构 3、删除策略 3.1、惰性删除 3.2、主动删除 3.3、缓存淘汰策略 ​​​​​​​3.4、缓存淘汰策略的选择 1、MaxMemory Redis作为DB使用时&#xff0c;为了保证数据的完整性&#xff0c;不允许淘汰任何键值对。Redis作为缓存使用时&…

汇编语言与微机原理 期末半开卷复习整理(上)

8086CPU寄存器 8086&#xff1a;16位&#xff0c;4.77MHz~10MHz,16根数据线&#xff0c;20根地址线 AX/AL:乘除法指令中用作累加器&#xff0c;IO指令中用作数据寄存器&#xff0c;可显式或隐含调用 AH&#xff1a;在LAHF用作目的寄存器&#xff0c;隐含调用。 AL&#xff1a;…

大数据必学Java基础(一百一十一):过滤器注解应用和开发案例

文章目录 过滤器注解应用和开发案例 一、过滤器注解应用 二、开发案例

Python学习日记-第三十八天-生成器(第二节)

系列文章目录 使用greenlet&#xff0c;gevent完成多任务一、使用greenlet&#xff0c;gevent完成多任务 这里要先在pycharm里面提前安装好greenlet和gevent的包 操作&#xff1a; 代码&#xff1a; from greenlet import greenlet import timedef test1():while True:prin…

Android入门第39天-系统设置Configuration类

简介 本节给大家介绍的Configuration类是用来描述手机设备的配置信息的&#xff0c;比如屏幕方向&#xff0c; 触摸屏的触摸方式等。 Configuration给我们提供的方法列表 densityDpi&#xff1a;屏幕密度fontScale&#xff1a;当前用户设置的字体的缩放因子hardKeyboardHidd…

[深度学习] python基础支持汇总

这个系列放一些看神经网络源码过程中的python语法现象 文章目录前言一、list操作://extends/append的区别1.引入2.细致场景再现前言 例如&#xff1a;这个系列放一些看神经网络源码过程中的python语法现象, 直接解析语法太干瘪无聊.希望用这个方式来巩固所学知识 一、list操作…

电商评论文本情感分类(中文文本分类+中文词云图)(第一部分-textcnn)

电商评论文本情感分类(中文文本分类中文词云图) 第一部分 第二部分Bert部分 本项目包含&#xff1a; 1.中文文本处理 2.中文词云图绘制 3.中文词嵌入 4.基于textcnn的中文文本分类&#xff08;Test_Acc89.2000&#xff09; 5.基于bert的中文文本分类&#xff08;Test_Acc0.…

ServiceComb场景及其原理

文章目录Java-ChassisEnableServiceComb初始化SCBSPIServiceUtils自定义SPI加载器职责链管理器FilterChainsManager/ConsumerHandlerManagerRpcSchema注册服务如何保活&#xff1f;RpcReferencePropertySourcesPlaceholderConfigurerThreadPoolExecutorEx/LinkedBlockingQueueE…

深刻理解JAVA并发中的有序性问题和解决之道

问题 Java并发情况下总是会遇到各种意向不到的问题&#xff0c;比如下面的代码&#xff1a; int num 0;boolean ready false; // 线程1 执行此方法 public void actor1(I_Result r) {if(ready) {r.r1 num num;} else {r.r1 1;} } // 线程2 执行此方法 public void actor…

Clickhouse 使用DBeaver连接

ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。 据处理大致可以分成两大类&#xff1a;联机事务处理OLTP&#xff08;on-line transaction processing&#xff09;、联机分析处理OLAP&#xff08;On-Line Analytical Processing&#xff09;。 OLTP是传统的…

JavaWeb--JDBC核心技术

JavaWeb--JDBC核心技术JDBC核心技术第1章&#xff1a;JDBC概述1.1 数据的持久化1.2 Java中的数据存储技术1.3 JDBC介绍1.4 JDBC体系结构1.5 JDBC程序编写步骤第2章&#xff1a;获取数据库连接2.1 要素一&#xff1a;Driver接口实现类2.1.1 Driver接口介绍2.1.2 加载与注册JDBC驱…

Redis学习笔记(六)

哨兵 哨兵时一个分布式系统&#xff0c;用于对主从结构中的每台服务器进行监控&#xff0c;当出现故障时通过投票机制选择新的master&#xff0c;并将所有slave连接到新的master哨兵的作用 监控 不断检查master和slave是否正常运行master存活检测、master与slave运行情况检测 通…

Linux调度(三)——抢占式调度

目录 抢占式场景一&#xff1a; 抢占式场景二 抢占的时机 用户态的抢占时机 抢占式机一&#xff1a; 抢占时机二&#xff1a; 内核态的抢占时机 时机一 时机二 总结 之前讲了主动式调度&#xff0c;就是进程运行到一半&#xff0c;因为等待I/O等操作而主动让出CPU&a…

动态规划算法(3)(不同方案数问题+拆分问题)

文章目录不同路径不同路径II整数拆分不同的二叉搜索树动态规划解题五步走&#xff1a; 确定dp数组以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 不同路径 力扣传送门&#xff1a; https://leetcode.cn/problems/unique-paths/description/ 确定dp…

[附源码]JAVA毕业设计酒店订房系统(系统+LW)

[附源码]JAVA毕业设计酒店订房系统&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&…

基于opencv答题卡识别基本处理_1

文章目录1.读取图片2.图片预处理2.1 原图转换为灰度图2.2 高斯滤波处理&#xff0c;去除噪声点2.3 增强亮度2.4 自适应二值化2.5 图片可视化3. 添加边框3.1 使用copyMakeBorder添加边框3.2 可视化图片查看效果3.3 手动截取答题卡区域1.读取图片 img cv2.imread(images/5.png)…

Nx C++程序使用spdlog库进行日志存储

1 spdlog简介 spdlog是一个开源的日志库&#xff0c;在github上有。代码见这里&#xff0c;文档这里 C语言的&#xff0c;支持Linux、windows等系统。 csdn上也有许多介绍&#xff0c;这里列举两个&#xff1a;1、2 2 使用 2.1下载编译链接 有多种使用方式&#xff0c;这里…

(三)沟通管理风险管理采购管理@相关方管理

沟通管理目录概述需求&#xff1a;设计思路实现思路分析1.沟通管理绩效报告提供资源2.管理沟通3.监督沟通风险管理规划风险管理识别风险定性风险分析&#xff1a;定量分析风险规划风险应对实施分享应对监督风险采购管理&#xff1a;12.1 规划采购的管理12.2 实施采购控制采购相…