内置特殊 Attributes
key
描述: 在 Vue 3 中,key
的作用主要与重新渲染和优化有关。
作用: 用于标识列表中的元素,以便在列表更新时,Vue 可以识别哪些元素发生了变化,而不是简单地替换所有元素。
期望接收: number
| string
| symbol
。key
绑定的值应为基础类型,如字符串或数字。不要使用对象作为 v-for
的 key。
例如,如果有一个动态列表,希望在列表更新时保留某些元素的身份,可以使用 key
。
import { reactive } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js'
let template = `
<button v-on:click='updateItems'>反转</button>
<ul>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
`
export default {
setup: function () {
let items = reactive([
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
{ id: 3, name: 'Cherry' },
])
let updateItems = () => items.reverse()
return { items, updateItems }
},
template,
}
在这个例子中,使用 key
可以确保列表的渲染更加高效,因为它可以区分哪些元素发生了变化,而不是每次重新创建所有元素。
同一父元素下的子元素必须具有唯一的 key。重复的 key 将导致渲染异常。
key
通常与 v-for
配合使用。建议在任何可行的情况下为 v-for
提供一个 key
属性,除非列表渲染的结果不依赖于子组件状态或临时 DOM 状态,或者你想有意采用默认行为来提高性能(默认模式是高效的,但只适用于列表渲染输出的结果不依赖子组件状态或者临时 DOM 状态 (例如表单输入值) 的情况。)
如果传递了 key
,则根据 key
的变化顺序重新排列元素,并且将移除/销毁已不存在的元素。
<transition>
<span :key="text">{{ text }}</span>
</transition>
当 text
变化时,<span>
将总是被替换而不是更新,触发 transition。
ref
描述: ref
允许在模板中为元素或子组件注册引用信息。
作用: ref
用于注册元素或子组件的引用。
期望接收: string
| Function
使用组合式 API,引用存储在与名称匹配的 ref
中。对于普通 DOM 元素,引用是元素本身;对于子组件,引用是子组件的实例。
let template = `
<div ref='div1'>通过ref获取到这个元素</div>
`
export default {
setup: function () {
let div1 = ref()
onMounted(() => {
console.log(div1.value) // <div>通过ref获取到这个元素</div>
})
return { div1 }
},
template
}
在模板中,不需要使用 v-bind:
来绑定 ref
,因为 ref
自动创建了一个响应式引用。直接使用 ref
返回的响应式引用即可。
因为 ref
是作为渲染函数的结果创建的,必须等待组件挂载后才能访问它。
is
is
属性用于动态决定应该渲染哪个组件。这允许在运行时决定渲染哪个组件,而不是在模板中静态声明。在某些场景下非常有用,例如条件渲染多个组件版本或动态切换组件类型。
期望接收: string
| Component
结合 <component>
(这个东西下面说)举例,有一个 C1 组件,C2 类似:
let template = `
<div>C1</div>
`
export default {
template
}
有个父组件:
import { ref } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js'
import C1 from './C1.js'
import C2 from './C2.js'
let template = `
<component :is="currentComponent"></component>
<button @click="changeCurrentComponent">Change Component</button>
`
export default {
setup: function () {
let currentComponent = ref('C1')
let changeCurrentComponent = () => {
currentComponent.value = currentComponent.value === 'C1' ? 'C2' : 'C1'
}
return { currentComponent, changeCurrentComponent }
},
components: {
C1,
C2,
},
template
}
在这个例子中,currentComponent
的值决定了渲染哪个组件。可以根据应用的状态动态地改变这个值,这里通过按钮切换两个组件。
如果is
这个属性用在原生的DOM元素上面,is
的值必须加上前缀 vue:
才可以被解析为一个 Vue 组件。为什么要用在原生DOM元素上?因为某些元素仅在放置于特定元素中时才会显示,例如<option>要在<select>才会显示,<select>中也只有<option>才能显示。
子组件:
<template>
<option>1</option>
<option>2</option>
<option>3</option>
</template>
<script setup></script>
<style lang="scss" scoped></style>
父组件:
<script setup>
import MyOption from './components/MyOption.vue'
</script>
<template>
<select>
<div>我会被忽略</div>
<MyOption/>
<option is="vue:MyOption"></option>
</select>
</template>
<style scoped></style>
这时,第一个div会被忽略;因为<select>中只能识别<option>,所以如果你第二个<MyOption>直接放置在<select>中出问题了,那你要使用第三种方式,即使用一个<option>伪装一下
内置特殊元素
<component>
在 Vue 3 中,<component>
标签是用于动态地渲染一个组件。它允许你在运行时根据条件或属性来决定渲染哪个组件。
<component>
标签的语法如下:
<component :is="dynamicComponent"></component>
要渲染的实际组件由 is
prop 决定。当 is
是字符串,它既可以是 HTML 标签名也可以是组件的注册名。或者,is
也可以直接绑定到组件的定义。dynamicComponent
是一个变量或表达式,它的值应该是一个组件的名称、组件对象或组件实例。
例如,假设你有一个名为 MyComponent
的组件,你可以通过以下方式动态地渲染它:
<component is="MyComponent"></component>
注意:如果MyComponent不是一个变量,而确确实实是一个组件,那么is前面就不需要加v-bind:
也可以渲染一个HTML 标签:
<component :is="href ? 'a' : 'span'"></component>
渲染异步组件
在 Vue 3 中,你可以使用 defineAsyncComponent
方法来定义异步组件,以便在组件加载时进行动态渲染。defineAsyncComponent
方法返回一个 Promise,该 Promise 解析为组件实例。
以下是一个使用 defineAsyncComponent
方法定义异步组件的示例:
假设你有你个C1组件:
import { defineAsyncComponent } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js'
let template = `
<component :is='C1'></component>
`
export default{
setup(){
let C1 = defineAsyncComponent(()=> import('./components/C1.js'))
return {C1}
},
template,
}
在模板中,你可以这样来动态渲染异步组件:
<component :is="C1"></component>
当 C1
的值发生变化时,Vue 会自动重新渲染相应的组件。
<slot>
这里只介绍<slot>标签,关于插槽的具体内容,请看我的另一篇文章
表示模板中的插槽内容出口
。元素本身将被其所匹配的插槽内容替换。
注意点
<slot>
元素可以使用name
attribute 来指定插槽名:你可以为<slot>
元素指定一个名称,以便在父组件中通过这个名称引用它。这样,父组件就可以知道应该将内容插入到哪个插槽中。- 当没有指定
name
时,将会渲染默认插槽:如果子组件中的<slot>
元素没有指定name
属性,那么父组件中的内容将会被渲染到默认的插槽中。 - 传递给插槽元素的附加 attributes 将作为插槽 props,传递给父级中定义的作用域插槽:如果你在
<slot>
元素上添加了一些属性(即附加的 attributes),这些属性将会作为 prop 传递给父组件中定义的作用域插槽。换句话说,这些属性可以在父组件的插槽内容中使用。
举例,我们有一个C3子组件
let template = `
<slot name="header" class='custom-class'></slot>
`
export default {
template
}
还有一个C2父组件
import C3 from './C3.js'
let template = `
<C3>
<template v-slot:header> <!-- 使用 v-slot 指令将内容插入到 header 插槽中 -->
<h1 class='custom-class'>这是标题内容</h1> <!-- 这里我们可以使用子组件传递下来的 class attribute -->
</template>
</C3>
`
export default {
components: {
C3,
},
template
}
在上面的例子中,子组件的 <slot>
元素有一个 name
属性和一个 class
属性。在父组件中,我们通过 v-slot:header
将内容插入到名为 “header” 的插槽中。同时,由于子组件传递下来的 class
属性,我们可以在父组件的插槽内容中使用它(通过审查元素可以看到这个attribute)。
<template>
1.在Vue.js中,<template>
标签可以被用来表示一个模板,但它不会在DOM中渲染成一个真实的元素。它的主要用途是作为占位符使用。
2.当<template>
标签与特定的Vue指令一起使用时,如v-if
、v-else-if
或v-else
、v-for
、v-slot
,Vue会对其特殊处理。如果没有这些指令,<template>
会被渲染成原生的HTML的<template>
元素。
例如,我有多个元素需要通过v-if来显示和隐藏,我可以在这几个元素外面用一个div包着,然后给这个div用上v-if指令,但我为什么不这样做呢?这是因为div会令页面多了一个元素,而<template>就不会在DOM中渲染成一个真实的元素。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
如果<template>
标签带有v-for
指令,它还可以有一个key
属性。对于其他的属性和指令,由于没有对应的元素,它们会被丢弃。
3.在单文件组件中,顶层的<template>
标签用来包裹整个模板。这种用法与上述描述的<template>
用法是不同的。顶层的<template>
标签并不是模板本身的一部分,它不支持像指令这样的模板语法。