Vue3用法总结


highlight: a11y-dark

模板语法

1.属性绑定的值是 nullundefined时,则该属性会从元素上被移除:

<div :id="dynamicId"></div>

2.布尔型属性的值为 false时,该属性会从元素上被移除:

<button :disabled="isButtonDisabled">Button</button>

3.指令是带有 v- 前缀的特殊属性,如:v-html

v-bindv-on是特殊的内置指令,分别可以以简写方式书写,冒号后是指令参数:

<a v-bind:[attributeName]="url"> ... </a>
<!-- 简写 -->
<a :[attributeName]="url"> ... </a>

<a v-on:[eventName]="doSomething"> ... </a>
<!-- 简写 -->
<a @[eventName]="doSomething">

逻辑渲染

1.如果要使用v-if切换多个元素,就可以用 <template> 包裹:

<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>

注意事项

v-show 不支持在 <template> 元素上使用

不推荐同时使用 v-if 和 v-for,因为二者的优先级不明显;

v-ifv-for 同时存在于一个元素上时,v-if 的优先级高,这意味着 v-if 会首先被执行,且无法访问到 v-for 作用域内的变量:

v-for

// 通过参数二获取索引
<li v-for="(item, index) in items">
  {{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
// 直接解构对象元素
<li v-for="({ message }, index) in items">
  {{ message }} {{ index }}
</li>
// 可使用 `of` 替代 `in`,作用相同:
<div v-for="item of items"></div>
//遍历对象属性,参数二为属性名,参数三为索引:
<li v-for="(value, key,index) in myObject">
  {{ key }}: {{ value }}
</li>

输入绑定

修饰符

.lazy

默认情况下,v-model 在每次 input 事件后更新数据 (IME 拼字阶段除外),但可以通过添加 lazy 修饰符来改为在每次 change 事件后更新数据:

<!-- 在 "change" 事件后同步更新而不是 "input" -->
<input v-model.lazy="msg" />

.number

将输入自动转换为数字:(如果输入值无法被 parseFloat() 处理,则返回原始值)

<input v-model.number="age" />

.trim

自动去除输入内容中两端的空格:

<input v-model.trim="msg" />

侦听器

深层侦听器

watch浅层侦听时,嵌套属性变化并不会触发侦听器;

watch深侦听时,会遍历被侦听对象的所有嵌套属性,当被侦听对象过于复杂时,性能开销会很大;

1.0> 直接给 watch() 传入一个响应式对象,会隐式地创建一个深层侦听器:

const obj = reactive({ count: 0 })

watch(obj, (newValue, oldValue) => {
  // 在嵌套的属性变更时触发
  // 注意:`newValue` 此处和 `oldValue` 是相等的
  // 因为它们是同一个对象!
})

2.0> 如下浅层侦听,一个返回响应式对象的 getter 函数,只有在返回不同对象时,才会触发侦听回调:

watch(
  () => state.someObject,
  () => {
    // 仅当 state.someObject 被替换时触发
  }
)

watchEffect()

watch 只追踪明确侦听的数据源,并且仅在数据源改变时才会触发回调;

watchEffect会在回调同步执行过程中自动追踪所有能访问到的响应式属性,并在创建后会立即执行一次;

watchEffect(async () => {
  const response = await fetch(url.value)
  data.value = await response.json()
})

注意:watchEffect 仅在同步执行期间才追踪依赖;在使用异步回调时,只有在第一个 await 正常工作前能够访问到的属性才会被追踪;

回调的触发时机

如果在响应式状态更新后,同时触发了DOM更新和侦听器回调,则侧侦听器回调会在 Vue 组件 DOM 更新之前被调用,这意味着在侦听器回调中只能访问到 DOM 更新之前的状态。如果想在侦听器回调中能访问更新之后的DOM,就需要指明 flush: 'post' 选项:

watch(source, callback, {
  flush: 'post'
})

// 或

watchEffect(callback, {
  flush: 'post'
})

后置刷新的 watchEffect() 有个更方便的别名 watchPostEffect()

watchPostEffect(() => {
  /* 在 Vue 组件 DOM 更新后执行 */
})

停止侦听器

setup()<script setup> 中用同步语句创建的侦听器,会自动绑定到宿主组件实例上,并且在宿主组件卸载时自动停止。因此,大多数情况下无需关心如何停止侦听器。

一个关键点是,侦听器必须用同步语句创建:如果在异步回调中创建侦听器,那么它不会绑定到当前组件上,就必须手动停止,以防内存泄漏:

<script setup>
import { watchEffect } from 'vue'

// 它会自动停止
watchEffect(() => {})

// ...这个则不会!
setTimeout(() => {
  watchEffect(() => {})
}, 100)
</script>

要手动停止一个侦听器,要调用 watchwatchEffect 返回的函数:

const unwatch = watchEffect(() => {})

// ...当该侦听器不再需要时
unwatch()

注意,需要异步创建侦听器的情况很少,请尽可能选择同步创建;如果需要等待一些异步数据,可以使用条件式的侦听逻辑:

// 需要异步请求得到的数据
const data = ref(null)

watchEffect(() => {
  if (data.value) {
    // 数据加载后执行某些操作...
  }

组件基础

子组件

子组件通过 defineEmits 宏来声明需要抛出的事件,然后调用 $emit 方法出事件:

<script setup>
const emit = defineEmits(['enlarge-text'])

emit('enlarge-text')
</script>

动态组件

参数 is 是被注册的组件名或导入的组件对象

<component :is="tabs[currentTab]"></component>

组件传值

Props 声明

子组件使用 defineProps() 宏显式声明所接受的 props

<script setup>
const props = defineProps(['foo'])

console.log(props.foo)
</script>
// Props声明
defineProps({
  greetingMessage: String
})

// 属性传值
<MyComponent greeting-message="hello" />

静态 vs 动态 Prop

1.0> 静态传值:参数值是字符时,则无须绑定,直接赋值即可;

<BlogPost title="My journey with Vue" />

2.0> 动态传值:参数值是非字符时,则需要动态绑定(当参数类型为Boolean时,可以不传值,默认会转为true);

<!-- 变量 -->
<BlogPost :title="post.title" />

<!-- 表达式 -->
<BlogPost :title="post.title + ' by ' + post.author.name" />

<!-- Number -->
<BlogPost :likes="42" />

<!-- Boolean(仅写上 prop 但不传值,会隐式转换为 `true`) -->
<BlogPost is-published />

<!-- Boolean -->
<BlogPost :is-published="false" />

<!-- 数组 -->
<BlogPost :comment-ids="[234, 266, 273]" />

<!-- 对象 -->
<BlogPost :author="{ name: 'Veronica', company: 'Veridian Dynamics'}" />

使用一个对象绑定多个 prop

props 对象的属性可以分别当作 props 传入:

const form = {
  id: 1,
  title: 'My Journey with Vue'
}
<BlogPost v-bind="form" />
## 或
<BlogPost :id="id" :title="title" />

单向数据流

props 遵循单向绑定原则,数据状态向下流往子组件,而不会逆向传递;这避免了子组件修改父组件的情况,否则数据流将很容易变得混乱而难以理解。一般要在子组件中修改 prop 的需求无非以下两种场景:

1.0>prop 被用于传入初始值;这种情况下,最好是新定义一个局部数据属性,从 props 上获取初始值即可,如下:

const props = defineProps(['initialCounter'])

const counter = ref(props.initialCounter)

2.0> 需要对传入的 prop 值做进一步转换;这种情况中,最好是基于该 prop 值定义一个计算属性:

const props = defineProps(['size'])

const normalizedSize = computed(() => props.size.trim().toLowerCase())

Prop 校验

props声明时可以约束数据类型、是否必填;

defineProps({
  // 1.0> 基础类型检查
  // (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
  propA: Number,

  // 2.0> 多种可能的类型
  propB: [String, Number],

  // 3.0> 必传,且为 String 类型
  propC: {
    type: String,
    required: true
  },
  // 4.0> Number 类型的默认值
  propD: {
    type: Number,
    default: 100
  },
  // 5.0> 对象类型的默认值
  propE: {
    type: Object,
    // 对象或数组的默认值必须从一个工厂函数返回
    default(rawProps) {
      return { message: 'hello' }
    }
  },
  // 6.0> 自定义类型校验函数
  propF: {
    validator(value) {
      // The value must match one of these strings
      return ['success', 'warning', 'danger'].includes(value)
    }
  },
  // 7.0> 函数类型的默认值
  propG: {
    type: Function,
    // 不像对象或数组的默认,这不是一个工厂函数,这会是一个用来作为默认值的函数
    default() {
      return 'Default function'
    }
  }
})

注意:

defineProps() 宏中的参数不可以访问 <script setup> 中定义的其他变量,因为编译时整个表达式都会被移到外部的函数中。

所有 prop 默认都是可选的,除非声明了 required: true

Boolean 外的未传递可选 prop 都会有一个默认值 undefined

事件传递

触发与监听事件

## 子组件中
<button @click="$emit('someEvent')">click me</button>

## 父组件中
<MyComponent @some-event.once="callback" />

注:和原生 DOM 事件不一样,组件触发的事件没有冒泡机制;只能监听直接子组件触发的事件;平级或多层嵌套的组件间通信,应使用Vuex或Pinia;

声明事件

<template> 中可以使用$emit 直接抛出事件,但在 <script setup> 中要先通过 defineEmits() 宏声明事件;

<script setup>
const emit = defineEmits(['inFocus', 'submit'])

emit('submit')
</script>

defineEmits() 宏不能在子函数中使用,必须放在 <script setup> 的顶级作用域下。

emits选项还支持对象语法,它允许我们对触发事件的参数进行验证:

<script setup>
const emit = defineEmits({
  submit(payload) {
    // 通过返回值为 `true` 还是为 `false` 来判断
    // 验证是否通过
  }
})
</script>

在 TypeScript 中,还可以使用纯类型标注来声明触发的事件:

<script setup lang="ts">
const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()
</script>

注:如果原生事件的名字 (如 click) 被定义在 emits 选项中,则监听器只会监听组件触发的 click 事件而不再响应原生 click 事件。

v-model参数

默认情况下,v-model 在组件上使用 modelValue 作为 prop,并以 update:modelValue 作为对应事件;也可通过给 v-model 指定一个参数来更改默认 prop 的名称:

<MyComponent v-model:title="bookTitle" />

如下,在子组件中应声明一个 title prop,并通过触发 update:title 事件更新父组件值:

<!-- MyComponent.vue -->
<script setup>
defineProps(['title'])
defineEmits(['update:title'])
</script>

<template>
  <input
    type="text"
    :value="title"
    @input="$emit('update:title', $event.target.value)"
  />
</template>

透传 Attributes

Attributes 继承

“透传 attribute”是指传给一个组件,却没有被该组件声明为 propsemitsv-on 事件的属性 ;最常见的就是 class、style 和 id。

当子组件只有一个根元素时,透传的 attribute 会自动被添加到子组件的根元素上。例如:在父组件中为子组件添加的classstyle,会直接被透传到子组件的根元素上,并与子组件根元素上的classstyle合并。

v-on 监听器继承

v-on事件监听器同样也会被透传;如果子组件根元素自身也通过 v-on 绑定了事件监听器,则这个监听器和从父组件继承的监听器都会被触发。

深层组件继承

如果子组件的根元素又是一个组件,则透传属性会继续向下传递;但透传不包括声明过的 props 或针对 emits 声明事件的v-on侦听;也就是说,声明过的 props 和侦听函数已经被当前组件消费了,因此不会再往下传递。

禁用 Attributes 继承

如果不想要组件自动地继承 attribute,可以在组件选项中设置 inheritAttrs: false``;如果使用了 <script setup>,就需要一个额外的 <script> 块来书写这个选项声明:

<script>
// 使用普通的 <script> 来声明选项
export default {
  inheritAttrs: false
}
</script>

<script setup>
// ...setup 部分逻辑
</script>

最常见的需要禁用 attribute 继承的场景就是 attribute 需要应用在根节点以外的其他元素上;将inheritAttrs 设为false,就可以完全控制透传进来的 attribute 被如何使用。

透传进来的 attribute 可以在模板的表达式中直接用 $attrs 访问:

<span>Fallthrough attribute: {{ $attrs }}</span>

也可以通过 v-bind 将透传的属性应用到任意元素上,如下:

<button class="btn" v-bind="$attrs">click me</button>

注意:a>. 和 props 不同,透传 attributes 在 JavaScript 中保留了它们原始的大小写,所以像 foo-bar 这样 attribute 需要通过 $attrs['foo-bar'] 来访问。

b>. 像 @click 这样的一个 v-on 事件监听器将在此对象下被暴露为一个函数 $attrs.onClick

多根节点的 Attributes 继承

多根节点的组件没有自动透传的行为,因为Vue并不知道要将 attribute 透传到哪个根元素上;因此需要通过 v-bind 显式绑定 $attrs ,以明确需要将 attribute 透传到哪个根元素,如下:

<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>

在 JavaScript 中访问透传 Attributes

<script setup>中可以使用 useAttrs() API 来访问所有透传 attribute:

<script setup>
import { useAttrs } from 'vue'

const attrs = useAttrs()
</script>

注意:attrs对象是最新的透传 attribute,但它并不是响应式的 (考虑到性能因素);因此,不能通过侦听器去监听其变化;如果需要响应性,可以使用 prop。

插槽

渲染作用域

因为插槽内容在父组件模板中定义,因此可以访问父组件作用域,而无法访问子组件的数据;

总结:Vue 模板中的表达式只能访问其定义时所处的作用域,这和 JavaScript 的词法作用域规则是一致的。

默认内容

在外部没有提供任何内容的情况下,可以在<slot> 标签之间为插槽指定默认内容:

<button type="submit">
  <slot>
    Submit <!-- 默认内容 -->
  </slot>
</button>

具名插槽

<slot> 元素有一个特殊的name 属性,用来给插槽分配唯一 ID,以确定每一处要渲染的内容:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

这种带 name 的插槽被称为具名插槽,没有提供 name 的插槽会隐式命名为“default”;

要为具名插槽传入内容,需要使用含 v-slot 指令的 <template> 元素,并将目标插槽的名字传给该指令:

<BaseLayout>
  <template v-slot:header>
    <!-- header 插槽的内容放这里 -->
  </template>
</BaseLayout>

// `v-slot` 有对应的简写 `#`
<BaseLayout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>
</BaseLayout>

当组件同时接收默认插槽和具名插槽时,所有位于顶级的非 <template> 节点都被隐式地视为默认插槽内容:

<BaseLayout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

  <!-- 隐式的默认插槽 -->
  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</BaseLayout>

动态插槽名

动态指令参数v-slot 上也是有效的,即可以定义下面这样的动态插槽名:

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>

  <!-- 缩写为 -->
  <template #[dynamicSlotName]>
    ...
  </template>
</base-layout>

作用域插槽

默认情况下,插槽内容无法访问到子组件的数据;如果要在插槽中访问子组件的数据,就需要子组件主动传递 prop 数据对象传给插槽:

<div>
  <slot :text="greetingMessage" :count="1"></slot>
</div>

然后,在父组件中通过在子组件标签上定义 v-slot 指令,来接收插槽 props 对象:

<MyComponent v-slot="slotProps">
  {{ slotProps.text }} {{ slotProps.count }}
</MyComponent>

也可以在 v-slot 中使用解构:

<MyComponent v-slot="{ text, count }">
  {{ text }} {{ count }}
</MyComponent>

具名作用域插槽

具名插槽也是类似的,插槽 props 可以作为 v-slot 指令的值被访问到v-slot:name="slotProps",如下

<MyComponent>
  <template #header="headerProps">
    {{ headerProps }}
  </template>

  <template #default="defaultProps">
    {{ defaultProps }}
  </template>

  <template #footer="footerProps">
    {{ footerProps }}
  </template>
</MyComponent>

向具名插槽中传入 props:

<slot name="header" message="hello"></slot>

注意:插槽上的 name 是一个 Vue 特别保留的属性,不会作为 props 传递给插槽;headerProps的结果是{ message="hello"}

依赖注入

Prop 逐级透传问题

当组件嵌套太深时,通过 Prop 逐级透传数据会变得很繁琐;而provideinject 可以将父组件作为依赖提供者,为所有后代组件注入由父组件提供的依赖。

Provide (提供)

为后代组件提供数据,需要用到 provide() 函数:

<script setup>
import { provide } from 'vue'

provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
</script>

provide() 函数接收两个参数;参数一是注入名,参数二注入值,注入值可以是任意类型,包括响应式状态;后代组件使用注入名来查找期望的值;可以多次调用 provide(),使用不同的注入名,注入不同的依赖值;当注入值为响应式状态时(如:ref),后代组件就可以由此和提供者建立响应式联系。

应用层 Provide

可以在整个应用层面提供依赖:

import { createApp } from 'vue'

const app = createApp({})

app.provide(/* 注入名 */ 'message', /* 值 */ 'hello!')

在应用级别提供的数据在该应用内的所有组件中都可注入;

Inject (注入)

要注入上层组件提供的数据,需使用 inject() 函数:

<script setup>
import { inject } from 'vue'

const message = inject('message')
</script>

如果提供的值是一个 ref,注入进来的会是该 ref 对象,而不会自动解包为其内部的值;

注入默认值

默认情况下,如果注入名不存在,则会抛出一个运行时警告;但可为 inject 声明一个默认值,和 props 类似:

const value = inject('message', '这是默认值')

默认值也可通过函数或初始化类来获得:

const value = inject('key', () => new ExpensiveClass())

和响应式数据配合使用

尽量将任何对响应式状态的变更都保持在供给方组件中,这样可以确保所提供状态的声明和变更操作都内聚在同一个组件内,使其更容易维护。

作为依赖供给方,如果想确保提供的数据不能被注入方组件更改,可以使用 readonly() 来包装提供的值:

<script setup>
import { ref, provide, readonly } from 'vue'

const count = ref(0)
provide('read-only-count', readonly(count))
</script>

使用 Symbol 作注入名

大型应用中,可能会包含非常多的依赖提供,或者你正在编写提供给其他开发者使用的组件库,建议最好使用 Symbol 来作为注入名以避免潜在的冲突。

推荐在一个单独的文件中导出这些注入名 Symbol:

// keys.js
export const myInjectionKey = Symbol()
// 在供给方组件中
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'

provide(myInjectionKey, { /*
  要提供的数据
*/ });
// 注入方组件
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'

const injected = inject(myInjectionKey)

异步组件

基本用法

在大型项目中,可能需要拆分应用为更小的块,并仅在需要时从服务器加载组件;Vue 提供了 defineAsyncComponent 方法来实现此功能, 该方法接收一个返回 Promise 的加载函数,这个 Promise 的 resolve 方法从服务器获取组件,也可以调用 reject(reason) 表明加载失败,如下:

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() => {
  return new Promise((resolve, reject) => {
    // ...从服务器获取组件
    resolve(/* 获取到的组件 */)
  })
})
// ... 像使用其他一般组件一样使用 `AsyncComp`

因为 ES 模块动态导入也会返回一个 Promise,所以多数情况下推荐将 defineAsyncComponent 和 ES动态导入搭配使用;Vite 和 Webpack 都支持此语法 ,并且会将其作为打包时的代码分割点,如下:

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
)

最后得到的 AsyncComp 是一个外层包装过的组件,仅在页面需要渲染时才会调用加载函数;AsyncComp 会将收到的 props 和插槽传给内部组件,所以可以使用这个异步的包装组件无缝地替换原始组件,同时实现延迟加载。

与普通组件一样,异步组件可以使用 app.component() 全局注册

app.component('MyComponent', defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
))

也可以直接在父组件中直接定义它们:

<script setup>
import { defineAsyncComponent } from 'vue'

const AdminPage = defineAsyncComponent(() =>
  import('./components/AdminPageComponent.vue')
)
</script>

<template>
  <AdminPage />
</template>

加载与错误状态

异步操作一般会涉及到加载中、加载错误等状态,因此 defineAsyncComponent() 也支持在高级选项中处理这些状态:

const AsyncComp = defineAsyncComponent({
  // 加载函数
  loader: () => import('./Foo.vue'),

  // 加载异步组件时使用的组件
  loadingComponent: LoadingComponent,

  // 展示加载组件前的延迟时间,默认为 200ms
  delay: 200,

  // 加载失败后展示的组件
  errorComponent: ErrorComponent,

  // 如果提供了一个 timeout 时间限制,并超时了
  // 也会显示这里配置的报错组件,默认值是:Infinity
  timeout: 3000
})

加载组件会在内部组件加载前(时)先行显示,但在加载组件显示之前默认有 200ms 延迟,这是因为在网络状况较好时,加载完成得很快,加载组件和最终组件之间的替换太快可能产生闪烁,反而影响用户感受。报错组件会在加载器函数返回的 Promise 抛错时被渲染;最后,还可以指定一个超时时间,在请求耗时超过指定时间时也会渲染报错组件。

组合式函数

组合式函数

“组合式函数”是利用 Vue 组合式 API 来封装和复用有状态逻辑的函数;与普通函数抽取不同的是在组合式函数中可以保留响应性、访问模板页面,就相当于在 Vue 页面中编写代码一样。

通过抽取组合式函数改善代码结构

<script setup>
import { useFeatureA } from './featureA.js'
import { useFeatureB } from './featureB.js'
import { useFeatureC } from './featureC.js'

const { foo, bar } = useFeatureA()
const { baz } = useFeatureB(foo)
const { qux } = useFeatureC(baz)
</script>

和无渲染组件的对比

相比无渲染组件,组合式函数不会产生额外的组件实例开销,因此推荐在纯逻辑复用时使用组合式函数,在需要同时复用逻辑和视图布局时使用无渲染组件。

自定义指令

基本介绍

自定义指令则是为了复用涉及普通元素 DOM 访问的逻辑。

<script setup>
// 在模板中启用 v-focus
const vFocus = {
  mounted: (el) => el.focus()
}
</script>

<template>
  <input v-focus />
</template>

<script setup> 中,任何以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令,上述示例中vFocus 即可以在模板中以 v-focus 的形式使用。

自定义指令全局注册:

const app = createApp({})

// 使 v-focus 在所有组件中都可用
app.directive('focus', {
  /* ... */
})

注意:只有当所需功能只能通过直接 DOM 操作来实现时,才应该使用自定义指令。其他情况下应该尽可能地使用 v-bind 这样的内置指令来声明式地使用模板,这样更高效,也对服务端渲染更友好。
注意:只有当所需功能只能通过直接 DOM 操作来实现时,才应该使用自定义指令。其他情况下应该尽可能地使用 v-bind 这样的内置指令来声明式地使用模板,这样更高效,也对服务端渲染更友好。

指令钩子

自定义指令有很多钩子函数(都是可选的),分别在不阶段被调用,常用的是mounted,如下:

const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode, prevVnode) {
    // 下面会介绍各个参数的细节
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode, prevVnode) {}
}

内置组件

Teleport

用于将组件内部的一部分模板“传送”到其它 DOM 结构中去;
<Teleport> 接受两个参数,参数一是传送的目的地, 是一个 CSS 选择器字符串;参数二是可用于动态的控制传送是否生效或开始;
最典型的应用场景是,将代码片段传送到 dialog 模态框中去;

<Teleport to="body" :disabled="isMobile">
  <div v-if="open" class="modal">
    <p>Hello from the modal!</p>
    <button @click="open = false">Close</button>
  </div>
</Teleport>

官方文档:https://v3.cn.vuejs.org/guide/teleport.html#%E4%B8%8E-vue-components-%E4%B8%80%E8%B5%B7%E4%BD%BF%E7%94%A8

Transition

<Transition> 会在一个元素或组件进入和离开 DOM 时应用动画。

TransitionGroup

<TransitionGroup> 会在 v-for 列表中的元素或组件被插入、移动、移除时应用动画。

KeepAlive

<KeepAlive> 是一个内置组件,用于在多个组件间动态切换时缓存被移除的组件实例。

Suspense

<Suspense> 是一个内置组件,用于在组件树中协调对异步依赖的处理,它让可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容