# 一、逻辑复用
# 1、组合式函数 ✍
“组合式函数”(Composables) 是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数(hooks)。
VueUse (opens new window):一个日益增长的 vue3 组合式函数集合
鼠标跟踪功能:useMouse
在 VueUse 中收集了该 Hooks 函数:useMouse (opens new window)
- 逻辑封装
// mouse.js import { ref, onMounted, onUnmounted } from 'vue' // 按照惯例,组合式函数名以“use”开头 export function useMouse() { // 被组合式函数封装和管理的状态 const x = ref(0) const y = ref(0) // 组合式函数可以随时更改其状态。 function update(event) { x.value = event.pageX y.value = event.pageY } // 一个组合式函数也可以挂靠在所属组件的生命周期上 // 来启动和卸载副作用 onMounted(() => window.addEventListener('mousemove', update)) onUnmounted(() => window.removeEventListener('mousemove', update)) // 通过返回值暴露所管理的状态 return { x, y } }
Copied!
- 使用示例
<script setup> import { useMouse } from './mouse.js' const { x, y } = useMouse() </script> <template>Mouse position is at: {{ x }}, {{ y }}</template>
Copied!
计数器:useCounter
在 VueUse 中收集了该 Hooks 函数:useCounter (opens new window)
使用标题:useTitle
在 VueUse 中收集了该 Hooks 函数:useTitle (opens new window)
# 2、Hooks 优势
vue2 中的 mixin 复用的是共用的一些相同的配置,但也存在不少问题:
问题
官方总结了几个点,但总结来说,就一个点:
多个 mixin 时,数据未与 mixin 做到分离(主要问题)
这就是 vue2 官方声明:如果使用全局 mixin 时,推荐使用 vue 插件的原因。
vue3 中的组合式函数中使用 ref + 解构模式,让属性的来源在消费组件时一目了然
自定义 hook 的优势: 复用代码, 让 setup 中的逻辑更清楚易懂


