Vue3纲领
Vue--基础篇
Vue知识篇
vue3响应式系统-propx代理
- vue 3 利用 JavaScript 的
Proxy对象来创建一个响应式代理。Proxy对象可以拦截并定义基本操作,如属性读取、属性赋值、属性删除等。这使得 Vue 3 能够更高效地追踪依赖关系和执行更新。 ReactivityAPI- 依赖追踪 当使用
reactive()或ref()创建响应式对象时,Vue 3 会自动追踪对这些对象的所有操作(如读取和赋值)。当一个组件的渲染函数或计算属性被触发时,它会收集这些依赖。 - 触发更新 当依赖的响应式数据发生变化时(例如,修改了对象的属性),Vue 3 的响应式系统会通知所有依赖于这些数据的组件重新渲染。这是通过以下方式实现的:
触发器(Trigger):当一个响应式对象的属性被修改时,会触发一个通知过程,通知所有依赖于该属性的组件进行更新。副作用(Effect):在 Vue 3 中,每当组件的渲染函数或计算属性被执行时,它会被标记为一个“副作用”。当依赖发生变化时,这些副作用会被重新执行。
Vue-指令
- v-memo(支持3.2+版本) 作用: 主要用于优化组件的渲染方面性能,能控制达到某个条件才重新当堂组件,否则不重新渲染。
v-memo会缓存DOM,只有当指定的数据发生变化时才会重新渲染,从而减少渲染次数提升性能。
v-memo 指令实现原理: Vue初始化组件时会识别是否有v-memo标记,如果有就把这部分vnode缓存起来,当数据变化时会对比依赖是否变化,变化再重新渲染。
示例代码: 用v-memo 绑定了arr,那么当arr的值变化才会重新渲染,否则不会重新渲染。
注意: 用v-memo来指定触发渲染的条件,但只建议在长列表或者说复杂的渲染结构才使用。
<template>
<div>
<ul v-memo="arr">
<li v-for="(item, index) in arr" :key="index">
{{ item.text }}
</li>
</ul>
</div>
</template>
<script setup>
import { ref } from 'vue';
let arr = ref([
{ text: '内容1' },
{ text: '内容2' },
{ text: '内容3' }
]);
setInterval(() => {
arr.value[1].text = '修改2';
}, 2000);
</script>
- v-pre
作用: 在标签上使用
v-pre后,Vue编译器会自动跳过这个元素的编译。使用此内置指令后会被视为静态内容。
v-pre指令实现原理: Vue初次编译时如果看到有v-pre标记,那么跳过这部分的编译,直接当成原始的HTML插入到DOM中。
示例代码: 常规文本会正常编译成您好!,但使用了v-pre后会跳过编译原样输出{{ message }}。
<template>
<div>
<h2>常规: {{ message }}</h2>
<h2 v-pre>使用v-pre后: {{ message }}</h2>
</div>
</template>
<script setup>
import { ref } from 'vue';
let message = ref('您好!');
</script>
- v-once 作用:在标签上使用
v-once能使元素或者表达式只渲染一次。首次渲染之后,后面数据再发生变化时使用了v-once的地方都不会更新,因此用在数据不需要变化的地方就能进行性能优化。
v-once指令实现原理: Vue组件初始化时会标记上v-once,首次渲染会正常执行,后续再次渲染时如果看到有v-once标记则跳过二次渲染。
示例代码: 直接作用在标签上,可以是普通标签也可以是图片标签,当2S后数据变化时标签上的值不会重新渲染更新。
注意: 作用v-once会使属性失去响应式,要确保这个地方不需要响应式更新才能使用,否则会导致数据和页面视图对不上。
<template>
<div>
<span v-once>{{ message }}</span>
<img v-once :src="imageUrl"></img>
</div>
</template>
<script setup>
import { ref } from 'vue';
let message = ref('Vue指令!');
let imageSrc = ref('/path/my/image.jpg');
setTimeout(() => {
message.value = '修改内容!';
imageUrl.value = '/new/path/my/images.jpg';
}, 2000);
</script>
- v-on元素绑定事件监听器之--修饰符。
- `.stop` - 调用 `event.stopPropagation()`。
- `.prevent` - 调用 `event.preventDefault()`。
- `.capture` - 在捕获模式添加事件监听器。
- `.self` - 只有事件从元素本身发出才触发处理函数。
- `.{keyAlias}` - 只在某些按键下触发处理函数。
- `.once` - 最多触发一次处理函数。
- `.left` - 只在鼠标左键事件触发处理函数。
- `.right` - 只在鼠标右键事件触发处理函数。
- `.middle` - 只在鼠标中键事件触发处理函数。
- `.passive` - 通过 `{ passive: true }` 附加一个 DOM 事件。
- v-model 在表单输入元素或组件上创建双向绑定。
期望的绑定值类型:根据表单输入元素或组件输出的值而变化
.lazy - 监听 change 事件而不是 input .number - 将输入的合法字符串转为数字 .trim- 移除输入内容两端空格。
- v-指令:自定义指令:如
复制,按钮权限等。
// 定义一个名为 'v-focus' 的自定义指令
app.directive('focus', {
// 当被绑定的元素挂载到DOM中时…… (生命周期)
mounted(el,binding) {
// el实例 , binding传递得值
// 处理逻辑 el.focus();
}
});
示例: <template> <input v-focus /></template>
app.directive('copy', {
mounted(el, binding) {
el.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(binding.value);
// 或者其他反馈方式,如显示一个提示框或更改按钮文本等。
useMessage().success('复制成功')
} catch (err) {
console.error('Failed to copy: ', err);
}
});
}
});
Vue3样式绑定
- 类名绑定
<div :class="classObject">我是一个带类名的div</div>
const classObject = reactive({
active: true,
'text-danger': false
});
- 动态绑定多个类名
<div :class="[activeClass, errorClass]">我是一个带多个类名的div</div>
const activeClass = ref('active');
const errorClass = ref('text-danger');
- 常用
<div v-for="(itme, index) in 5" :key="index" class="tab"
:class="activeTab == index?'activeTab' : ''" @click="activeTab = index">
{{ itme }}
</div>
- 内联绑定
<div :style="styleObject">我是一个带样式的div</div>
const styleObject = reactive({
color: 'blue',
fontSize: '14px'
});
生命周期
-
- setup
-
- onBeforeMount() 在组件被挂载之前被调用。
-
- onMounted() 在组件挂载完成后执行。
-
- onBeforeUpdate() 在组件即将因为响应式状态变更而更新其 DOM 树之前调用。
-
- onUpdated() 在组件因为响应式状态变更而更新其 DOM 树之后调用。
-
- onBeforeUnmount() 在组件实例被卸载之前调用。
-
- onUnmounted() 在组件实例被卸载之后调用。
-
- onErrorCaptured() 在捕获了后代组件传递的错误时调用。
-
- onActivated() 若组件实例是
<KeepAlive>缓存树的一部分,当组件被插入到 DOM 中时调用。
- onActivated() 若组件实例是
-
- onDeactivated() 若组件实例是
<KeepAlive>缓存树的一部分,当组件从 DOM 中被移除时调用。
- onDeactivated() 若组件实例是
-
- onServerPrefetch() 注册一个异步函数,在组件实例在服务器上被渲染之前调用。
Vue--金丹期
computed()计算属性
- 作用:默认接受一个
getter、具有set修改return数据、 函数返回一个只读的响应式 ref对象。具有缓存性、只有数据变化后才会调用。该 ref 通过.value暴露 getter 函数的返回值。它也可以接受一个带有get和set函数的对象来创建一个可写的 ref 对象。 - 示例
<template>
<div>
{{ haveChineseName }}
</div>
</template>
<script setup lang="ts">
import { reactive,computed } from "vue";
const onePeople = reactive({
name: "Sam Xiaoguai",
chineseName: [
"萨姆",
"小乖"
]
});
// 一个计算属性 ref
const haveChineseName = computed<string>(() => {
return onePeople.chineseName.length > 0 ? "有中文名" : "没有"
})
</script>
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: (val) => {
count.value = val - 1
}
})
plusOne.value = 1
console.log(count.value) // 0
watchEffect() 立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。
- 基础示例
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> 输出 0
count.value++
// -> 输出 1
- 副作用清除
watchEffect(async (onCleanup) => {
const { response, cancel } = doAsyncWork(id.value)
// `cancel` 会在 `id` 更改时调用
// 以便取消之前
// 未完成的请求
onCleanup(cancel)
data.value = await response
})
- 停止侦听器
const stop = watchEffect(() => {})
// 当不再需要此侦听器时:
stop()
- 暂停/恢复侦听器(3.5版+)
const { stop, pause, resume } = watchEffect(() => {})
// 暂停侦听器
pause()
// 稍后恢复
resume()
// 停止
stop()
- 副作用清理:
watchEffect(async (onCleanup) => {
const { response, cancel } = doAsyncWork(newId)
// 如果 `id` 变化,则调用 `cancel`,
// 如果之前的请求未完成,则取消该请求
onCleanup(cancel)
data.value = await response
})
- 3.5+ 中的副作用清理:
import { onWatcherCleanup } from 'vue'
watchEffect(async () => {
const { response, cancel } = doAsyncWork(newId)
// 如果 `id` 变化,则调用 `cancel`,
// 如果之前的请求未完成,则取消该请求
onWatcherCleanup(cancel)
data.value = await response
})
watch 侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。
- 第三个可选的参数是一个对象,支持以下这些选项:
immediate:在侦听器创建时立即触发回调。第一次调用时旧值是undefined。deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。在 3.5+ 中,此参数还可以是指示最大遍历深度的数字。参考深层侦听器。flush:调整回调函数的刷新时机。参考回调的刷新时机及watchEffect()。onTrack / onTrigger:调试侦听器的依赖。参考调试侦听器。once:(3.4+) 回调函数只会运行一次。侦听器将在回调函数首次运行后自动停止。
与 watchEffect() 相比,watch() 使我们可以:
-
懒执行副作用;更加明确是应该由哪个状态触发侦听器重新执行;可以访问所侦听状态的前一个值和当前值。
-
基础示例
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
onWatcherCleanup() 注册一个清理函数,在当前侦听器即将重新运行时执行。只能在 watchEffect 作用函数或 watch 回调函数的同步执行期间调用 (即不能在异步函数的 await 语句之后调用)。
Vue-元婴期之内置组件
- Transition元素或组件进入和离开 DOM 时应用动画,可以由以下的条件之一触发
- 由
v-if所触发的切换 - 由
v-show所触发的切换 - 由特殊元素
<component>切换的动态组件 - 改变特殊的
key属性
组件示例:
<Transition>
<p v-if="show">hello</p>
</Transition>
CSS 过渡 class属性示例
/* ### CSS 过渡 class */
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
v-enter-from:进入动画的起始状态。在元素插入之前添加,在元素插入完成后的下一帧移除。v-enter-active:进入动画的生效状态。应用于整个进入动画阶段。在元素被插入之前添加,在过渡或动画完成之后移除。这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型。v-enter-to:进入动画的结束状态。在元素插入完成后的下一帧被添加 (也就是v-enter-from被移除的同时),在过渡或动画完成之后移除。v-leave-from:离开动画的起始状态。在离开过渡效果被触发时立即添加,在一帧后被移除。v-leave-active:离开动画的生效状态。应用于整个离开动画阶段。在离开过渡效果被触发时立即添加,在过渡或动画完成之后移除。这个 class 可以被用来定义离开动画的持续时间、延迟与速度曲线类型。v-leave-to:离开动画的结束状态。在一个离开动画被触发后的下一帧被添加 (也就是v-leave-from被移除的同时),在过渡或动画完成之后移除。
TransitionGroup用于对 v-for 列表中的元素或组件的插入、移除和顺序改变添加动画效果。
- 默认情况下,它不会渲染一个容器元素。但你可以通过传入
tagprop 来指定一个元素作为容器元素来渲染。 - 过渡模式在这里不可用,因为我们不再是在互斥的元素之间进行切换。
- 列表中的每个元素都必须有一个独一无二的
keyattribute。 - CSS 过渡 class 会被应用在列表内的元素上,而不是容器元素上。
<TransitionGroup> 对一个 v-for 列表添加进入 / 离开动画的示例:
<TransitionGroup name="list" tag="ul">
<li v-for="item in items" :key="item">
{{ item }}
</li>
</TransitionGroup>
.list-enter-active,
.list-leave-active {
transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(30px);
}
<KeepAlive>的功能是在多个组件间动态切换时缓存被移除的组件实例。通常与component 动态组件搭配使用
示例
<!-- 非活跃的组件将会被缓存! -->
<KeepAlive>
<component :is="activeComponent" />
</KeepAlive>
属性-包含/排除
默认会缓存内部的所有组件实例,但我们可以通过 include包含缓存内容 和 exclude排除缓存内容 prop 来定制该行为。也可以通过传入 max prop 来限制可被缓存的最大组件实例数。
<!-- 以英文逗号分隔的字符串 -->
<KeepAlive include="a,b" :max="5">
<component :is="view" />
</KeepAlive>
<!-- 正则表达式 (需使用 `v-bind`) -->
<KeepAlive :include="/a|b/">
<component :is="view" />
</KeepAlive>
<!-- 数组 (需使用 `v-bind`) -->
<KeepAlive :include="['a', 'b']">
<component :is="view" />
</KeepAlive>
但想要被缓存、需要根据组件的 name 选项进行匹配,所以组件如果想要条件性地被 KeepAlive 缓存,就必须显式声明一个 name 选项。
TIP 在3.2.34 或以上的版本中,使用 <script setup> 的单文件组件会自动根据文件名生成对应的 name 选项,无需再手动声明。
<Teleport> 它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。通常用于模态弹框使用。具体
<button @click="open = true">Open Modal</button>
<Teleport to="body">
<div v-if="open" class="modal">
<p>Hello from the modal!</p>
<button @click="open = false">Close</button>
</div>
</Teleport>
组件插槽
组件通信
父传子 defineProps
defineProps({ name:{ type:String, default:'我是默认值' } })
子传父 defineEmits
<!-- 子组件发送 -->
const emit = defineEmits(['success'])
const childUpdate = ()=>{
emit("success",'我是子组件的值')
}
<!-- 父组件接收事件绑定方法 -->
<DialogForm ref="formRef" @success="getList" />
方法导出 defineExpose
<!-- 子组件 -->
defineExpose({ open }) // 提供 open 方法
<!-- 父组件 -->
<DialogForm ref="formRef" />
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, row?: any) => {
formRef.value.open(type, row)
}
祖传后代 provide和inject
- 示例1
<!-- 祖组件 -->
const data = ref(0);
provide('setData',data)
<!-- 任何后代组件 -->
let data = inject("setData",'默认值')
- 示例2
<!-- 祖组件 -->
const increment = () => count.value++;// 提供修改方法
provide('increment', increment);
<!-- 任何后代组件 -->
const increment = inject('increment');
increment(); // 调用父组件提供的方法
从 Setup 上下文中返回 attrs 对象useAttrs()
<!-- 父组件 -->
<HidtButton :message="message" />
<!-- 子组件 -->
import { useAttrs } from 'vue'
let attrs = useAttrs()
console.log(attrs)
其他
v-model、Vuex或者Pinia状态管理,mitt事件总线等
Vue-路由
HTML5 History模式:Vue Router 4默认使用HTML5 History模式构建URL,使URL更加美观,同时支持传统的哈希模式。
路由守卫:Vue Router 4在路由守卫方面进行了优化,例如,在全局守卫中,不再需要显式调用next()函数,可以通过返回对象或Promise来中断导航。
// Vue Router 4
router.beforeEach((to, from) => {
if (to.meta.requiresAuth && !isAuthenticated) {
return { path: '/login' };
}
});
// Vue Router 3
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !isAuthenticated) {
next('/login');
} else {
next();
}
});
路由元信息:Vue Router 4允许在路由记录中存储自定义信息(如标题、是否需要认证等),这些信息可以在路由守卫、组件内部或其他地方被访问和使用。
const routes = [
{
path: '/dashboard',
component: Dashboard,
meta: { requiresAuth: true }
}
];
Vue3比较Vue2有哪些更新
- V3 组件可以没有根标签了允许多个根组件,内部会将多个标签包含在一个
Fragment虚拟元素中 - vue3
Proxy替代vue2object.defineProperty(),带来性能提升 - Composition API Vue 3 引入了 Composition API,这是一个基于函数的 API,允许你将组件逻辑以更灵活、可重用和可组合的方式组织。可与现有的
Options API选项式一起使用 - 移除了一些废弃的功能 如:过滤器、maxix
- Teleport传送组件,绑定Dom节点
- 绑定全局方法及事件
app.config.globalPropertiesvue中引入const { proxy } = getCurrentInstance() - 支持多个绑定-事件处理器同理
v-modelMyComponent v-model:value="message" v-model:title="title" Vue3是基于typescipt编写的,可以享受到自动的类型定义提示- 舍弃Vue2 Mixins 使用 Vue3 Hooks思想
Mixins: 代码混入机制:将mixin对象的内容合并到组件中
选项式合并:相同选项(data、methods等)会进行特定策略的合并
命名冲突风险:多个mixin之间或与组件之间容易产生命名冲突
关系不清晰:难以追踪属性/方法的来源
Hooks:函数式组合:通过函数调用明确地组合逻辑
明确的数据来源:每个hook返回的数据来源清晰
命名空间隔离:hook内部的变量名不会与组件或其他hook冲突
按需使用:可以选择性使用hook返回的内容