25 个 Vue 技巧,开发了 5 年了,才知道还能这么用
作者 | 前端小智
1. 将一个 prop 限制在一个类型的列表中
export default { name: 'Image', props: { src: { type: String, }, style: { type: String, validator: s => ['square', 'rounded'].includes(s) } }};
2. 默认内容和扩展点
<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(以及何时避免使用)
<ComplicatedChart v-show="chartEnabled" />
5. 单个作用域插槽的简写(不需要 template 标签)
<DataTable> <template #header="tableAttributes"> <TableHeader v-bind="tableAttributes" /> </template></DataTable>
<DataTable #header="tableAttributes"> <TableHeader v-bind="tableAttributes" /></DataTable>
6. 有条件地渲染插槽
const $slots = { default: <default slot>, icon: <icon slot>, button: <button slot>,};
<!-- Slots.vue --><template> <div> <h2>Here are some slots</h2> <slot /> <slot name="second" /> <slot name="third" /> </div></template>
<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来添加默认样式时 插槽是空的 如果我们将默认内容与嵌套槽相结合
<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> <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" />
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. 重写子组件的样式--正确的方法
<style scoped>.my-component >>> .child-component { font-size: 24px;}</style>
10. 用上下文感知组件创造魔法
1.状态共享
<!-- 为简单起见,作为一个单一组件使用 -->
<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
3.样式
.statistic {
color: black;
font-size: 24px;
font-weight: bold;
}
.statistic + .statistic {
margin-left: 10px;
}
11. 如何在Vue之外创建一个具有响应性的变量(Vue2和3)
const externalVariable = getValue();
export default {
data() {
return {
reactiveVariable: externalVariable,
};
}
};
import { ref } from 'vue';
// 可以完全在Vue组件之外完成
const externalVariable = getValue();
const reactiveVariable = ref(externalVariable);
console.log(reactiveVariable.value);
import { reactive } from 'vue';
// 可以完全在Vue组件之外完成
const externalVariable = getValue();
// reactive 只对对象和数组起作用
const anotherReactiveVariable = reactive(externalVariable);
// Access directly
console.log(anotherReactiveVariable);
12. v-for 中的解构
<li v-for="{ name, id } in users" :key="id"> {{ name }}</li>
<li v-for="(movie, index) in [ 'Lion King', 'Frozen', 'The Princess Bride']"> {{ index + 1 }} - {{ movie }}</li>
<li v-for="(value, key) in { name: 'Lion King', released: 2019, director: 'Jon Favreau',}"> {{ key }}: {{ value }}</li>
<li v-for="(value, key, index) in { name: 'Lion King', released: 2019, director: 'Jon Favreau',}"> #{{ index + 1 }}. {{ key }}: {{ value }}</li>
13. 在指定范围内循环
<template> <ul> <li v-for="n in 5">Item #{{ n }}</li> </ul></template>
Item #1Item #2Item #3Item #4Item #5
14. 监听你的组件中的任何东西
export default { computed: { someComputedProperty() { // Update the computed prop }, }, watch: { someComputedProperty() { // Do something when the computed prop is updated } }};
计算属性 props 嵌套值
15.窃取 prop 类型
<template> <div> <h2>{{ heading }}</h2> <Icon :type="iconType" :size="iconSize" :colour="iconColour" /> </div></template>
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, }, },};
import Icon from './Icon';export default { components: { Icon }, props: { ...Icon.props, heading: { type: String, required: true, }, },};
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,
},
},
};
16. 检测元素外部(或内部)的单击
window.addEventListener('mousedown', e => { // 获取被点击的元素 const clickedEl = e.target; if (el.contains(clickedEl)) { //在 "el "里面点击了 } else { //在 "el "外点击了 }});
17. 递归插槽
<!-- VFor.vue --><template> <div> <!-- 渲染第一项 --> {{ list[0] }} <!-- 如果我们有更多的项目,继续!但是不要使用我们刚刚渲染的项 --> <v-for v-if="list.length > 1" :list="list.slice(1)" /> </div></template>
<template>
<div>
<!-- Pass the item into the slot to be rendered -->
<slot v-bind:item="list[0]">
<!-- Default -->
{{ list[0] }}
</slot>
<v-for
v-if="list.length > 1"
:list="list.slice(1)"
>
<!-- Recursively pass down scoped slot -->
<template v-slot="{ item }">
<slot v-bind:item="item" />
</template>
</v-for>
</div>
</template>
<template>
<div>
<!-- 常规列表 -->
<v-for :list="list" />
<!-- 加粗的项目列表 -->
<v-for :list="list">
<template v-slot="{ item }">
<strong>{{ item }}</strong>
</template>
</v-for>
</div>
</template>
18. 组件元数据
export default {
name: 'LiveUsersWidget',
// 👇 只需将其作为一个额外的属性添加
columns: 3,
props: {
// ...
},
data() {
return {
//...
};
},
};
export default {
name: 'LiveUsersWidget',
// 👇 只需将其作为一个额外的属性添加
columns: 3,
props: {
// ...
},
data() {
return {
//...
};
},
};
import LiveUsersWidget from './LiveUsersWidget.vue';const { columns } = LiveUsersWidget;
export default { name: 'LiveUsersWidget', columns: 3, created() { // 👇 `$options` contains all the metadata for a component console.log(`Using ${this.$options.metadata} columns`); },};
保持单个组件的版本号 用于构建工具的自定义标志,以区别对待组件 在计算属性、数据、watch 等之外为组件添加自定义功能 其它
19. 多文件单文件组件
<template src="./template.html"></template><script src="./script.js"></script><style scoped src="./styles.css"></style>
20. 可重复使用的组件并不是你所想的那样
<!-- OverflowMenu.vue --><template> <Menu> <!-- 添加一个自定义按钮来触发我们的菜单 --> <template #button v-slot="bind"> <!-- 使用bind来传递click处理程序、a11y 属性等 --> <Button v-bind="bind"> <template #icon> <svg src="./ellipsis.svg" /> </template> </Button> </template> </Menu></template>
21. 从组件外部调用一个方法
<!-- Parent.vue -->
<template>
<ChildComponent ref="child" />
</template>
// Somewhere in Parent.vue
this.$refs.child.method();
<template>
<ChildComponent
:tell-me-what-to-do="someInstructions"
@something-happened="hereIWillHelpYouWithThat"
/>
</template>
// Child.vue
export default {
props: ['trigger'],
watch: {
shouldCallMethod(newVal) {
if (newVal) {
// Call the method when the trigger is set to `true`
this.method();
}
}
}
}
父组件将 true 传递给触发器 prop Watch 被触发,然后 Child 组件调用该方法 子组件发出一个事件,告诉父组件该方法已被成功触发 Parent组件将 trigger 重置为 false,所以我们可以从头再来一次
<!-- Parent.vue -->
<template>
<ChildComponent ref="child" />
</template>
// Somewhere in Parent.vue
this.$refs.child.method();
22. 监听数组和对象
export default {
name: 'ColourChange',
props: {
colours: {
type: Array,
required: true,
},
},
watch: {
// 使用对象语法,而不仅仅是方法
colours: {
// 这将让Vue知道要在数组内部寻找
deep: true,
handler()
console.log('The list of colours has changed!');
}
}
}
}
watch( colours, () => { console.log('The list of colours has changed!'); }, { deep: true, });
23. 用Vue Router进行深度链接
someurl.com/edit?date-range=last-week
const dateRange = this.$route.query.dateRange;
<RouterLink :to="{ query: { dateRange: newDateRange }}">
24. template 标签的另一个用途
<template> <div class="card"> <img src="imgPath" /> <h3> {{ title }} </h3> <h4 v-if="expanded"> {{ subheading }} </h4> <div v-if="expanded" class="card-content" > <slot /> </div> <SocialShare v-if="expanded" /> </div></template>
<template> <div class="card"> <img src="imgPath" /> <h3> {{ title }} </h3> <template v-if="expanded"> <h4> {{ subheading }} </h4> <div class="card-content"> <slot /> </div> <SocialShare /> </template> </div></template>
25. 处理错误(和警告)的更好方法
// Vue 3
const app = createApp(App);
app.config.errorHandler = (err) => {
alert(err);
};
// Vue 2
Vue.config.errorHandler = (err) => {
alert(err);
};
赞 (0)