# 3、vue3 插件
vue3 的插件和 vue2 的插件使用几乎一致,只是变换了一些全局 API 罢了:
插件可以是一个带 install() 方法的对象,亦或直接是一个将被用作 install() 方法的函数。插件选项 (app.use() 的第二个参数) 将会传递给插件的 install() 方法。
官方使用示例
- i18n.js
// plugins/i18n.js export default { install: (app, options) => { // 注入一个全局可用的 $translate() 方法 app.config.globalProperties.$translate = (key) => { // 获取 `options` 对象的深层属性 // 使用 `key` 作为索引 return key.split('.').reduce((o, i) => { if (o) return o[i] }, options) } // 将插件接收到的 options 参数提供给整个应用 app.provide('i18n', options) } }
Copied!
- main.js
import i18nPlugin from './plugins/i18n' app.use(i18nPlugin, { greetings: { hello: '你好' } })
Copied!
- 使用
<template> <h1>{{ $translate('greetings.hello') }}</h1> </template> <script setup> import { inject } from 'vue' const i18n = inject('i18n') console.log(i18n.greetings.hello) </script>
Copied!
下面写一个真实开发的示例:
- main.ts
import registerIcons from './global/register-element-icons.ts' app.use(registerIcons)
Copied!
- register-element-icons.ts
import * as ElementPlusIconsVue from '@element-plus/icons-vue' import type { App } from 'vue' const registerIcons = (app: App<Element>) => { for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } } export default registerIcons
Copied!
# 4、自定义指令
vue3 的自定义指令和 vue2 的自定义指令在没有使用 setup 语法糖的情况下,使用几乎一致,只是变换了一些全局 API 罢了:
vue2 中的全局自定义指令
// 简写形式 Vue.directive('color', function (el, binding){ el.style.color = binding.value }) // 完整形式 Vue.directive('color', { bind (el, binding) { el.style.color = binding.value } update (el, binding) { el.style.color = binding.value } })
Copied!
提示
- 在 vue2 中,仅仅提供了
bind
、inserted
、update
、(componentUpdated、unbind)几个钩子函数。 - 在 vue3 中,它提供的指令钩子大多生命周期钩子,如:created、beforeMount、
mounted
、beforeUpdate、updated
、beforeUnmount、unmounted
- setup 语法糖中 🤔
<script setup> // 在模板中启用 v-color const vColor = (el, binding) => { // 这会在 `mounted` 和 `updated` 时都调用 el.style.color = binding.value } </script>
Copied!
- 全局自定义指令
const app = createApp({}) // 使 v-focus 在所有组件中都可用 app.directive('color', (el, binding) => { // 这会在 `mounted` 和 `updated` 时都调用 el.style.color = binding.value })
Copied!
# 二、vue 发展
更多阅读
# 1、vue2 - vue3.0
vue3.0 是 vue 自 2014 年以来的一个重大版本。从开发角度来开,变化如下:
- 重大变化
全面拥抱 TypeScript
Composition API
- 细微变化
scoped 样式
Vue 2.x 中的深度选择器可以使用
>>>
、/deep/
、::v-deep
操作符:Vue Loader 中有相关描述:Scoped CSS (opens new window)
<style scoped> .a ::v-deep .b { /* ... */ } </style>
Copied!Vue3.0 中做出了调整,改为使用
:deep()
这个伪类:vue3 官方文档中的相关描述:CSS 功能 (opens new window)
<style scoped> .a :deep(.b) { /* ... */ } </style>
Copied!
全局 API 转移
Vue 2.x 有许多全局 API 和配置。
- 例如:注册全局组件、注册全局指令等。
//注册全局组件 Vue.component('MyButton', { data: () => ({ count: 0 }), template: '<button @click="count++">Clicked {{ count }} times.</button>' }) //注册全局指令 Vue.directive('focus', { inserted: el => el.focus() }
Copied!
Vue3.0 中对这些 API 做出了调整:
- 将全局的 API,即:
Vue.xxx
调整到应用实例(app
)上
- 将全局的 API,即:
2.x 全局 API(Vue ) | 3.x 实例 API (app ) |
---|---|
Vue.config.xxxx | app.config.xxxx |
Vue.config.productionTip | 移除 |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use |
Vue.prototype | app.config.globalProperties |
Vue.filter | 移除 |
Vue.extend | 移除 |
部分 API 移除
移除
v-on.native
修饰符父组件中绑定事件
<my-component v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent" />
Copied!子组件中声明自定义事件
<script> export default { emits: ['close'] // 由子组件指定哪些是自定义事件(自然父组件的click就可以作为原生事件被筛选出来) } </script>
Copied!
移除实例中的
$on
、$off
和$once
方法(即 vue3 去除了 EventBus)移除keyCode 作为 v-on 的修饰符,同时也不再支持
config.keyCodes
移除过滤器(filter)
过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。
......
# 2、vue3.2 发布
- 重大新特性:
SFC 单文件组件新特性
<script setup>
语法糖 🎉:script setup (opens new window)<style> v-bind
新特性:v-bind() (opens new window)
<script setup> import { ref } from 'vue' const color = ref('red') </script> <template> <button @click="color = color === 'red' ? 'green' : 'red'">Color is: {{ color }}</button> </template> <style scoped> button { color: v-bind(color); } </style>
Copied!
- 其他新特性
新特性 | 学习文档 |
---|---|
Web Components | definecustomelement (opens new window)、自定义元素 (opens new window) |
性能提升 | v-memo (opens new window) |
服务端渲染 | @vue/server-renderer (opens new window) |
Effect 作用域 API | effectScope (opens new window) |
# 3、vue3.3 发布
此版本侧重于开发人员体验改进 - 特别是 TypeScript 的 SFC
<script setup>
用法。
- 重大新特性
setup 语法糖中的参数类型
以前,在 和 defineEmits 的类型 defineProps 参数位置中使用的类型仅限于本地类型,并且仅支持类型文本和接口
让 defineProps 支持外部类型文件导入的方式,并支持一组有限的复杂类型
拓展
其实在 vue3.3 之前,我们也可以通过某种方式实现间接的引入外部类型文件:
<script setup lang="ts"> import type { PropsType } from './foo' interface IProps { data: PropsType } cosnt props = defineProps<IProps>() </script>
Copied!
// ./foo.ts export interface Props { name: string age: number }
Copied!
<script setup lang="ts"> import type { Props } from './foo' // imported + intersection type defineProps<Props & { extraProp?: string }>() </script>
Copied!
<script setup>
组件现在可以通过以下 generic 属性接受泛型类型参数
<script setup lang="ts" generic="T extends string | number, U extends Item"> import type { Item } from './types' defineProps<{ id: T list: U[] }>() </script>
Copied!
对原 defineEmits 参数的调用签名语法,提供一个更加简洁的语法
// 原来的调用签名语法 const emit = defineEmits<{ (e: 'change', id: number): void (e: 'update', value: string): void }>() // 3.3+:另一种更简洁的语法 const emit = defineEmits<{ change: [id: number] // 具名元组语法 update: [value: string] }>()
Copied!
- 其他新特性
defineSlots (opens new window)、defineOptions (opens new window)、toValue (opens new window)
- 原功能增强
- 实验性功能
defineModel