# 一、逻辑复用
# 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 }
}
- 使用示例
<script setup>
import { useMouse } from './mouse.js'
const { x, y } = useMouse()
</script>
<template>Mouse position is at: {{ x }}, {{ y }}</template>
计数器: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)
}
}
- main.js
import i18nPlugin from './plugins/i18n'
app.use(i18nPlugin, {
greetings: {
hello: '你好'
}
})
- 使用
<template>
<h1>{{ $translate('greetings.hello') }}</h1>
</template>
<script setup>
import { inject } from 'vue'
const i18n = inject('i18n')
console.log(i18n.greetings.hello)
</script>
下面写一个真实开发的示例:
- main.ts
import registerIcons from './global/register-element-icons.ts'
app.use(registerIcons)
- 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
# 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
}
})
提示
- 在 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>
- 全局自定义指令
const app = createApp({})
// 使 v-focus 在所有组件中都可用
app.directive('color', (el, binding) => {
// 这会在 `mounted` 和 `updated` 时都调用
el.style.color = binding.value
})
# 二、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>
Vue3.0 中做出了调整,改为使用
:deep()
这个伪类:vue3 官方文档中的相关描述:CSS 功能 (opens new window)
<style scoped> .a :deep(.b) { /* ... */ } </style>
全局 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()
}
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" />
子组件中声明自定义事件
<script> export default { emits: ['close'] // 由子组件指定哪些是自定义事件(自然父组件的click就可以作为原生事件被筛选出来) } </script>
移除实例中的
$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>
- 其他新特性
新特性 | 学习文档 |
---|---|
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>
// ./foo.ts
export interface Props {
name: string
age: number
}
<script setup lang="ts">
import type { Props } from './foo'
// imported + intersection type
defineProps<Props & { extraProp?: string }>()
</script>
<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>
对原 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]
}>()
- 其他新特性
defineSlots (opens new window)、defineOptions (opens new window)、toValue (opens new window)
- 原功能增强
- 实验性功能
defineModel