1. 将一个 prop 限度在一个类型的列表中
应用 prop 定义中的 validator 选项,能够将一个 prop 类型限度在一组特定的值中。
export default {
name: 'Image',
props: {
src: {type: String,},
style: {
type: String,
validator: s => ['square', 'rounded'].includes(s)
}
}
};
这个验证函数承受一个 prop,如果 prop 无效或有效,则返回 true 或 false。
当单单传入的 true 或 false 来管制某些条件不能满足需要时,我通常应用这个办法来做。
按钮类型或正告类型 (信息、胜利、危险、正告) 是最常见的用法、、。色彩也是一个很好的用处。
2. 默认内容和扩大点
Vue 中的槽能够有默认的内容,这使咱们能够制作出更容易应用的组件。
<button class="button" @click="$emit('click')">
<slot>
<!-- Used if no slot is provided -->
Click me
</slot>
</button
我最喜爱应用默认槽,就是用它们来创立扩大点。
咱们能够取组件的任何局部,将其封装在一个插槽中,在里面咱们能够用想要的任何内容笼罩组件的该局部。默认状况下,它依然会依照原来的形式工作,但这样做会有了更多的选项
<template>
<button class="button" @click="$emit('click')">
<slot>
<div class="formatting">
{{text}}
</div>
</slot>
</button>
</template>
当初咱们能够用许多不同的形式应用这个组件。简略的、默认的形式,或者自定义的形式。
<!-- Uses default functionality of the component -->
<ButtonWithExtensionPoint text="Formatted text" />
<ButtonWithExtensionPoint>
<div class="different-formatting">
Do something a little different here
</div>
</ButtonWithExtensionPoint>
3. 应用引号来监听嵌套属性
你可能不晓得这一点,咱们能够通过应用引号轻松地间接监听嵌套值:
watch {'$route.query.id'() {// ...}
}
4. 晓得何时应用 v -if(以及何时防止应用)
与其应用 v -if,有时应用 v -show 来代替,会有更高的性能。
<ComplicatedChart v-show="chartEnabled" />
当 v -if 被关上或敞开时,它将创立并齐全销毁该元素。相同,v-show 将创立该元素并将其留在那里,通过设置其款式为 display: none 来暗藏它。
如果你要切换的组件的渲染老本很高,那么这样做会更有效率。
反过来说,如果不须要立刻执行低廉的组件件,能够应用 v -if,这样它就会跳过渲染它,而使页面的加载速度更快一些。
5. 单个作用域插槽的简写(不须要 template 标签)
限定范畴的插槽十分乏味,但为了应用它们,您还必须应用许多模板标记。
侥幸的是,有一个简写能够让咱们解脱它,但只有在咱们应用单个作用域槽的状况下。
一般写法:
<DataTable>
<template #header="tableAttributes">
<TableHeader v-bind="tableAttributes" />
</template>
</DataTable>
不应用 template:
<DataTable #header="tableAttributes">
<TableHeader v-bind="tableAttributes" />
</DataTable>
简略、含糊其辞,令人赞叹不已。
6. 有条件地渲染插槽
咱们先来看如何做,而后在探讨为什么想暗藏插槽。
每个 Vue 组件都有一个非凡的 $slots 对象,外面有你所有的插槽。默认槽的键是 default,任何被命名的槽都应用其名称作为键。
const $slots = {
default: <default slot>,
icon: <icon slot>,
button: <button slot>,
};
但这个 $slots 对象只有实用于该组件的插槽,而不是每一个定义的插槽。
拿这个定义了几个插槽的组件来说,包含几个命名的插槽。
<!-- Slots.vue -->
<template>
<div>
<h2>Here are some slots</h2>
<slot />
<slot name="second" />
<slot name="third" />
</div>
</template>
如果咱们只对组件利用一个插槽,那么只有那个插槽会显示在咱们的 $slots 对象中。
<template>
<Slots>
<template #second>
This will be applied to the second slot.
</template>
</Slots>
</template>
$slots = {second: <vnode>}
咱们能够在咱们的组件中应用这一点来检测哪些插槽曾经被利用到组件中,例如,通过暗藏插槽的包装元素。
<template>
<div>
<h2>A wrapped slot</h2>
<div v-if="$slots.default" class="styles">
<slot />
</div>
</div>
</template>
当初,利用款式的包装器 div 只有在咱们用某些货色填充这个插槽时才会被渲染。
如果不应用 v -if,那么如果没有插槽,就会失去一个空的不必要的 div。依据 div 的款式,这可能会打乱咱们的布局,让界面看起来很奇怪。
那么,为什么咱们心愿可能有条件地渲染插槽呢?
应用条件插槽的次要起因有三个:
- 当应用封装的 div 来增加默认款式时
- 插槽是空的
- 如果咱们将默认内容与嵌套槽相结合
例如,当咱们在增加默认款式时,咱们在插槽四周增加一个 div:
<template>
<div>
<h2>This is a pretty great component, amirite?</h2>
<div class="default-styling">
<slot >
</div>
<button @click="$emit('click')">Click me!</button>
</div>
</template>
然而,如果父组件没有将内容利用到该插槽中,咱们最终会在页面上渲染出一个空的 div。
<div>
<h2>This is a pretty great component, amirite?</h2>
<div class="default-styling">
<!-- 槽中没有内容,但这个 div 依然被渲染。蹩脚 -->
</div>
<button @click="$emit('click')">Click me!</button>
</div>
解决办法就是像下面讲的一样,多个条件判断,就行啦。
7. 如何监听一个插槽的变动
有时咱们须要晓得插槽内的内容何时产生了变动。
<!-- 惋惜这个事件不存在 -->
<slot @change="update" />
可怜的是,Vue 没有内置的办法让咱们检测这一点。
MutationObserver 接口提供了监督对 DOM 树所做更改的能力。它被设计为旧的 Mutation Events 性能的替代品,该性能是 DOM3 Events 标准的一部分。
export default {mounted() {
// 当有变动时调用 `update`
const observer = new MutationObserver(this.update);
// 监听此组件的变动
observer.observe(this.$el, {
childList: true,
subtree: true
});
}
};
8. 将部分和全局的 style 混合在一起
通常状况下,在解决款式时,咱们心愿它们能被划分到一个独自的组件中。
<style scoped>
.component {background: green;}
</style>
不过,如果需要的话,也能够增加一个非作用域款式块来增加全局款式
<style>
/* 全局 */
.component p {margin-bottom: 16px;}
</style>
<style scoped>
/* 在该组件内无效 */
.component {background: green;}
</style>
但要小心,全局款式是危险的,难以追踪。但有时,它们是一个完满的逃生舱口,正是你所须要的
9. 重写子组件的款式 – 正确的办法
Scoped CSS 在放弃内容整洁方面十分棒,而且不会将款式引入利用的其余组件中。
但有时你须要笼罩一个子组件的款式,并跳出这个作用域。
Vue 有一个 deep 选择器:
<style scoped>
.my-component >>> .child-component {font-size: 24px;}
</style>
留神:如果你应用像 SCSS 这样的 CSS 预处理器,你可能须要应用 /deep/ 来代替。
10. 用上下文感知组件发明魔法
上下文感知组件 (context-aware) 是“魔法的”,它们主动适应四周产生的事件,解决边缘状况、状态共享等等。
有 3 种次要的 context-aware,但 Configuration 是我最感兴趣的一种。
1. 状态共享
当你把一个大的组件分解成多个小的组件时,它们往往依然须要共享状态。
咱们能够在 “ 幕后 “ 做这些工作,而不是把这些工作推给使用者。
咱们个别会把 Dropdown 组件分解成 Select 和 Option 组件,这样会取得更多的灵活性。然而为了方便使用,Select 和 Option 组件彼此共享 selected 状态。
<!-- 为简略起见,作为一个繁多组件应用 -->
<Dropdown v-model="selected" :options="[]" />
<!-- 分多个组件,更灵便 -->
<Select v-model="selected">
<Option value="mustard">Mustard</Option>
<Option value="ketchup">Ketchup</Option>
<div class="relish-wrapper">
<Option value="relish">Relish</Option>
</div>
</Select>
2. Configuration
有时,一个组件的行为须要依据应用程序的其余局部的状况来扭转。这通常是为了主动解决边缘状况,否则解决起来会很烦人。
一个 Popup 或 Tooltip 应该从新定位,以便它不会溢出页面。然而,如果该组件是在一个 modal 内,它应该从新定位,以便它不会溢出 modal。
如果 Tooltip 晓得它是在一个模态外面,这能够主动实现。
3. 款式
创立了 context-aware 的 CSS,依据父级或同级元素的状况利用不同的款式。
.statistic {
color: black;
font-size: 24px;
font-weight: bold;
}
.statistic + .statistic {margin-left: 10px;}
CSS 变量让咱们更进一步,容许咱们在一个页面的不同局部设置不同的值。
11. 如何在 Vue 之外创立一个具备响应性的变量(Vue2 和 3)
如果你从 Vue 之外失去一个变量,让它具备反馈性是很好的。
这样,咱们就能够在 computed props、watch 和其余任何中央应用它,它的工作形式就像 Vue 中的任何其余状态一样。
如果咱们应用的选项 API,须要的只是将其放在组件的数据局部中:
const externalVariable = getValue();
export default {data() {
return {reactiveVariable: externalVariable,};
}
};
如果应用 Vue3 的组合 API,能够间接应用 ref 或 reactive。
import {ref} from 'vue';
// 能够齐全在 Vue 组件之外实现
const externalVariable = getValue();
const reactiveVariable = ref(externalVariable);
console.log(reactiveVariable.value);
应用 reactive 代替:
import {reactive} from 'vue';
// 能够齐全在 Vue 组件之外实现
const externalVariable = getValue();
// reactive 只对对象和数组起作用
const anotherReactiveVariable = reactive(externalVariable);
// Access directly
console.log(anotherReactiveVariable);
如果你还在应用 Vue2,你能够应用 observable 而不是 reactive 来实现完全相同的后果。
12. v-for 中的解构
你晓得能够在 -vfor 中应用解构吗?
<li
v-for="{name, id} in users"
:key="id"
>
{{name}}
</li>
更广为人知的是,能够通过应用这样的元组从 v -for 中取出索引。
<li v-for="(movie, index) in ['Lion King','Frozen','The Princess Bride']">
{{index + 1}} - {{movie}}
</li>
当应用一个对象时,能够这样应用 key:
<li v-for="(value, key) in {
name: 'Lion King',
released: 2019,
director: 'Jon Favreau',
}">
{{key}}: {{value}}
</li>
也能够将这两种办法联合起来,获取 key 以及属性的 index。
<li v-for="(value, key, index) in {
name: 'Lion King',
released: 2019,
director: 'Jon Favreau',
}">
#{{index + 1}}. {{key}}: {{value}}
</li>
13. 在指定范畴内循环
v-for 指令容许咱们遍历数组,但它也容许咱们遍历一个范畴
<template>
<ul>
<li v-for="n in 5">Item #{{n}}</li>
</ul>
</template>
渲染后果:
Item #1
Item #2
Item #3
Item #4
Item #5
当咱们应用带范畴的 v -for 时,它将从 1 开始,以咱们指定的数字完结。
14. 监听你的组件中的任何货色
export default {
computed: {someComputedProperty() {// Update the computed prop},
},
watch: {someComputedProperty() {// Do something when the computed prop is updated}
}
};
咱们能够监听:
- 计算属性
- props
-
嵌套值
如果你应用组合 API,任何值都能够被监督,只有它是一个 ref 或 reactive 对象。15. 窃取 prop 类型
我从一个子组件中复制 prop 类型,只是为了在一个父组件中应用它们。但我发现,偷取这些 prop 类型要比仅仅复制它们好得多。
例如,咱们在这个组件中应用了一个 Icon 组件。
<template>
<div>
<h2>{{heading}}</h2>
<Icon
:type="iconType"
:size="iconSize"
:colour="iconColour"
/>
</div>
</template>
为了让它工作,咱们须要增加正确的 prop 类型,从 `Icon
组件复制。
import Icon from './Icon';
export default {components: { Icon},
props: {
iconType: {
type: String,
required: true,
},
iconSize: {
type: String,
default: 'medium',
validator: size => [
'small',
'medium',
'large',
'x-large'
].includes(size),
},
iconColour: {
type: String,
default: 'black',
},
heading: {
type: String,
required: true,
},
},
};
当 Icon 组件的 prop 类型被更新时,咱们必定会遗记返回这个组件并更新它们。随着工夫的推移,当该组件的 prop 类型开始偏离 Icon 组件中的 prop 类型时,就会引入谬误。
因而,这就是为什么咱们要窃取组件的 prop 类型:
import Icon from './Icon';
export default {components: { Icon},
props: {
...Icon.props,
heading: {
type: String,
required: true,
},
},
};
不须要再简单了。
除了在咱们的例子中,咱们把 icon 加在每个 prop 名称的结尾。所以咱们必须做一些额定的工作来实现这一点。
import Icon from './Icon';
const iconProps = {};
Object.entries(Icon.props).forEach((key, val) => {iconProps[`icon${key.toUpperCase()}`] = val;
});
export default {components: { Icon},
props: {
...iconProps,
heading: {
type: String,
required: true,
},
},
};
当初,如果 Icon 组件中的 prop 类型被批改,咱们的组件将放弃最新状态。
然而,如果一个 prop 类型从 Icon 组件中被增加或删除了呢?为了应答这些状况,咱们能够应用 v -bind 和一个计算的 prop 来放弃动静。