不知江月待何人!

132 阅读13分钟

Vue3纲领

Vue--基础篇

Vue知识篇

vue3响应式系统-propx代理
  1. vue 3 利用 JavaScript 的 Proxy 对象来创建一个响应式代理。Proxy 对象可以拦截并定义基本操作,如属性读取、属性赋值、属性删除等。这使得 Vue 3 能够更高效地追踪依赖关系和执行更新。
  2. Reactivity API
  3. 依赖追踪 当使用 reactive()ref() 创建响应式对象时,Vue 3 会自动追踪对这些对象的所有操作(如读取和赋值)。当一个组件的渲染函数或计算属性被触发时,它会收集这些依赖。
  4. 触发更新 当依赖的响应式数据发生变化时(例如,修改了对象的属性),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'
});

生命周期

    1. setup
    1. onBeforeMount() 在组件被挂载之前被调用。
    1. onMounted() 在组件挂载完成后执行。
    1. onBeforeUpdate() 在组件即将因为响应式状态变更而更新其 DOM 树之前调用。
    1. onUpdated() 在组件因为响应式状态变更而更新其 DOM 树之后调用。
    1. onBeforeUnmount() 在组件实例被卸载之前调用。
    1. onUnmounted() 在组件实例被卸载之后调用。
    1. onErrorCaptured() 在捕获了后代组件传递的错误时调用。
    1. onActivated() 若组件实例是 <KeepAlive>缓存树的一部分,当组件被插入到 DOM 中时调用。
    1. onDeactivated() 若组件实例是 <KeepAlive>缓存树的一部分,当组件从 DOM 中被移除时调用。
    1. onServerPrefetch() 注册一个异步函数,在组件实例在服务器上被渲染之前调用。

Vue--金丹期

computed()计算属性

  • 作用:默认接受一个 getter、具有 set 修改 return数据、 函数返回一个只读的响应式 ref对象。具有缓存性、只有数据变化后才会调用。该 ref 通过 .value 暴露 getter 函数的返回值。它也可以接受一个带有 getset 函数的对象来创建一个可写的 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;
}
  1. v-enter-from:进入动画的起始状态。在元素插入之前添加,在元素插入完成后的下一帧移除。
  2. v-enter-active:进入动画的生效状态。应用于整个进入动画阶段。在元素被插入之前添加,在过渡或动画完成之后移除。这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型。
  3. v-enter-to:进入动画的结束状态。在元素插入完成后的下一帧被添加 (也就是 v-enter-from 被移除的同时),在过渡或动画完成之后移除。
  4. v-leave-from:离开动画的起始状态。在离开过渡效果被触发时立即添加,在一帧后被移除。
  5. v-leave-active:离开动画的生效状态。应用于整个离开动画阶段。在离开过渡效果被触发时立即添加,在过渡或动画完成之后移除。这个 class 可以被用来定义离开动画的持续时间、延迟与速度曲线类型。
  6. v-leave-to:离开动画的结束状态。在一个离开动画被触发后的下一帧被添加 (也就是 v-leave-from 被移除的同时),在过渡或动画完成之后移除。

TransitionGroup用于对 v-for 列表中的元素或组件的插入、移除和顺序改变添加动画效果。

  • 默认情况下,它不会渲染一个容器元素。但你可以通过传入 tag prop 来指定一个元素作为容器元素来渲染。
  • 过渡模式在这里不可用,因为我们不再是在互斥的元素之间进行切换。
  • 列表中的每个元素都必须有一个独一无二的 key attribute。
  • 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-modelVuex或者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虚拟元素中
  • vue3Proxy 替代vue2 object.defineProperty(),带来性能提升
  • Composition API Vue 3 引入了 Composition API,这是一个基于函数的 API,允许你将组件逻辑以更灵活、可重用和可组合的方式组织。可与现有的Options API 选项式一起使用
  • 移除了一些废弃的功能 如:过滤器、maxix
  • Teleport传送组件,绑定Dom节点
  • 绑定全局方法及事件 app.config.globalProperties vue中引入 const { proxy } = getCurrentInstance()
  • 支持多个绑定-事件处理器同理 v-model MyComponent v-model:value="message" v-model:title="title"
  • Vue3是基于typescipt编写的,可以享受到自动的类型定义提示
  • 舍弃Vue2 Mixins 使用 Vue3 Hooks思想

Mixins: 代码混入机制:将mixin对象的内容合并到组件中

选项式合并:相同选项(data、methods等)会进行特定策略的合并

命名冲突风险:多个mixin之间或与组件之间容易产生命名冲突

关系不清晰:难以追踪属性/方法的来源

Hooks:函数式组合:通过函数调用明确地组合逻辑

明确的数据来源:每个hook返回的数据来源清晰

命名空间隔离:hook内部的变量名不会与组件或其他hook冲突

按需使用:可以选择性使用hook返回的内容