示例
代码语言:javascript复制<span v-text="msg"></span>
<!-- 等同于 -->
<span>{{msg}}</span>
在你的站点上动态渲染任意的 HTML 是非常危险的,因为它很容易导致 [XSS 攻击]。请只对可信内容使用 HTML 插值,绝不要将用户提供的内容作为插值
在[单文件组件],scoped
样式将不会作用于 v-html
里的内容,因为 HTML 内容不会被 Vue 的模板编译器解析。如果你想让 v-html
的内容也支持 scoped CSS,你可以使用 [CSS modules]或使用一个额外的全局 <style>
元素,手动设置类似 BEM 的作用域策略。
当同时使用时,v-if
比 v-for
优先级更高。我们并不推荐在一元素上同时使用这两个指令
表示 v-if
或 v-if
/ v-else-if
链式调用的“else 块”。
基于原始数据多次渲染元素或模板块。
- 期望的绑定值类型:
Array | Object | number | string | Iterable
详细信息
指令值必须使用特殊语法 alias in expression
为正在迭代的元素提供一个别名:
<div v-for="item in items">
{{ item.text }}
</div>
或者,你也可以为索引指定别名 (如果用在对象,则是键值):
代码语言:javascript复制<div v-for="(item, index) in items"></div>
<div v-for="(value, key) in object"></div>
<div v-for="(value, name, index) in object"></div>
v-for
的默认方式是尝试就地更新元素而不移动它们。要强制其重新排序元素,你需要用特殊 attribute(归于) (归于) key
来提供一个排序提示:
<div v-for="item in items" :key="item.id">
{{ item.text }}
</div>
给元素绑定事件监听器。
- 缩写:
@
- 期望的绑定值类型:
Function | Inline Statement | Object (不带参数)
- 参数:
event
(使用对象语法则为可选项) - 修饰符:
.stop
- 调用event.stopPropagation()
。.prevent
- 调用event.preventDefault()
。.capture
- 在捕获模式添加事件监听器。.self
- 只有事件从元素本身发出才触发处理函数。.{keyAlias}
- 只在某些按键下触发处理函数。.once
- 最多触发一次处理函数。.left
- 只在鼠标左键事件触发处理函数。.right
- 只在鼠标右键事件触发处理函数。.middle
- 只在鼠标中键事件触发处理函数。.passive
- 通过{ passive: true }
附加一个 DOM 事件。
当监听原生 DOM 事件时,方法接收原生事件作为唯一参数。如果使用内联声明,声明可以访问一个特殊的 event 变量:v-on:click="handle('ok',
示例:
代码语言:javascript复制<!-- 方法处理函数 -->
<button v-on:click="doThis"></button>
<!-- 动态事件 -->
<button v-on:[event]="doThis"></button>
<!-- 内联声明 -->
<button v-on:click="doThat('hello', $event)"></button>
<!-- 缩写 -->
<button @click="doThis"></button>
<!-- 使用缩写的动态事件 -->
<button @[event]="doThis"></button>
<!-- 停止传播 -->
<button @click.stop="doThis"></button>
<!-- 阻止默认事件 -->
<button @click.prevent="doThis"></button>
<!-- 不带表达式地阻止默认事件 -->
<form @submit.prevent></form>
<!-- 链式调用修饰符 -->
<button @click.stop.prevent="doThis"></button>
<!-- 按键用于 keyAlias 修饰符-->
<input @keyup.enter="onEnter" />
<!-- 点击事件将最多触发一次 -->
<button v-on:click.once="doThis"></button>
<!-- 对象语法 -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
监听子组件的自定义事件 (当子组件的“my- event(事件) (事件) ”事件被触发,处理函数将被调用):
代码语言:javascript复制<MyComponent @my-event="handleThis" />
<!-- 内联声明 -->
<MyComponent @my-event="handleThis(123, $event)" />
动态的绑定一个或多个 attribute(归于) (归于) ,也可以是组件的 prop。
- 缩写:
:
或者.
(当使用.prop
修饰符) - 期望:
any (带参数) | Object (不带参数)
- 参数:
attrOrProp (可选的)
- 修饰符:
.camel
- 将短横线命名的 attribute(归于) (归于) 转变为驼峰式命名。.prop
- 强制绑定为 DOM property(属性) (属性) 。3.2.attr
- 强制绑定为 DOM attribute(归于) (归于) 。3.2
示例:
代码语言:javascript复制<!-- 绑定 attribute -->
<img v-bind:src="imageSrc" />
<!-- 动态 attribute 名 -->
<button v-bind:[key]="value"></button>
<!-- 缩写 -->
<img :src="imageSrc" />
<!-- 缩写形式的动态 attribute 名 -->
<button :[key]="value"></button>
<!-- 内联字符串拼接 -->
<img :src="'/path/to/images/' fileName" />
<!-- class 绑定 -->
<div :class="{ red: isRed }"></div>
<div :class="[classA, classB]"></div>
<div :class="[classA, { classB: isB, classC: isC }]"></div>
<!-- style 绑定 -->
<div :style="{ fontSize: size 'px' }"></div>
<div :style="[styleObjectA, styleObjectB]"></div>
<!-- 绑定对象形式的 attribute -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
<!-- prop 绑定。“prop” 必须在子组件中已声明。 -->
<MyComponent :prop="someThing" />
<!-- 传递子父组件共有的 prop -->
<MyComponent v-bind="$props" />
<!-- XLink -->
<svg><a :xlink:special="foo"></a></svg>
当在 DOM 内模板使用 .camel
修饰符,可以驼峰化 v-bind
attribute(归于) (归于) 的名称,例如 SVG viewBox
attribute(归于) (归于) :
<svg :view-box.camel="viewBox"></svg>
如果使用字符串模板或使用构建步骤预编译模板,则不需要 .camel
。
在表单输入元素或组件上创建双向绑定。
- 期望的绑定值类型:根据表单输入元素或组件输出的值而变化
- 仅限:
<input>
<select>
<textarea>
- components(组件) (组件)
- 修饰符:
- [
.lazy
] - 监听change
事件而不是input
- [
.number
] - 将输入的合法字符串转为数字 - [
.trim
] - 移除输入内容两端空格
- [
用于声明具名插槽或是期望接收 props 的作用域插槽。
示例:
代码语言:javascript复制<!-- 具名插槽 -->
<BaseLayout>
<template v-slot:header>
Header content
</template>
<template v-slot:default>
Default slot content
</template>
<template v-slot:footer>
Footer content
</template>
</BaseLayout>
<!-- 接收 prop 的具名插槽 -->
<InfiniteScroll>
<template v-slot:item="slotProps">
<div class="item">
{{ slotProps.item.text }}
</div>
</template>
</InfiniteScroll>
<!-- 接收 prop 的默认插槽,并解构 -->
<Mouse v-slot="{ x, y }">
Mouse position: {{ x }}, {{ y }}
</Mouse>
跳过该元素及其所有子元素的编译。
- 无需传入
- 详细信息
元素内具有
v-pre
,所有 Vue 模板语法都会被保留并按原样渲染。最常见的用例就是显示原始双大括号标签及内容。
在随后的重新渲染,元素/组件及其所有子项将被当作静态内容并跳过渲染。这可以用来优化更新时的性能。
仅渲染元素和组件一次,并跳过之后的更新。
代码语言:javascript复制<!-- 单个元素 -->
<span v-once>This will never change: {{msg}}</span>
<!-- 带有子元素的元素 -->
<div v-once>
<h1>comment</h1>
<p>{{msg}}</p>
</div>
<!-- 组件 -->
<MyComponent v-once :comment="msg" />
<!-- `v-for` 指令 -->
<ul>
<li v-for="i in list" v-once>{{i}}</li>
</ul>
缓存一个模板的子树。在元素和组件上都可以使用。为了实现缓存,该指令需要传入一个固定长度的依赖值数组进行比较。如果数组里的每个值都与最后一次的渲染相同,那么整个子树的更新将被跳过。
代码语言:javascript复制<div v-memo="[valueA, valueB]">
...
</div>
当组件重新渲染,如果 valueA
和 valueB
都保持不变,这个 <div>
及其子项的所有更新都将被跳过。实际上,甚至虚拟 DOM 的 vnode 创建也将被跳过,因为缓存的子树副本可以被重新使用。
正确指定缓存数组很重要,否则应该生效的更新可能被跳过。v-memo(备忘录)传入空依赖数组 (v-memo="[]") 将与 v-once 效果相同。
与 v-for
一起使用
v-memo
仅用于性能至上场景中的微小优化,应该很少需要。最常见的情况可能是有助于渲染海量 v-for
列表 (长度超过 1000 的情况):
<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
<p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p>
<p>...more child nodes</p>
</div>
当组件的 selected
状态改变,默认会重新创建大量的 vnode,尽管绝大部分都跟之前是一模一样的。v-memo
用在这里本质上是在说“只有当该项的被选中状态改变时才需要更新”。这使得每个选中状态没有变的项能完全重用之前的 vnode 并跳过差异比较。注意这里 memo(备忘录) (备忘录) 依赖数组中并不需要包含 item.id
,因为 Vue 也会根据 item(项目) (项目) 的 :key
进行判断。
当搭配 v-for
使用 v-memo
,确保两者都绑定在同一个元素上。**v-memo
不能用在 v-for
内部。**
用于隐藏尚未完成编译的 DOM 模板。
- 无需传入
- 详细信息 该指令只在没有构建步骤的环境下需要使用。
当使用直接在 DOM 中书写的模板时,可能会出现一种叫做“未编译模板闪现”的情况:用户可能先看到的是还没编译完成的双大括号标签,直到挂载的组件将它们替换为实际渲染的内容。
v-cloak
会保留在所绑定的元素上,直到相关组件实例被挂载后才移除。配合像 [v-cloak] { display: none }
这样的 CSS 规则,它可以在组件编译完毕前隐藏原始模板。
示例:
代码语言:javascript复制[v-cloak] {
display: none;
}
代码语言:javascript复制<div v-cloak>
{{ message }}
</div>
组件注册和使用
内置组件无需注册便可以直接在模板中使用。它们也支持 tree- shake(动摇) (动摇) :仅在使用时才会包含在构建中。
在[渲染函数]中使用它们时,需要显式导入。例如:
代码语言:javascript复制import { h, Transition } from 'vue'
h(Transition, {
/* props */
})
不是组件
<component>
、<slot>
和 <template>
具有类似组件的特性,也是模板语法的一部分。但它们并非真正的组件,同时在模板编译期间会被编译掉。因此,它们通常在模板中用小写字母书写。
按注册名渲染组件 (选项式 API):
代码语言:javascript复制<script>
import Foo from './Foo.vue'
import Bar from './Bar.vue'
export default {
components: { Foo, Bar },
data() {
return {
view: 'Foo'
}
}
}
</script>
<template>
<component :is="view" />
</template>
按定义渲染组件 (<script setup>
组合式 API):
<script setup>
import Foo from './Foo.vue'
import Bar from './Bar.vue'
</script>
<template>
<component :is="Math.random() > 0.5 ? Foo : Bar" />
</template>
[内置组件]都可以传递给 is
,但是如果想通过名称传递则必须先对其进行注册。举例来说:
<script>
import { Transition, TransitionGroup } from 'vue'
export default {
components: {
Transition,
TransitionGroup
}
}
</script>
<template>
<component :is="isGroup ? 'TransitionGroup' : 'Transition'">
...
</component>
</template>
如果在 <component>
标签上使用 v-model
,模板编译器会将其扩展为 modelValue
prop 和 update:modelValue
事件监听器,就像对任何其他组件一样。但是,这与原生 HTML 元素不兼容,例如 <input>
或 <select>
。因此,在动态创建的原生元素上使用 v-model
将不起作用:
<script setup>
import { ref } from 'vue'
const tag = ref('input')
const username = ref('')
</script>
<template>
<!-- 由于 'input' 是原生 HTML 元素,因此这个 v-model 不起作用 -->
<component :is="tag" v-model="username" />
</template>
当我们想要使用内置指令而不在 DOM 中渲染元素时,<template>
标签可以作为占位符使用。
单文件组件使用[顶层的 <template>
标签]来包裹整个模板。这种用法与上面描述的 <template>
使用方式是有区别的。该顶层标签不是模板本身的一部分,不支持指令等模板语法。
举例来说:
代码语言:javascript复制<transition>
<span :key="text">{{ text }}</span>
</transition>
使用选项式 API,引用将被注册在组件的 this.$refs
对象里:
<!-- 存储为 this.$refs.p -->
<p ref="p">hello</p>
使用组合式 API,引用将存储在与名字匹配的 ref 里:
代码语言:javascript复制<script setup>
import { ref } from 'vue'
const p = ref()
</script>
<template>
<p ref="p">hello</p>
</template>
关于 ref 注册时机的重要说明:因为 ref 本身是作为渲染函数的结果来创建的,必须等待组件挂载后才能对它进行访问。
this.$refs
也是非响应式的,因此你不应该尝试在模板中使用它来进行数据绑定。
在 is
attribute(归于) (归于) 的值中加上 vue:
前缀,这样 Vue 就会把该元素渲染为 Vue 组件:
<table>
<tr is="vue:my-row-component"></tr>
</table>
<script setup>
是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。当同时使用 SFC 与组合式 API 时该语法是默认推荐。相比于普通的 <script>
语法,它具有更多优势:
- 更少的样板内容,更简洁的代码。
- 能够使用纯 TypeScript 声明 props 和自定义事件。
- 更好的运行时性能 (其模板会被编译成同一作用域内的渲染函数,避免了渲染上下文代理对象)。
- 更好的 IDE 类型推导性能 (减少了语言服务器从代码中抽取类型的工作)。
<script setup>
中的代码会在每次组件实例被创建的时候执行。
当使用 <script setup>
的时候,任何在 <script setup>
声明的顶层的绑定 (包括变量,函数声明,以及 import(进口) (进口) 导入的内容) 都能在模板中直接使用:
<script setup>
// 变量
const msg = 'Hello!'
// 函数
function log() {
console.log(msg)
}
</script>
<template>
<button @click="log">{{ msg }}</button>
</template>
import(进口) (进口) 导入的内容也会以同样的方式暴露。这意味着我们可以在模板表达式中直接使用导入的 helper(帮手) (帮手) 函数,而不需要通过 methods
选项来暴露它:
<script setup>
import { capitalize } from './helpers'
</script>
<template>
<div>{{ capitalize('hello') }}</div>
</template>
响应式状态需要明确使用[响应式 API]来创建。和 setup()
函数的返回值一样,ref 在模板中使用的时候会自动解包:
vue
代码语言:javascript复制<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count ">{{ count }}</button>
</template>
<script setup>
范围里的值也能被直接作为自定义组件的标签名使用:
<script setup>
import MyComponent from './MyComponent.vue'
</script>
<template>
<MyComponent />
</template>
由于组件是通过变量引用而不是基于字符串组件名注册的,在 <script setup>
中要使用动态组件的时候,应该使用动态的 :is
来绑定:
<script setup>
import Foo from './Foo.vue'
import Bar from './Bar.vue'
</script>
<template>
<component :is="Foo" />
<component :is="someCondition ? Foo : Bar" />
</template>
一个单文件组件可以通过它的文件名被其自己所引用。例如:名为 FooBar.vue
的组件可以在其模板中用 <FooBar/>
引用它自己。
请注意这种方式相比于导入的组件优先级更低。如果有具名的导入和组件自身推导的名字冲突了,可以为导入的组件添加别名:
代码语言:javascript复制import { FooBar as FooBarChild } from './components'
为了在声明 props
和 emits
选项时获得完整的类型推导支持,我们可以使用 defineProps
和 defineEmits
API,它们将自动地在 <script setup>
中可用:
<script setup>
const props = defineProps({
foo: String
})
const emit = defineEmits(['change', 'delete'])
// setup 代码
</script>
defineProps
和defineEmits
都是只能在<script setup>
中使用的编译器宏。他们不需要导入,且会随着<script setup>
的处理过程一同被编译掉。defineProps
接收与props
选项相同的值,defineEmits
接收与emits
选项相同的值。defineProps
和defineEmits
在选项传入后,会提供恰当的类型推导。- 传入到
defineProps
和defineEmits
的选项会从 setup 中提升到模块的作用域。因此,传入的选项不能引用在 setup 作用域中声明的局部变量。这样做会引起编译错误。但是,它可以引用导入的绑定,因为它们也在模块作用域内。
可以通过 defineExpose
编译器宏来显式指定在 <script setup>
组件中要暴露出去的属性:
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
defineExpose({
a,
b
})
</script>
在 <script setup> 使用 slots 和 attrs 的情况应该是相对来说较为罕见的,因为可以在模板中直接通过
代码语言:javascript复制<script setup>
import { useSlots, useAttrs } from 'vue'
const slots = useSlots()
const attrs = useAttrs()
</script>
useSlots
和 useAttrs
是真实的运行时函数,它的返回与 setupContext.slots
和 setupContext.attrs
等价。它们同样也能在普通的组合式 API 中使用。
<script setup>
中可以使用顶层 await
。结果代码会被编译成 async setup()
:
<script setup>
const post = await fetch(`/api/post/1`).then((r) => r.json())
</script>
创建原生元素:
代码语言:javascript复制import { h } from 'vue'
// 除了 type 外,其他参数都是可选的
h('div')
h('div', { id: 'foo' })
// attribute 和 property 都可以用于 prop
// Vue 会自动选择正确的方式来分配它
h('div', { class: 'bar', innerHTML: 'hello' })
// class 与 style 可以像在模板中一样
// 用数组或对象的形式书写
h('div', { class: [foo, { bar }], style: { color: 'red' } })
// 事件监听器应以 onXxx 的形式书写
h('div', { onClick: () => {} })
// children 可以是一个字符串
h('div', { id: 'foo' }, 'hello')
// 没有 prop 时可以省略不写
h('div', 'hello')
h('div', [h('span', 'hello')])
// children 数组可以同时包含 vnode 和字符串
h('div', ['hello', h('span', 'hello')])
创建组件:
代码语言:javascript复制import Foo from './Foo.vue'
// 传递 prop
h(Foo, {
// 等价于 some-prop="hello"
someProp: 'hello',
// 等价于 @update="() => {}"
onUpdate: () => {}
})
// 传递单个默认插槽
h(Foo, () => 'default slot')
// 传递具名插槽
// 注意,需要使用 `null` 来避免
// 插槽对象被当作是 prop
h(MyComponent, null, {
default: () => 'default slot',
foo: () => h('div', 'foo'),
bar: () => [h('span', 'one'), h('span', 'two')]
})
如果你不需要合并行为而是简单覆盖,可以使用原生 object(目的) (目的) spread 语法来代替。
示例
代码语言:javascript复制import { h, cloneVNode } from 'vue'
const original = h('div')
const cloned = cloneVNode(original, { id: 'foo' })
示例
代码语言:javascript复制import { h, withModifiers } from 'vue'
const vnode = h('button', {
// 等价于 v-on:click.stop.prevent
onClick: withModifiers(() => {
// ...
}, ['stop', 'prevent'])
})
示例
代码语言:javascript复制import type { PropType } from 'vue'
interface Book {
title: string
author: string
year: number
}
export default {
props: {
book: {
// 提供一个比 `Object` 更具体的类型
type: Object as PropType<Book>,
required: true
}
}
}
示例
代码语言:javascript复制import axios from 'axios'
declare module 'vue' {
interface ComponentCustomProperties {
$http: typeof axios
$translate: (key: string) => string
}
}
用来扩展组件选项类型以支持自定义选项。
示例
代码语言:javascript复制import { Route } from 'vue-router'
declare module 'vue' {
interface ComponentCustomOptions {
beforeRouteEnter?(to: any, from: any, next: () => void): void
}
}
用于扩展全局可用的 TSX props,以便在 TSX 元素上使用没有在组件选项上定义过的 props。
示例
代码语言:javascript复制declare module 'vue' {
interface ComponentCustomProps {
hello?: string
}
}
export {}
代码语言:javascript复制// 现在即使没有在组件选项上定义过 hello 这个 prop 也依然能通过类型检查了
<MyComponent hello="world" />
用于扩展在样式属性绑定上允许的值的类型。
- 示例
允许任意自定义 CSS 属性:
代码语言:javascript复制declare module 'vue' {
interface CSSProperties {
[key: `--${string}`]: string
}
}
代码语言:javascript复制<div style={ { '--bg-color': 'blue' } }>
代码语言:javascript复制<div :style="{ '--bg-color': 'blue' }">
仓库地址:https://github.com/webVueBlog/WebGuideInterview